qubes-features.rst 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. :py:mod:`qubes.features` - Qubes VM features, services
  2. ======================================================
  3. Features are generic mechanism for storing key-value pairs attached to a
  4. VM. The primary use case for them is data storage for extensions (you can think
  5. of them as more flexible properties, defined by extensions), but some are also
  6. used in the qubes core itself. There is no definite list of supported features,
  7. each extension can set their own and there is no requirement of registration,
  8. but :program:`qvm-features` man page contains well known ones.
  9. In addition, there is a mechanism for VM request setting a feature. This is
  10. useful for extensions to discover if its VM part is present.
  11. Features can have three distinct values: no value (not present in mapping,
  12. which is closest thing to :py:obj:`None`), empty string (which is
  13. interpreted as :py:obj:`False`) and non-empty string, which is
  14. :py:obj:`True`. Anything assigned to the mapping is coerced to strings,
  15. however if you assign instances of :py:class:`bool`, they are converted as
  16. described above. Be aware that assigning the number `0` (which is considered
  17. false in Python) will result in string `'0'`, which is considered true.
  18. :py:class:`qubes.features.Features` inherits from :py:class:`dict`, so provide
  19. all the standard functions to get, list and set values. Additionally provide
  20. helper functions to check if given feature is set on the VM and default to the
  21. value on the VM's template or netvm. This is useful for features which nature is
  22. inherited from other VMs, like "is package X is installed" or "is VM behind
  23. a VPN".
  24. Example usage of features in extension:
  25. .. code-block:: python
  26. import qubes.exc
  27. import qubes.ext
  28. class ExampleExtension(qubes.ext.Extension):
  29. @qubes.ext.handler('domain-pre-start')
  30. def on_domain_start(self, vm, event, **kwargs):
  31. if vm.features.get('do-not-start', False):
  32. raise qubes.exc.QubesVMError(vm,
  33. 'Start prohibited because of do-not-start feature')
  34. if vm.features.check_with_template('something-installed', False):
  35. # do something
  36. The above extension does two things:
  37. - prevent starting a qube with ``do-not-start`` feature set
  38. - do something when ``something-installed`` feature is set on the qube, or its
  39. template
  40. qvm-features-request, qubes.PostInstall service
  41. ------------------------------------------------
  42. When some package in the VM want to request feature to be set (aka advertise
  43. support for it), it should place a shell script in ``/etc/qubes/post-install.d``.
  44. This script should call :program:`qvm-features-request` with ``FEATURE=VALUE``
  45. pair(s) as arguments to request those features. It is recommended to use very
  46. simple values here (for example ``1``). The script should be named in form
  47. ``XX-package-name.sh`` where ``XX`` is two-digits number below 90 and
  48. ``package-name`` is unique name specific to this package (preferably actual
  49. package name). The script needs executable bit set.
  50. ``qubes.PostInstall`` service will call all those scripts after any package
  51. installation and also after initial template installation.
  52. This way package have a chance to report to dom0 if any feature is
  53. added/removed.
  54. The features flow to dom0 according to the diagram below. Important part is that
  55. qubes core :py:class:`qubes.ext.Extension` is responsible for handling such
  56. request in ``features-request`` event handler. If no extension handles given
  57. feature request, it will be ignored. The extension should carefuly validate
  58. requested features (ignoring those not recognized - may be for another
  59. extension) and only then set appropriate value on VM object
  60. (:py:attr:`qubes.vm.BaseVM.features`). It is recommended to make the
  61. verification code as bulletproof as possible (for example allow only specific
  62. simple values, instead of complex structures), because feature requests come
  63. from untrusted sources. The features actually set on the VM in some cases may
  64. not be necessary those requested. Similar for values.
  65. .. graphviz::
  66. digraph {
  67. "qubes.PostInstall";
  68. "/etc/qubes/post-install.d/ scripts";
  69. "qvm-features-request";
  70. "qubes.FeaturesRequest";
  71. "qubes core extensions";
  72. "VM features";
  73. "qubes.PostInstall" -> "/etc/qubes/post-install.d/ scripts";
  74. "/etc/qubes/post-install.d/ scripts" -> "qvm-features-request"
  75. [xlabel="each script calls"];
  76. "qvm-features-request" -> "qubes.FeaturesRequest"
  77. [xlabel="last script call the service to dom0"];
  78. "qubes.FeaturesRequest" -> "qubes core extensions"
  79. [xlabel="features-request event"];
  80. "qubes core extensions" -> "VM features"
  81. [xlabel="verification"];
  82. }
  83. Example ``/etc/qubes/post-install.d/20-example.sh`` file:
  84. .. code-block:: shell
  85. #!/bin/sh
  86. qvm-features-request example-feature=1
  87. Example extension handling the above:
  88. .. code-block:: python
  89. import qubes.ext
  90. class ExampleExtension(qubes.ext.Extension):
  91. # the last argument must be named untrusted_features
  92. @qubes.ext.handler('features-request')
  93. def on_features_request(self, vm, event, untrusted_features):
  94. # don't allow TemplateBasedVMs to request the feature - should be
  95. # requested by the template instead
  96. if hasattr(vm, 'template'):
  97. return
  98. untrusted_value = untrusted_features.get('example-feature', None)
  99. # check if feature is advertised and verify its value
  100. if untrusted_value != '1':
  101. return
  102. value = untrusted_value
  103. # and finally set the value
  104. vm.features['example-feature'] = value
  105. Services
  106. ---------
  107. `Qubes services <https://www.qubes-os.org/doc/qubes-service/>`_ are implemented
  108. as features with ``service.`` prefix. The
  109. :py:class:`qubes.ext.services.ServicesExtension` enumerate all the features
  110. in form of ``service.<service-name>`` prefix and write them to QubesDB as
  111. ``/qubes-service/<service-name>`` and value either ``0`` or ``1``.
  112. VM startup scripts list those entries for for each with value of ``1``, create
  113. ``/var/run/qubes-service/<service-name>`` file. Then, it can be conveniently
  114. used by other scripts to check whether dom0 wishes service to be enabled or
  115. disabled.
  116. VM package can advertise what services are supported. For that, it needs to
  117. request ``supported-service.<service-name>`` feature with value ``1`` according
  118. to description above. The :py:class:`qubes.ext.services.ServicesExtension` will
  119. handle such request and set this feature on VM object. ``supported-service.``
  120. features that stop being advertised with ``qvm-features-request`` call are
  121. removed. This way, it's enough to remove the file from
  122. ``/etc/qubes/post-install.d`` (for example by uninstalling package providing
  123. the service) to tell dom0 the service is no longer supported. Services
  124. advertised by TemplateBasedVMs are currently ignored (related
  125. ``supported-service.`` features are not set), but retrieving them may be added
  126. in the future. Applications checking for specific service support should use
  127. ``vm.features.check_with_template('supported-service.<service-name>', False)``
  128. call on desired VM object. When enumerating all supported services, application
  129. should consider both the vm and its template (if any).
  130. Various tools will use this information to discover if given service is
  131. supported. The API does not enforce service being first advertised before being
  132. enabled (means: there can be service which is enabled, but without matching
  133. ``supported-service.`` feature). The list of well known services is in
  134. :program:`qvm-service` man page.
  135. Example ``/etc/qubes/post-install.d/20-my-service.sh``:
  136. .. code-block:: shell
  137. #!/bin/sh
  138. qvm-features-request supported-service.my-service=1
  139. Services and features can be then inspected from dom0 using
  140. :program:`qvm-features` tool, for example:
  141. .. code-block:: shell
  142. $ qvm-features my-qube
  143. supported-service.my-service 1
  144. Announcing supported features
  145. ------------------------------
  146. For non-service features, there is similar announce mechanis to the above, but
  147. uses ``supported-feature.`` prefix. It works like this:
  148. 1. The TemplateVM (or StandaloneVM) announces
  149. ``supported-feature.FEATURE_NAME=1`` (with ``FEATURE_NAME`` replaced with an
  150. actual feature name) using ``qvm-features-request`` tool (see above how to use it).
  151. 2. core-admin extension
  152. :py:class:`qubes.ext.supported_features.SupportedFeaturesExtension` records
  153. such requests as features on the same VM.
  154. 3. Any tool that wants to check if the feature support is advertised by the VM,
  155. can look into its features. For template-based VMs it is advised to check
  156. also its template - there is a `vm.features.check_with_template()`
  157. function specifically for this.
  158. Note that (similar to services) it is not necessary for the feature to be
  159. advertised to enable it. In fact, many features does not need any support from
  160. the VM side, so they will work without matching ``supported-feature.`` entry.
  161. Whether a feature requires VM-side support, is documented on case-by-case basis
  162. in `qvm-features` tool manual page.
  163. Module contents
  164. ---------------
  165. .. automodule:: qubes.features
  166. :members:
  167. :show-inheritance:
  168. .. vim: ts=3 sw=3 et