1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
Commit graph

460 commits

Author SHA1 Message Date
catborise
21f0777e76 fix(instances): internal snapshots no longer rewrite UEFI loaders
Taking or reverting an internal snapshot redefined a pflash VM with a
rom loader around the operation. libvirt 10.10 rejects that definition,
so the request ended in a 500; where it was accepted, the snapshot
recorded the fake loader, and a failure between rewrite and restore
left the VM unable to boot.

The definition is no longer touched: libvirt decides, and its error is
shown (for a running VM with raw NVRAM: 'internal snapshots of a VM
with pflash based firmware require QCOW2 nvram format'). A stopped VM's
disk-only internal snapshot is taken, as libvirt allows it. Revert
errors are shown too. The external snapshot path keeps its rewrite for
now.

Live tests cover running and stopped UEFI VMs; both failed with a 500
on the previous code.
2026-10-04 01:08:11 +03:00
catborise
3e427a41b8 fix(instances): find known VMs by UUID, not by name
Instance.proxy looked the domain up by its stored name. After a rename
or a name swap outside WebVirtCloud, an operation on one VM changed
another; after a VM was replaced under the same name, operations hit
the new VM.

wvmInstance takes the UUID and uses lookupByUUIDString, with no
fallback to the name: a missing UUID fails instead of picking another
domain. Every caller that has an Instance passes it: proxy, console,
quota, post-migration autostart and the VDI URL. Migration uses the
domain the proxy already resolved. getvvfile and the gstfsd requests
use the host's current name of that UUID.

Live tests cover a name swap and a VM replaced under the same name;
both fail on the previous code.
2026-10-04 01:08:11 +03:00
catborise
ffb2ce6bfa fix(instances): destroy and volume delete no longer lose disks
Destroy:
- Stops every active state, not only running; a paused VM used to keep
  running as a transient domain after the undefine.
- Undefines with MANAGED_SAVE and SNAPSHOTS_METADATA, before any disk is
  deleted. A managed-save image used to make the undefine fail after the
  disks were already gone; now a failed undefine deletes nothing.
- Keeps disks that another domain references, directly or as a backing
  file anywhere in its chain (running domains' backingStore, and the
  backingStore of the volumes in the active pools for stopped ones), and
  says so; they used to be deleted with the VM.

Volume delete:
- Detaches from the live domain in every active state, including paused.
- Deletes the volume only after neither the live nor the persistent
  definition has the disk (bounded wait for the guest to release it),
  and refuses a volume another VM references. Otherwise the volume is
  kept and the user is told why.

Both hold the compute exclusively for the usage scan and the deletion,
so another VM cannot attach the disk in between; the destroy
confirmation page (GET) takes no lock.

Live tests reproduce each case on a RHEL qemu-kvm host and fail on the
previous code; unit tests cover the destroy ordering and the locks.
2026-10-04 01:08:11 +03:00
catborise
dbbf2ce2e8 test(instances): isolate the live libvirt suite on shared hosts
- Runs only when TEST_LIBVIRT_HOST is set, so manage.py test on a
  developer machine no longer creates VMs on the local libvirtd.
- Every VM and volume is named wvc-test-*, and volumes live in a
  temporary wvc-test pool (/var/lib/libvirt/wvc-test) instead of the
  default pool next to real images.
- Cleanup removes only prefixed domains and the test pool's volumes;
  it also clears leftovers of an interrupted run before starting, and
  tolerates domains that turned transient.
- tearDownClass fails if any domain or volume outside the test
  namespace appeared, disappeared or was replaced.
- livetest gains create_volume/define_vm helpers and
  test_live_dataloss.py, whose setUpClass opens the class transaction
  last, so a failed libvirt connection cannot lock the test database
  for later test classes.

The first live tests reproduce two known external-snapshot data losses
and are marked expectedFailure: reverting deletes a disk added after
the snapshot, and deleting the older of two snapshots deletes the
newer one's overlay.
2026-10-04 01:08:11 +03:00
catborise
fbc6c4976f fix(instances): change the edited NIC instead of adding a new one
The edit form posts net-*-<n> fields for NIC n and carries only the
new MAC. The backend read index 0 and looked the NIC up by the new MAC,
so editing a MAC detached nothing and attached a second NIC, and edits
of any NIC but the first were applied with empty values.

The form now also posts the current MAC (net-old-mac-<n>). The view
reads the submitted NIC's fields, validates the new MAC and reports
errors. The backend replaces the NIC with the old MAC, refuses an
unknown one, and on a failed attach restores the old NIC and raises
instead of reporting success.

The view crashed with a TypeError without a Referer header; it now
returns to the instance page, and a Referer from another site is
ignored.
2026-10-04 01:08:11 +03:00
catborise
9e92d208db fix(instances): flavor redirects and create-from-XML crashed
- Flavor create/update/delete fell back to the nonexistent
  instances:flavors URL; reverse() ran on every request, so each of
  them raised NoReverseMatch. Fall back to instances:index.
- Creating a VM from XML called instances.utils.refresh_instance_database,
  which does not exist; use utils.refr.

Both found by running the live libvirt suite.
2026-10-04 01:08:11 +03:00
catborise
88ec3c5f6a test(instances): make the live libvirt suite runnable on real hosts
- Pick the test VM by name; .first() could select any VM already on
  the compute, and tearDownClass deletes the chosen VM's disks.
- Delete the test volume when setUpClass fails after creating it.
- test-vm.xml: no hard-coded emulator, machine alias q35 instead of
  pc-q35-5.1, so it defines on RHEL qemu-kvm hosts too.
- Follow current behavior: power actions are POST-only, the create
  form needs add_cdrom/add_input and VNC, cloning needs change
  permission on the source VM.
- README: the tests read TEST_LIBVIRT_HOST/TYPE/LOGIN/PASSWORD;
  WEBVIRTCLOUD_TEST_LIBVIRT_URI never existed.
2026-10-04 01:08:11 +03:00
catborise
b04f656d2b ci: replace super-linter with ruff
Super-linter ran from the unpinned :latest image and, because of
branches-ignore: [master], never on pull requests to master. Ruff now
checks syntax errors and pyflakes rules (E9, F) on every push and PR.

Fixes the existing findings: unused imports and variables, an unused
duplicate import in instances/tests.py, and the star import in
accounts/views.py. settings-dev.py keeps its star import of the base
settings.
2026-10-04 01:08:11 +03:00
catborise
51dedb7b8f fix(instances): disk edits keep their XML; changes lock per VM
Disk edits:
- edit_disk built a new <disk> from the form and replaced the old one,
  losing what the form does not show (e.g. encryption and virtio
  driver options); a bus change detached and re-attached the disk.
  The disk of the persistent definition is now edited in place and the
  domain defined once; a bus change picks a free device name and drops
  the old address and virtio-only options. Pending persistent changes of
  a running VM are kept.
- Clone omits empty disk devices, refuses a disk without a supported
  source and destination, and checks the name before allocating storage.

Locks:
- Every VM change held its compute's exclusive lock, so one slow
  operation blocked all VMs on that compute. A VM change now holds its
  own VM lock plus a shared compute lock; reconciliation, migration and
  cloning hold the compute exclusively (two clones of different VMs
  must not pass the name check together).
- Quota-checked changes of one user (CPU, memory and disk resize,
  clone) run one at a time under a per-user lock, also across computes,
  so concurrent requests cannot all pass the check against the old
  allocation. It is the outermost lock; superusers, who have no quota,
  skip it.
- A busy lock answers 409 to the API and a message in the web UI. API
  power and destroy actions return the view's error instead of success.
2026-10-04 01:08:11 +03:00
catborise
3b8869c72f fix(api): typed URLs and accurate response schemas
- Routers use path converters (use_regex_path=False): computes, instances
  and flavors take <int:...> ids, so non-numeric ids get 404 and
  drf-spectacular derives the parameter types itself.
- Each method declares what it really returns: detail serializers,
  a shared StatusSerializer for status-only replies, no request body for
  actions. Instance properties used by the details serializer get return
  type hints.
- The create-instance API drops the firmware field. Its only accepted
  value was "" (BIOS), which produced an empty, invalid enum, and the
  UEFI branch behind it was unreachable. Clients that still send it are
  unaffected; DRF ignores unknown fields.

The schema now passes spectacular --validate --fail-on-warn; a test
keeps it that way.
2026-10-04 01:08:11 +03:00
catborise
a6364f5d9b fix(gstfsd): append SSH keys, IPv6 loopback bind, server-side timeout
- Add the public key to authorized_keys instead of replacing the file.
- Use AF_INET6 when GSTFSD_BIND_HOST is an IPv6 address (binding ::1
  crashed with the AF_INET server).
- Close idle client connections after 30 s.

Tests run the real daemon against a small fake guestfs module over a
socket; this also exercises the earlier sendall() fix.
2026-10-03 00:02:01 +03:00
catborise
32d7f279e7 test: inventory HTML sinks in templates, realistic datasource params in the matrix
- instances/test_xss.py: scan all templates for .html(, innerHTML = and
  append('<...') and require each line to be in a reviewed allowlist, so
  new sinks fail the test (the exact-string tests only caught the lines
  they named).
- Authorization matrix: fill the datasource "version" parameter with
  "latest" instead of "x". The datasource views then answer 200 to any
  logged-in user; they are listed in the allowlist as known F-14.
2026-10-03 00:00:27 +03:00
catborise
479faaa4ca fix(instances): allow editing disks that have no driver format
A disk without driver/@type could not be edited: the empty format was
rejected, and edit_disk would have written type=''. An empty format is
now accepted and edit_disk leaves the type attribute out.
2026-10-02 23:58:55 +03:00
catborise
e626da5002 fix(instances): do not refuse clones for volume-less disks or unusual names
Clone refused VMs that cloned fine before: a disk without a volume got a
None disk name, and a source image with characters such as a space or "@"
produced a derived name outside the allowed pattern. Disks without a
volume (which clone_instance does not copy) are no longer validated, and
derived disk names are sanitized to the allowed charset. Names typed by a
superuser are still validated strictly.
2026-10-02 23:14:28 +03:00
catborise
9205e56e3b fix(instances): change a disk's bus without rebuilding it as a file disk
The edit_volume bus change detached the disk and attached a new one built
as a file disk, so block and rbd disks lost their source and auth, and a
failed attach left the disk detached. Add wvmInstance.change_disk_bus,
which reuses the disk element and only changes <target> (dropping the old
bus address), and re-attaches the original disk if attaching fails.

edit_disk also rebuilt every disk with <source file=...>; it now keeps the
existing <source> and <auth> of block and network disks.
2026-10-02 23:06:42 +03:00
catborise
7286ba079d fix(gstfsd): random password salt, timeouts, loopback-only daemon
- Hash the root password with a random SHA-512 salt instead of the fixed
  "$6$kgPoiREy".
- Talk to gstfsd through one helper with a 120 s socket timeout; an
  unreachable daemon or a bad reply is an error message, not a 500.
- gstfsd refuses to listen on a non-loopback address unless
  GSTFSD_ALLOW_REMOTE=1: it is an unauthenticated root service.
- gstfsd: encode replies (sendall(str) raised TypeError on Python 3, so
  the success path never worked) and use octal chmod modes.
- README: what gstfsd does and how to allow it on a remote compute.
2026-10-02 22:52:09 +03:00
catborise
e76149c71a fix: serve compute JSON endpoints as application/json, build chipset list safely
compute_graph (text/javascript) and the machine type, disk bus, video
model and domain capability views (text/html) return JSON; send it as
application/json. create_instance_w1.html builds the chipset <option>
elements with .val().text() instead of string concatenation.
2026-10-02 22:34:05 +03:00
catborise
d250915e17 fix(instances): edit_volume bus change and format, full-string validation
- edit_volume passed driver_type to attach_disk, which has no such
  parameter, so every bus change failed after the disk was detached; pass
  format_type.
- A disk without a driver type shows "None" in the edit form; keep the
  disk's current format instead of rejecting it.
- Validate with fullmatch, so a trailing newline is no longer accepted,
  and use one volume-name pattern for disk views and clone.
2026-10-02 22:31:25 +03:00
catborise
634e9acb69 Merge branch 'fix/wave0-template-flag' into wave0-integration 2026-10-02 22:14:40 +03:00
catborise
34851b8dc2 Merge branch 'fix/wave0-staff-console-password' into wave0-integration 2026-10-02 22:14:40 +03:00
catborise
51db12acfe Merge branch 'fix/wave0-authorization-matrix' into wave0-integration
# Conflicts:
#	datasource/views.py
2026-10-02 22:14:40 +03:00
catborise
fbb48fb3ea Merge branch 'fix/wave0-clone-permission' into wave0-integration 2026-10-02 22:14:29 +03:00
catborise
3df2e68431 Merge branch 'fix/wave0-xss' into wave0-integration 2026-10-02 22:14:29 +03:00
catborise
385a332197 fix(instances): only superusers and staff may change the template flag
change_options set is_template for any owner with is_change; the
"staff only" rule existed only as a disabled checkbox in the UI. A
non-staff owner could mark or unmark templates with a POST, and because a
disabled checkbox is not submitted, a non-staff owner who only edited a
template's title or description silently unmarked it, which lifted the
template-only restrictions.

Only change is_template for superusers and staff (for them an unchecked,
unsubmitted checkbox still means "not a template"); leave it alone for
everyone else.
2026-10-02 22:13:46 +03:00
catborise
8e23699987 fix(instances): show console settings and the VNC password only to console managers
The console settings form on the VM detail page renders the VNC password
(PasswordInput(render_value=True)) and was shown to every is_staff user.
A staff user with the global view_instances permission, or a staff owner
without is_vnc, could read the VNC password of every VM they could see.

Add instances.utils.can_manage_console (active superuser, or an active
owner with is_change and is_vnc), which matches what update_console
already enforced. The detail view only builds the form, and so only puts
the password into the page, when it returns True; the template and
update_console use the same rule.
2026-10-02 22:04:05 +03:00
catborise
4d34d9d07c fix: add an authorization matrix test and close the endpoints it finds
Add webvirtcloud/test_authorization_matrix.py, which walks every named URL
and checks that a user without any VM/compute relation only reaches an
explicit allowlist, and that the global view_instances permission does not
open consoles. It found:

- /computes/<id>/... views without any check (compute_graph,
  get_compute_*, get_dom_capabilities, storages.get_volumes): the whole
  computes URL namespace is now wrapped with superuser_only, so a view
  added there later cannot be exposed by a missing decorator.
- guess_mac_address (leaks dhcpd.conf host/MAC pairs), random_mac_address
  and guess_clone_name: superuser-only, like the buttons that use them.
- check_instance: cross-tenant VM name lookup with no caller; removed.
- getvvfile and get_vdi_url: reachable with only view_instances, and the
  .vv file contains the VNC password.

Console access now follows one rule, instances.utils.can_open_console:
an active superuser or an active owner of the VM. The global
view_instances permission is read-only. It is used by the console page,
novncd, getvvfile and get_vdi_url (getvvfile and the VDI tab are kept for
virt-viewer users). The console page answers 403 instead of 500 when
access is denied, with the same answer for a missing VM.
2026-10-02 20:33:12 +03:00
catborise
5758073e82 fix(instances): require change permission to clone and stop trusting clone POST data
clone only required "view" access, so a user with clone_instances and the
global view_instances permission could clone anyone's VM and read its
disks. It also copied every POST field into clone_data, which let the
client override the server-set disk_owner_uid/gid, and disk-<dev> values
went unvalidated into volume XML and os.path.join.

- Require change permission on the source VM, except for templates,
  which any user who can view them may clone.
- Build clone_data from an allowlist: name, title and description for
  everyone; disk names and MAC addresses only from superusers.
- For non-superusers, derive disk names (get_clone_disk_name) and MAC
  addresses (DHCP or random) on the server; their form only sent the
  source VM's own values, which could never work.
- Validate every MAC address (not only the first) and every disk name;
  a missing MAC is an error message instead of a KeyError.
2026-10-02 19:07:53 +03:00
catborise
4546653b5a fix: serve JSON endpoints as application/json and build DOM safely
Several views returned json.dumps() output through HttpResponse, i.e. as
text/html, and guess_mac_address reflects the URL's vname into it
(reflected XSS). Templates filled the log table and volume dropdowns by
concatenating server data into HTML strings, and log messages contain
user-entered snapshot/disk/clone names (stored XSS that runs when a
superuser opens the Logs tab).

- Use JsonResponse in guess_mac_address, random_mac_address,
  guess_clone_name, sshkeys, storages.get_volumes,
  datasource.os_metadata_json and logs.vm_logs; sshkeys?plain=true is
  served as text/plain.
- stats_tab.html builds log rows with .text(); instance.html and
  create_instance_w2.html build <option> elements with .val().text().
2026-10-02 18:29:11 +03:00
catborise
8a3ee13b41 fix(instances): restrict disk views to superusers and validate disk input
Disk and media mutation views (add_new_vol, add_existing_vol, edit_volume,
delete_vol, detach_vol, add_cdrom, detach_cdrom, mount_iso, unmount_iso)
were reachable by any owner with is_change, although the UI only shows the
Disk tab to superusers. An ordinary user could delete any volume on the
compute or attach arbitrary host files to their VM.

- Require superuser for all disk/media views, matching the UI.
- delete_vol resolves the volume from the VM's own disk XML by dev and
  ignores the posted storage/name.
- Validate bus/cache/io/discard/detect_zeroes against the hypervisor's
  lists, and format/serial/volume name against strict patterns.
- add_existing_vol only accepts volumes that exist in the selected pool.
- edit_volume only accepts a dev attached to the VM.
- vrtManager: escape all values interpolated into disk XML and pass dev
  as an XPath variable in detach_disk/edit_disk.
2026-10-02 17:11:12 +03:00
catborise
73d8fd71fb fix(instances): guard mutations and refresh lists after sync 2026-09-28 14:56:19 +03:00
catborise
2cdf5dfca1 fix(reconciliation): serialize compute sync and enforce UUID identity 2026-09-28 14:55:49 +03:00
catborise
b3e6fb2b7a feat(security): enforce require_POST on power actions, prevent open redirects, and isolate instance permissions 2026-09-27 13:20:45 +03:00
catborise
3f8eeb04bd fix(reconciliation): implement UUID-based domain reconciliation and connection safety 2026-09-27 13:13:43 +03:00
catborise
708b986e8e feat(security): apply Codex review hardening to console tokens and superuser authorization 2026-09-27 11:30:03 +03:00
catborise
1227674d8f feat(security): implement P0 critical security fixes for API, console, and access control
- Computes API: mark password as write_only in ComputeSerializer, restrict ComputeViewSet and architectures to IsAdminUser.
- Instances API: enforce user ownership tenant isolation in InstanceViewSet.list, restrict MigrateViewSet and CreateInstanceViewSet to IsAdminUser, require IsAdminUser for mutating FlavorViewSet.
- Storage, Network, and Interface APIs: restrict StorageViewSet, VolumeViewSet, NetworkViewSet, InterfaceViewSet to IsAdminUser.
- AppSettings & NWFilters: protect appsettings and nwfilter views with @superuser_only (403 Forbidden for non-superusers).
- noVNC Console: sign tokens with TimestampSigner (salt='console.novnc', max_age=600), verify signature and user authorization in novncd with backward-compatible fallback for debug/test mode.
- Accounts: avoid hardcoded default admin password; generate cryptographically secure random password unless ADMIN_PASSWORD is set (preserve 'admin' in test runner).
- gstfsd daemon: bind to 127.0.0.1 by default instead of 0.0.0.0.
- Tests: added comprehensive test coverage for API security, token expiration/rejection, and view permissions (132 tests passing).
2026-09-27 10:50:09 +03:00
catborise
e980aca2ff feat(console): remove deprecated SPICE support in favor of noVNC
- Remove obsolete spice-html5 static assets and SPICE console templates
- Restrict default console types to VNC with migration 0012
- Filter out SPICE in VM creation form and backend capabilities
- Return friendly deprecation notice if legacy VM is opened with SPICE
- Clean up docs, architecture references, and libvirt-bootstrap scripts
2026-09-26 22:43:40 +03:00
catborise
60565b05b3 fix(tests): decouple tests from local libvirt, support configurable test host, and fix URL/logging bugs 2026-09-25 11:51:45 +03:00
catborise
c31ed28042 test: expand unit test coverage across admin, accounts, vrtManager, logs, appsettings, and datasource 2026-09-25 11:26:53 +03:00
catborise
7a9b3c9e13 feat(dev): improve Rocky Linux and RHEL compatibility for local development
- Relax libvirt-python and lxml version pins to allow precompiled distro packages
- Add PEP 508 environment marker to crypt-r (python_version >= 3.13) to avoid unnecessary C compilation on Python 3.12
- Add stdlib crypt fallback when crypt_r is not available in instances/views.py
- Make LDAP imports and configuration optional in settings.py.template
- Add --system-site-packages and missing packages in webvirtcloud.sh for RHEL/CentOS
- Update README with working Rocky Linux / RHEL / Fedora installation and local dev setup
2026-09-25 09:45:54 +03:00
catborise
3633c0096e refactor(console): replace deprecated optparse with argparse and fix regex escape sequences 2026-09-24 22:08:08 +03:00
Jan Krcmar
80541239ec fix template clone icon replaced with copy icon 2026-04-15 17:29:08 +02:00
Liang Suilong
8bca7db917 crypt-r replaces crypt stdlib, support python-3.13 2025-03-30 10:33:51 +08:00
catborise
3f23acbb9d
Merge pull request #638 from catborise/master
fix default nic type problem
2024-02-28 08:18:08 +03:00
catborise
765b759953 if nic type is default then make it virtio 2024-02-28 08:16:44 +03:00
catborise
a048adef8e do not show warning message when instance is shutdown 2024-02-28 08:13:08 +03:00
catborise
9ee1571f5b
Merge pull request #634 from catborise/master
fix user machine for listing
2024-02-13 14:35:03 +03:00
catborise
81b96b6512 fix listing vms for normal users 2024-02-13 14:24:38 +03:00
catborise
1f90a6c8c9
Merge pull request #631 from catborise/master
update requirements
2024-01-17 10:24:13 +03:00
catborise
9a05c182cc fix chevron up down icons 2024-01-17 10:19:03 +03:00
Wang Hong
c58d6f484d Corrected untranslated items 2023-12-13 14:00:02 +08:00