Handling Python Dependencies
The Python dependencies are not a part of the basic Python program and are instead loaded during runtime. As such the application measurement, when loaded, does not cover these dependencies.
Anjuna provides the user a way to identify these dependencies.
A Python application can have two types of dependencies - static dependencies and dependencies that are computed during execution.
A static dependency is one that appears in the code in the following way:
import mariadb
Here the code says that it depends on the MariaDB module.
A dependency that is computed during execution is one that is realized during runtime.
The following example is from the file onnx/symbolic_registry.py
from the pytorch module:
for opset_version in _onnx_stable_opsets + [_onnx_main_opset]: module = importlib.import_module("torch.onnx.symbolic_opset{}".format(opset_version)) _symbolic_versions[opset_version] = module
You want to identify these two types of dependencies and then store their data in the manifest file of the enclave.
Identifying Python Static Dependencies
The static dependencies can be identified by the Anjuna manifest compiler. The compiler will add all of the static dependencies and their measurements to the manifest file.
This is done by including the tag python_config
in the manifest template.
For the basic analyzer, include an empty python_config
tag in the template, i.e., add the following
line to the template:
python_config: {}
This allows anjuna-compile-manifest
to automatically invoke the Python analyzer to include the Python
dependencies in the application manifest from the manifest template.
If you change your code or change your dependencies, you should re-run the anjuna-compile-manifest
(or remove the python*.manifest.sgx
file and re-run anjuna-sgxrun
).
Identifying Python Dependencies that are Computed during Execution
The dependencies that are computed during execution can be identified when running the application in the enclave. For any dependency that is not a part of the manifest, or its measurements are different, a proper message will be printed to the standard error.
Dependencies that are not a part of the manifest
When in non-strict mode, any dependency that is missing from the manifest file will be printed to the standard error in the following format:
Module: <module_name> not in config
In the non-strict mode, the module will be allowed to be imported.
When in strict mode, the module will not be allowed to be imported for any dependency that is missing
from the manifest file and as a result triggers a ModuleNotFoundError
by the Python engine.
To solve the problem of the missing dependency, you will need to add the modules to the manifest
template file. For example, if the modules torch.onnx.symbolic_opset7
and
torch.onnx.symbolic_opset8
are computed during execution, you should add a python_modules
tag
into your manifest template, under the python_config
tag with the field name
and the value
<module_name>
for each missing dependency.
In the above example, you will have the following tag in the manifest template:
python_config: python_modules: - name: torch.onnx.symbolic_opset7 - name: torch.onnx.symbolic_opset8
Dependencies with a different measurement
When in non-strict mode, any dependency whose measurement differs from the one identified by the analyzer will be printed to the standard error in the following format:
Module: <module_name> has improper hash value Expected: <expected_module_measurement> Found: <actual_module_measurement>
In the non-strict mode, the module will be allowed to be imported.
When in strict mode, any dependency that has a different hash prevents the module from getting
imported and as a result triggers a ModuleNotFoundError
by the Python engine.
To solve the problem of a measurement that differs, determine if the module is indeed the correct
module. If you trust the module with the new measurement, delete the manifest file and recompile
it from the template (anjuna-compile-manifest
or anjuna-sgxrun
).