gstfsd, the root service on each compute that rewrites a shut-off VM's
/etc/shadow and authorized_keys, did what any client on its port asked.
Now it needs GSTFSD_KEY (64 hexadecimal characters, in the unit's
/etc/default/webvirt-gstfsd) and does not start without it. Each
connection gets a new challenge; the panel signs its request for it
with HMAC-SHA256 and gstfsd checks the signature before any change, so
a request recorded on one connection is refused on another. gstfsd
signs its reply for the same challenge and the panel's nonce, and the
panel accepts only that reply.
Each compute has a gstfsd key, the host's GSTFSD_KEY: the compute forms
and the API take it and never show it, and an empty value on an edit
keeps it. The bootstrap script makes the key file once, readable by
root only; the README shows how to make it and where the panel takes
it. The traffic is still not encrypted: a remote panel's port stays
open to the panel's address only.
- An API volume takes the name and size the web form takes. Its name went
into the volume XML unchecked, so a name could add elements such as a
backing store pointing at another VM's disk; the name is also escaped
there now. A size of 0 and metadata preallocation on a non-qcow2 volume
are refused or dropped, as on the web form.
- An API compute update with an empty or null password keeps the stored
one, as the web form does; it emptied it and cut the host off.
- The architectures endpoint answers 404 for an unknown compute instead of
500; an unrouted copy of it is removed.
The Logs page shows who added, changed or deleted a compute, from web
or API, and every login, failed login, lockout and logout, with the
client address and a Result column (Done, Failed, Unknown for older
rows). A failed change is recorded with its reason and the error still
reaches the user; a refused one is recorded too.
- logs.audit.write saves each row in its own savepoint and never
raises, so a log write cannot break or undo the action it records.
The audit() context manager writes one row when the change ends.
- Logs gains success and ip; user, host and instance are wider and
date is indexed. The page orders by date then id.
- A compute edit names the changed fields; an edit that changes
nothing writes no row, and an empty password field is no change.
- A compute's VMs leaving the panel get a system row each: owners
kept on the Removed VMs page, moved to another compute, restored,
left without owners, or the removed record expired.
- Job rows carry the result. A job's log entry that cannot be
computed is logged and skipped instead of rolling back the finish
(test_service.py changes this on purpose).
- Logins are recorded by signal receivers; CustomLoginView is removed
and the plain LoginView is used. A wrong OTP code is a failed login,
counted by django-axes toward the lockout.
Panel records and host-side objects follow in the next layers.
Migration is client-driven: libvirt sends the VM's memory to the
destination's own host name unless a migration URI is given, and the
app never gave one. Where the hosts cannot resolve each other's names
(or the names point at a management network) every migration failed
with "Unable to resolve address '<host name>' service '49152'".
A compute now has an optional migration address (an IP address or a
host name, no scheme or port), editable in the compute form and the
API. A VM migrating to that compute is sent to tcp://<address>
(bracketed for IPv6). Empty, the default for existing computes, keeps
the previous behavior. The compute's host name is not reused, because
for SSH computes it can be an ssh_config alias.
The migration log entry names the address when one is set.
- 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.
- 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).
* Add rest framework for API: First Commit
* modify some shell scripts to make variable references safer; modify some python scripts to reduce the code complexity and cyclomatic complexity of functions.
* Add REST API for some webvirtcloud functions. Instance list/delete/create, compute list/delete/create, storages-network list/retrieve. Add swagger and redoc for API interface
* update requirements
Co-authored-by: herengui <herengui@uniontech.com>