forked from clan/clan-core
Compare commits
3 Commits
main
...
ui-testing
Author | SHA1 | Date | |
---|---|---|---|
246457ffa2 | |||
bd5fd8b7b1 | |||
54e9439f0a |
@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
weston,
|
||||||
python3,
|
python3,
|
||||||
runCommand,
|
runCommand,
|
||||||
setuptools,
|
setuptools,
|
||||||
@ -14,8 +17,7 @@
|
|||||||
libadwaita,
|
libadwaita,
|
||||||
pytest, # Testing framework
|
pytest, # Testing framework
|
||||||
pytest-cov, # Generate coverage reports
|
pytest-cov, # Generate coverage reports
|
||||||
pytest-subprocess, # fake the real subprocess behavior to make your tests more independent.
|
pytest-subprocess, # fake the real subprocess behavior to make your tests more independent. # Run tests in parallel on multiple cores
|
||||||
pytest-xdist, # Run tests in parallel on multiple cores
|
|
||||||
pytest-timeout, # Add timeouts to your tests
|
pytest-timeout, # Add timeouts to your tests
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
@ -52,9 +54,9 @@ let
|
|||||||
pytest # Testing framework
|
pytest # Testing framework
|
||||||
pytest-cov # Generate coverage reports
|
pytest-cov # Generate coverage reports
|
||||||
pytest-subprocess # fake the real subprocess behavior to make your tests more independent.
|
pytest-subprocess # fake the real subprocess behavior to make your tests more independent.
|
||||||
pytest-xdist # Run tests in parallel on multiple cores
|
|
||||||
pytest-timeout # Add timeouts to your tests
|
pytest-timeout # Add timeouts to your tests
|
||||||
];
|
]
|
||||||
|
++ (lib.optionals stdenv.isLinux [ weston ]);
|
||||||
|
|
||||||
# Dependencies required for running tests
|
# Dependencies required for running tests
|
||||||
testDependencies = runtimeDependencies ++ allPythonDeps ++ externalTestDeps;
|
testDependencies = runtimeDependencies ++ allPythonDeps ++ externalTestDeps;
|
||||||
|
@ -19,7 +19,7 @@ testpaths = "tests"
|
|||||||
faulthandler_timeout = 60
|
faulthandler_timeout = 60
|
||||||
log_level = "DEBUG"
|
log_level = "DEBUG"
|
||||||
log_format = "%(levelname)s: %(message)s\n %(pathname)s:%(lineno)d::%(funcName)s"
|
log_format = "%(levelname)s: %(message)s\n %(pathname)s:%(lineno)d::%(funcName)s"
|
||||||
addopts = "--cov . --cov-report term --cov-report html:.reports/html --no-cov-on-fail --durations 5 --color=yes --new-first" # Add --pdb for debugging
|
addopts = "--cov . --cov-report term --cov-report html:.reports/html --no-cov-on-fail --durations 5 --color=yes -vv --new-first" # Add --pdb for debugging
|
||||||
norecursedirs = "tests/helpers"
|
norecursedirs = "tests/helpers"
|
||||||
markers = ["impure"]
|
markers = ["impure"]
|
||||||
|
|
||||||
@ -29,11 +29,17 @@ warn_redundant_casts = true
|
|||||||
disallow_untyped_calls = true
|
disallow_untyped_calls = true
|
||||||
disallow_untyped_defs = true
|
disallow_untyped_defs = true
|
||||||
no_implicit_optional = true
|
no_implicit_optional = true
|
||||||
|
exclude = '^tests/helpers/libvncclient.py$'
|
||||||
|
|
||||||
[[tool.mypy.overrides]]
|
[[tool.mypy.overrides]]
|
||||||
module = "clan_cli.*"
|
module = "clan_cli.*"
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = "libvncclient.*"
|
||||||
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
target-version = "py311"
|
target-version = "py311"
|
||||||
line-length = 88
|
line-length = 88
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
python3,
|
python3,
|
||||||
gtk4,
|
gtk4,
|
||||||
libadwaita,
|
libadwaita,
|
||||||
|
tigervnc,
|
||||||
|
libvncserver,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
@ -31,6 +33,9 @@ mkShell {
|
|||||||
ruff
|
ruff
|
||||||
gtk4.dev # has the demo called 'gtk4-widget-factory'
|
gtk4.dev # has the demo called 'gtk4-widget-factory'
|
||||||
libadwaita.devdoc # has the demo called 'adwaita-1-demo'
|
libadwaita.devdoc # has the demo called 'adwaita-1-demo'
|
||||||
|
tigervnc
|
||||||
|
libvncserver.dev
|
||||||
|
libvncserver
|
||||||
]
|
]
|
||||||
++ devshellTestDeps
|
++ devshellTestDeps
|
||||||
|
|
||||||
@ -40,14 +45,28 @@ mkShell {
|
|||||||
desktop-file-utils # verify desktop files
|
desktop-file-utils # verify desktop files
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
# Use ipdb as the default debugger for python
|
||||||
|
PYTHONBREAKPOINT = "ipdb.set_trace";
|
||||||
|
|
||||||
|
hardeningDisabled = "all";
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
|
export LIBVNC_INCLUDE=${libvncserver.dev}/include
|
||||||
|
export LIBVNC_LIB=${libvncserver}/lib
|
||||||
|
|
||||||
export GIT_ROOT=$(git rev-parse --show-toplevel)
|
export GIT_ROOT=$(git rev-parse --show-toplevel)
|
||||||
export PKG_ROOT=$GIT_ROOT/pkgs/clan-vm-manager
|
|
||||||
|
|
||||||
# Add clan-vm-manager command to PATH
|
# Add clan-vm-manager command to PATH
|
||||||
export PATH="$PKG_ROOT/bin":"$PATH"
|
export PATH="$GIT_ROOT/pkgs/clan-vm-manager/bin":"$PATH"
|
||||||
|
|
||||||
|
# Add clan-vm-manager to the python path so that we can
|
||||||
|
# import it in the tests
|
||||||
|
export PYTHONPATH="$GIT_ROOT/pkgs/clan-vm-manager":"$PYTHONPATH"
|
||||||
|
|
||||||
# Add clan-cli to the python path so that we can import it without building it in nix first
|
# Add clan-cli to the python path so that we can import it without building it in nix first
|
||||||
export PYTHONPATH="$GIT_ROOT/pkgs/clan-cli":"$PYTHONPATH"
|
export PYTHONPATH="$GIT_ROOT/pkgs/clan-cli":"$PYTHONPATH"
|
||||||
|
|
||||||
|
# Add clan-cli to the PATH
|
||||||
|
export PATH="$GIT_ROOT/pkgs/clan-cli/bin":"$PATH"
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,6 @@ from clan_cli.custom_logger import setup_logging
|
|||||||
from clan_cli.nix import nix_shell
|
from clan_cli.nix import nix_shell
|
||||||
|
|
||||||
sys.path.append(str(Path(__file__).parent / "helpers"))
|
sys.path.append(str(Path(__file__).parent / "helpers"))
|
||||||
sys.path.append(
|
|
||||||
str(Path(__file__).parent.parent)
|
|
||||||
) # Also add clan vm manager to PYTHONPATH
|
|
||||||
|
|
||||||
pytest_plugins = [
|
pytest_plugins = [
|
||||||
"temporary_dir",
|
"temporary_dir",
|
||||||
|
12
pkgs/clan-vm-manager/tests/data/vnc-security/ca.crl
Normal file
12
pkgs/clan-vm-manager/tests/data/vnc-security/ca.crl
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
-----BEGIN X509 CRL-----
|
||||||
|
MIIB2zCBxAIBATANBgkqhkiG9w0BAQsFADCBgDELMAkGA1UEBhMCVVMxEjAQBgNV
|
||||||
|
BAgMCVlvdXJTdGF0ZTERMA8GA1UEBwwIWW91ckNpdHkxGzAZBgNVBAoMEllvdXJD
|
||||||
|
QU9yZ2FuaXphdGlvbjEZMBcGA1UECwwQWW91ckNBRGVwYXJ0bWVudDESMBAGA1UE
|
||||||
|
AwwJMTI3LjAuMC4xFw0yNDA0MDExMzQ4MTBaFw0yNDA1MDExMzQ4MTBaoA8wDTAL
|
||||||
|
BgNVHRQEBAICEAAwDQYJKoZIhvcNAQELBQADggEBAKlXZYq3euvI5jdDreK6o9YQ
|
||||||
|
VtdLzdf/J0kQF8MVFjPo+x585xKYu2YgFLBmvZ5Jfpi6MjYH6E/liJdT1be16lDo
|
||||||
|
/CYy+55UdUgm61rordF+ddWQBcr4+Nw9k9ZQIdOMtHbleLRwk/hLvJ3d/3A+emVb
|
||||||
|
vs2I07tG+hfqkR2NlY5ff4cutd2WdVCcHYw0cF2S785cY+LfDz92bT03lswfm0qz
|
||||||
|
PoJbKtk2FYRcpddC3KxQl4gSDzmyiWEMuzOHDflMJO0K5OPvuzTc9JChS0n+Qb2y
|
||||||
|
klFtCZskKDy1NpM+pLH/RgFmivs8xiLAOsyk5/Ln5itHH1lm/pSaaj2XtI4aTSk=
|
||||||
|
-----END X509 CRL-----
|
24
pkgs/clan-vm-manager/tests/data/vnc-security/ca.crt
Normal file
24
pkgs/clan-vm-manager/tests/data/vnc-security/ca.crt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIID9DCCAtygAwIBAgIURrbmCTlyih5uvTVoknewtlE0M6QwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwgYAxCzAJBgNVBAYTAlVTMRIwEAYDVQQIDAlZb3VyU3RhdGUxETAPBgNVBAcM
|
||||||
|
CFlvdXJDaXR5MRswGQYDVQQKDBJZb3VyQ0FPcmdhbml6YXRpb24xGTAXBgNVBAsM
|
||||||
|
EFlvdXJDQURlcGFydG1lbnQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0yNDAzMjgx
|
||||||
|
MTUwMDFaFw0zNDAzMjYxMTUwMDFaMIGAMQswCQYDVQQGEwJVUzESMBAGA1UECAwJ
|
||||||
|
WW91clN0YXRlMREwDwYDVQQHDAhZb3VyQ2l0eTEbMBkGA1UECgwSWW91ckNBT3Jn
|
||||||
|
YW5pemF0aW9uMRkwFwYDVQQLDBBZb3VyQ0FEZXBhcnRtZW50MRIwEAYDVQQDDAkx
|
||||||
|
MjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDcb6ZpdEVi
|
||||||
|
D/TAdMtpI/lLOLBnBuA5+5+SEbq2M3U+7tmZ1GxhuJhFddP+JbLq84X5BzFYS3Be
|
||||||
|
Hn9W+iHOr58BvAdFi6lS1H7w5ZwRPXsZ+/HlydWBszKmTi7h8ByB5UnOezduRc26
|
||||||
|
gT6zPLtMXuUCAN9dAmThCA2G74EwUTJyZYOql+8wM3BbfE6wcnVdkADQcqFs86C4
|
||||||
|
0o9qitOoCXmtdd9TVDkyLcQMgxEx1W3LHvH2lNpp60T/FetilrdqsICaon/IbiYf
|
||||||
|
XeUtqUE2L61Os8Y0FqRxquMIgYm+vPtTOQk8v4slNmsnl/veFY+SpNt3pY1FNK6b
|
||||||
|
n1XaZop9p2pLAgMBAAGjZDBiMB0GA1UdDgQWBBReyxFuGLwIL/Q/dJXUSv51s4my
|
||||||
|
6TAfBgNVHSMEGDAWgBReyxFuGLwIL/Q/dJXUSv51s4my6TAPBgNVHRMBAf8EBTAD
|
||||||
|
AQH/MA8GA1UdEQQIMAaHBH8AAAEwDQYJKoZIhvcNAQELBQADggEBAHC+9xYh9P5v
|
||||||
|
wGr1HY+tQdDzc0xpsXGtsHbUK859OJXI8HWew6+iAefMrnKBYBXphnmp5IolHlP+
|
||||||
|
qaUs2Y7Lih+z28skQh2kpJHnJMKbAN0MLucA6NTbRdQD7EyAO91cuZQ7PS3DyAvy
|
||||||
|
DosjgK4aMvpd07cbYLCTqaczz4KN/EdjMcvK2QelqtzZphGq+5meQDafLXBtf+CG
|
||||||
|
MmTh9szwYzzk7dGurNdsgugNNvk/X0p074SWjLvsK5FCuRJEh23Wx3bWBDMiVUcr
|
||||||
|
RrgVjJ47Jyeozto4whOglQRk9hc2VBjmeXBYlmi+PorcRFZrvAdWilBCsi37ki2G
|
||||||
|
qTrAwVx06AE=
|
||||||
|
-----END CERTIFICATE-----
|
28
pkgs/clan-vm-manager/tests/data/vnc-security/ca.key
Normal file
28
pkgs/clan-vm-manager/tests/data/vnc-security/ca.key
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDcb6ZpdEViD/TA
|
||||||
|
dMtpI/lLOLBnBuA5+5+SEbq2M3U+7tmZ1GxhuJhFddP+JbLq84X5BzFYS3BeHn9W
|
||||||
|
+iHOr58BvAdFi6lS1H7w5ZwRPXsZ+/HlydWBszKmTi7h8ByB5UnOezduRc26gT6z
|
||||||
|
PLtMXuUCAN9dAmThCA2G74EwUTJyZYOql+8wM3BbfE6wcnVdkADQcqFs86C40o9q
|
||||||
|
itOoCXmtdd9TVDkyLcQMgxEx1W3LHvH2lNpp60T/FetilrdqsICaon/IbiYfXeUt
|
||||||
|
qUE2L61Os8Y0FqRxquMIgYm+vPtTOQk8v4slNmsnl/veFY+SpNt3pY1FNK6bn1Xa
|
||||||
|
Zop9p2pLAgMBAAECggEAOHy77iYHaZuhInhLb8PyLB54xc3zQ6iBOZMlf28sSlY2
|
||||||
|
mL7gjyIYkyQgkO3kLWT+HdSEBpY+U0AJbaZnZ2mFm5ItYtrpJvqhFOYh2iEhHIV9
|
||||||
|
dV8FQVlET22VgfxfscGp6imVCMMGdxaLlK6paGag1KSYmGL2qtu/a6aQOmt0O+/h
|
||||||
|
iWYSpl2Rgdwi85fyEay/ziccmPKbkJEt4yjqhcEjzxZi+NDYvqEkdCa47pKZB6mY
|
||||||
|
5E8Eh+HdMaBNx1tVcs5gclQBm4PjTLFPpZohbVZiv88hvP79RYlIGHvUiWkEN4A7
|
||||||
|
6uGgJEcCH85SHNEE6Bfg9kD9nW2Lq4jvDJ0tMhibQQKBgQD+E13YmewzzROy8UKY
|
||||||
|
FxN8gqHWSkRJ5v1vP/z7WSibGOpkhLwRL2p4HHPvpU6F1272E3cPSHEDZ7QbrBn1
|
||||||
|
gWfCFk8zyj/LxEtvDsfEYL/ShKvDVXBaKmb8llILIR1LbtqEKu+hkuEDSmaFv9Gw
|
||||||
|
nGcfj3B2RN4LlZmmRQE0Adf4UQKBgQDeGw8ZyA9yd1XYsdHoh+YxZVZv+mxxU1SO
|
||||||
|
AYlmEBSFbrzl+zMg+exgb5GRmYWKSqhSvw0yGjcy37SAXDK4+faXyZ2YR2NjVzDh
|
||||||
|
4Odqu4aYwE7HU5Y/9zpsn40qc9UylRDBkcX9Dekj1b9hRzj19nFNVBJGjgZRLgMV
|
||||||
|
57UgGxPt2wKBgHMzLsLuD4XxPzRMZch19hTnWh/CbrIfdNvDZJ5Gb73bDzPiZy9X
|
||||||
|
k2vAYuTOzAqtgpc6fipEy1Ei7Sv63Y5OTVBYMzMlScXHS/if9/3XbEI0e3jGvXl0
|
||||||
|
bluqgKqhKhowug1hNmPJKBMI4fFU5uuwDqXlsLU/Rnp0K0WTVhdRmq3xAoGAQTpz
|
||||||
|
KeAuYTCY3qYCfqcCvLkFNKe4F2Qgrf/XiUjprfJCucwXTPT5La02dCtBI8cfPgXr
|
||||||
|
6y31zhQS36u0Hc0TVaqZhPJaRv+BVKUHcboXIl9AA5wRwUFrQCFvhOs1zsAmhqK4
|
||||||
|
IcRnFuYcaYZQPTQePFaXc28cfdTkhRdig0ZQiQcCgYEAtUGFZHsKP+Ft23m4X9KR
|
||||||
|
nYBehkG/L3S9PinXIBQU/32R1lc2G/ziMYijh8p9fYlzYm3AkkFURfBPHvCm8nl8
|
||||||
|
xduLA2D/CPb1HelwcuPNSrK6bJbu/Knn73ki1DUDtOQI/MhUc83SudGYzkPJvZTU
|
||||||
|
r5QeLVBf34I4meNOzOOXmag=
|
||||||
|
-----END PRIVATE KEY-----
|
1
pkgs/clan-vm-manager/tests/data/vnc-security/ca.srl
Normal file
1
pkgs/clan-vm-manager/tests/data/vnc-security/ca.srl
Normal file
@ -0,0 +1 @@
|
|||||||
|
070517FB67B9D171836447F4C188BB6C1F52E712
|
1
pkgs/clan-vm-manager/tests/data/vnc-security/crlnumber
Normal file
1
pkgs/clan-vm-manager/tests/data/vnc-security/crlnumber
Normal file
@ -0,0 +1 @@
|
|||||||
|
1001
|
@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Check if openssl is available
|
||||||
|
if ! command -v openssl &> /dev/null; then
|
||||||
|
echo "openssl is not installed. Please install openssl to generate the certificates."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 0: Define CA subject details
|
||||||
|
CA_SUBJECT="/C=US/ST=YourState/L=YourCity/O=YourCAOrganization/OU=YourCADepartment/CN=127.0.0.1"
|
||||||
|
|
||||||
|
# Step 1: Generate the CA's private key
|
||||||
|
openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:2048
|
||||||
|
|
||||||
|
# Step 2: Create a self-signed CA certificate
|
||||||
|
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt -subj "$CA_SUBJECT" -addext "subjectAltName = IP:127.0.0.1"
|
||||||
|
|
||||||
|
# Step 3: Generate a private key for the TLS certificate
|
||||||
|
openssl genpkey -algorithm RSA -out tls.key -pkeyopt rsa_keygen_bits:2048
|
||||||
|
|
||||||
|
# Step 4: Create a Certificate Signing Request (CSR) for the TLS certificate
|
||||||
|
TLS_SUBJECT="/C=US/ST=YourState/L=YourCity/O=YourOrganization/OU=YourDepartment/CN=127.0.0.1"
|
||||||
|
openssl req -new -key tls.key -out tls.csr -subj "$TLS_SUBJECT" -addext "subjectAltName = IP:127.0.0.1"
|
||||||
|
|
||||||
|
# Step 5: Sign the CSR with your CA to generate the TLS certificate
|
||||||
|
openssl x509 -req -in tls.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt -days 365 -sha256 -extfile <(printf "subjectAltName=IP:127.0.0.1")
|
||||||
|
|
||||||
|
# Step 6: Generate a ca.crl file needed for libvncserver
|
||||||
|
openssl ca -config ./openssl.conf -gencrl -keyfile ca.key -cert ca.crt -out ca.crl
|
||||||
|
|
||||||
|
echo "CA and TLS certificate, CSR, and key have been generated."
|
65
pkgs/clan-vm-manager/tests/data/vnc-security/openssl.conf
Normal file
65
pkgs/clan-vm-manager/tests/data/vnc-security/openssl.conf
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
[ ca ]
|
||||||
|
default_ca = CA_default
|
||||||
|
|
||||||
|
[ CA_default ]
|
||||||
|
dir = . # Where everything is stored
|
||||||
|
certs = $dir/certs # Where the issued certs are kept
|
||||||
|
crl_dir = $dir/crl # Where the issued crl are kept
|
||||||
|
database = $dir/index.txt # Database index file
|
||||||
|
new_certs_dir = $dir/newcerts
|
||||||
|
certificate = $dir/cacert.pem # The CA certificate
|
||||||
|
serial = $dir/serial # The current serial number
|
||||||
|
crlnumber = $dir/crlnumber # The current crl number
|
||||||
|
crl = $dir/crl.pem # The current CRL
|
||||||
|
private_key = $dir/private/cakey.pem
|
||||||
|
RANDFILE = $dir/private/.rand
|
||||||
|
|
||||||
|
x509_extensions = usr_cert # The extensions to add to the cert
|
||||||
|
crlnumber = $dir/crlnumber # the current crl number must be commented out to leave a V1 CRL
|
||||||
|
crl = $dir/crl.pem # The current CRL
|
||||||
|
private_key = $dir/private/cakey.pem
|
||||||
|
RANDFILE = $dir/private/.rand
|
||||||
|
|
||||||
|
default_days = 365 # How long to certify for
|
||||||
|
default_crl_days = 30 # How long before next CRL
|
||||||
|
default_md = sha256 # Which md to use.
|
||||||
|
preserve = no # Keep passed DN ordering
|
||||||
|
|
||||||
|
policy = policy_match
|
||||||
|
|
||||||
|
[ policy_match ]
|
||||||
|
countryName = optional
|
||||||
|
stateOrProvinceName = optional
|
||||||
|
organizationName = optional
|
||||||
|
organizationalUnitName = optional
|
||||||
|
commonName = supplied
|
||||||
|
emailAddress = optional
|
||||||
|
|
||||||
|
[ req ]
|
||||||
|
default_bits = 2048
|
||||||
|
default_md = sha256
|
||||||
|
prompt = no
|
||||||
|
distinguished_name = req_distinguished_name
|
||||||
|
x509_extensions = v3_ca
|
||||||
|
|
||||||
|
[ req_distinguished_name ]
|
||||||
|
countryName = US
|
||||||
|
stateOrProvinceName = California
|
||||||
|
localityName = San Francisco
|
||||||
|
0.organizationName = My Organization
|
||||||
|
organizationalUnitName = My Organizational Unit
|
||||||
|
commonName = My CA
|
||||||
|
emailAddress = email@example.com
|
||||||
|
|
||||||
|
[ v3_ca ]
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
authorityKeyIdentifier = keyid:always,issuer
|
||||||
|
basicConstraints = critical,CA:true
|
||||||
|
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
||||||
|
|
||||||
|
[ usr_cert ]
|
||||||
|
basicConstraints = CA:FALSE
|
||||||
|
nsComment = "OpenSSL Generated Certificate"
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
authorityKeyIdentifier = keyid,issuer
|
||||||
|
|
23
pkgs/clan-vm-manager/tests/data/vnc-security/tls.crt
Normal file
23
pkgs/clan-vm-manager/tests/data/vnc-security/tls.crt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIID3jCCAsagAwIBAgIUBwUX+2e50XGDZEf0wYi7bB9S5xIwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwgYAxCzAJBgNVBAYTAlVTMRIwEAYDVQQIDAlZb3VyU3RhdGUxETAPBgNVBAcM
|
||||||
|
CFlvdXJDaXR5MRswGQYDVQQKDBJZb3VyQ0FPcmdhbml6YXRpb24xGTAXBgNVBAsM
|
||||||
|
EFlvdXJDQURlcGFydG1lbnQxEjAQBgNVBAMMCTEyNy4wLjAuMTAeFw0yNDAzMjgx
|
||||||
|
MTUwMDJaFw0yNTAzMjgxMTUwMDJaMHwxCzAJBgNVBAYTAlVTMRIwEAYDVQQIDAlZ
|
||||||
|
b3VyU3RhdGUxETAPBgNVBAcMCFlvdXJDaXR5MRkwFwYDVQQKDBBZb3VyT3JnYW5p
|
||||||
|
emF0aW9uMRcwFQYDVQQLDA5Zb3VyRGVwYXJ0bWVudDESMBAGA1UEAwwJMTI3LjAu
|
||||||
|
MC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwYSlR4nEj4LuNclm
|
||||||
|
QFfqei1FU+bvLggzWzNNGfhGiqArzV8F7snUGaGymA6ZbXAj//Z+XXDQ5tE4DTDP
|
||||||
|
fU2SD38VpswioCtco/9ke3zZXXvZqnUhryWMfNjpxNF4E3UVkOsWwvkLV5qODsGr
|
||||||
|
K9+0oXxUYHIfcryF1O7F2Kf0HeHdEP15EB6pwb9qW/YcH/+M06P8zOS4dv7etZKv
|
||||||
|
ntGQevlF3Xw6KNy7PZ3iQgmigctA/MlYHUoNXCRNL2Wq5/QnVTSJbf9p+WN5DHEn
|
||||||
|
p662DRZT/K0pzJETB3/Sqswi86S/+bnggEegPeRzVDz0k3qXgia2S254mxibkD9R
|
||||||
|
7yTYiwIDAQABo1MwUTAPBgNVHREECDAGhwR/AAABMB0GA1UdDgQWBBSoZWaF9pFA
|
||||||
|
JEKZcTGJYnUkf/kFjjAfBgNVHSMEGDAWgBReyxFuGLwIL/Q/dJXUSv51s4my6TAN
|
||||||
|
BgkqhkiG9w0BAQsFAAOCAQEAicFkgSl+smSTM3SK9O4f8OOBtcX1aF1XkLvXHXpt
|
||||||
|
JIXyPkJbW+44Vb8q/ARI/4Nss4AlWuCWy0vHAhhPmvqBl0sr7CL/jAfJTSq++7P3
|
||||||
|
XjfQ/l7hT26DejmqXMZxIF89EwZha0DmetEIYnowvp6oiFluuMGY2qOzF0NXZTAK
|
||||||
|
NvHrj5yT1bpq6qdPLcKszaR9+MYmVC0r4pAargfpjCr01lBRH1h+p5QEEf/tYmQP
|
||||||
|
BY0pWbgI3wnMPtFCD0fPLHhCJXnWTpGeFsAczOaF3YJicCKg+3EusiazwJihzYfY
|
||||||
|
FdTnJlD2KkP2ipIia5q7wja7SeWBV8hzPvEuqvlUW0BCKQ==
|
||||||
|
-----END CERTIFICATE-----
|
18
pkgs/clan-vm-manager/tests/data/vnc-security/tls.csr
Normal file
18
pkgs/clan-vm-manager/tests/data/vnc-security/tls.csr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIC4zCCAcsCAQAwfDELMAkGA1UEBhMCVVMxEjAQBgNVBAgMCVlvdXJTdGF0ZTER
|
||||||
|
MA8GA1UEBwwIWW91ckNpdHkxGTAXBgNVBAoMEFlvdXJPcmdhbml6YXRpb24xFzAV
|
||||||
|
BgNVBAsMDllvdXJEZXBhcnRtZW50MRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0G
|
||||||
|
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBhKVHicSPgu41yWZAV+p6LUVT5u8u
|
||||||
|
CDNbM00Z+EaKoCvNXwXuydQZobKYDpltcCP/9n5dcNDm0TgNMM99TZIPfxWmzCKg
|
||||||
|
K1yj/2R7fNlde9mqdSGvJYx82OnE0XgTdRWQ6xbC+QtXmo4Owasr37ShfFRgch9y
|
||||||
|
vIXU7sXYp/Qd4d0Q/XkQHqnBv2pb9hwf/4zTo/zM5Lh2/t61kq+e0ZB6+UXdfDoo
|
||||||
|
3Ls9neJCCaKBy0D8yVgdSg1cJE0vZarn9CdVNIlt/2n5Y3kMcSenrrYNFlP8rSnM
|
||||||
|
kRMHf9KqzCLzpL/5ueCAR6A95HNUPPSTepeCJrZLbnibGJuQP1HvJNiLAgMBAAGg
|
||||||
|
IjAgBgkqhkiG9w0BCQ4xEzARMA8GA1UdEQQIMAaHBH8AAAEwDQYJKoZIhvcNAQEL
|
||||||
|
BQADggEBAH8RwLJXJZWq4Q91+/cLP/s8aKge3CK09laDAcWMNDWfvR8+mNlSte7P
|
||||||
|
EwVVL1GJN6ywwy0xCLKCY2Ixb66jfaW6lcphVgWiP0+8l8t/7p0MVRD9Uogf3nut
|
||||||
|
Sj4cyzjUcqR6zK7MYwBEHy+PFURjhpvYcskClmZYSP2WQIzt7ZyYBO95NZ8nTM3t
|
||||||
|
Fr6VUIqs8aiLevQbLhjU8eJCyo20WF2/XiDRyub6tEbz1onU1WwTXuEMsWdL4M78
|
||||||
|
/jbB9GQ+XK48kNluw3URz8sPBU6iilgj4xoEcR/A31ORnaEULL5udLr2akq3sOm8
|
||||||
|
CeibewRVc1nnivi0d+WUi0NvSgPdeuA=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
28
pkgs/clan-vm-manager/tests/data/vnc-security/tls.key
Normal file
28
pkgs/clan-vm-manager/tests/data/vnc-security/tls.key
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDBhKVHicSPgu41
|
||||||
|
yWZAV+p6LUVT5u8uCDNbM00Z+EaKoCvNXwXuydQZobKYDpltcCP/9n5dcNDm0TgN
|
||||||
|
MM99TZIPfxWmzCKgK1yj/2R7fNlde9mqdSGvJYx82OnE0XgTdRWQ6xbC+QtXmo4O
|
||||||
|
wasr37ShfFRgch9yvIXU7sXYp/Qd4d0Q/XkQHqnBv2pb9hwf/4zTo/zM5Lh2/t61
|
||||||
|
kq+e0ZB6+UXdfDoo3Ls9neJCCaKBy0D8yVgdSg1cJE0vZarn9CdVNIlt/2n5Y3kM
|
||||||
|
cSenrrYNFlP8rSnMkRMHf9KqzCLzpL/5ueCAR6A95HNUPPSTepeCJrZLbnibGJuQ
|
||||||
|
P1HvJNiLAgMBAAECggEAIJ5Bl53Onlv01+cTD5xh/ub7jQlbXlhug5xRjiONjFc0
|
||||||
|
GuE96EJnuExLhJrNXKduwfmj0g8ufwFb38lO5/F3wZnrpdo5qeK1MkVdg/0GzF2Q
|
||||||
|
Uk18+H8tP2v2d0DRawIsuOkPRJzivwjjkfQt7G7ADQoeVMVXrKi/LCV0/rBMku6Q
|
||||||
|
oIjgY/odhX6w+Xb1RNqvtgvI2VpIQFTrb17GDF+RUVBADjoSNQyeqg/LsNvDne0N
|
||||||
|
7VitPJsdwNjgt6SmSBp/R0rBI3LHgSuk/2/ca9tFbBMmI9PYKQhUbXod14D8i6WH
|
||||||
|
jMsaBKXNeqYkkrXUQFKo2YmIfb5RHMRgxv/CYdxT4QKBgQDhvG9yPPLwfj3Ni76Q
|
||||||
|
4PyQtuHdpD0H35D4pnLQqv18Ec5aIu7sCjG+EJSBLRWD2PPAVieGX5/AyS31pBf3
|
||||||
|
6MUaqISrfGMVtU4egTIoK5fbJDy9w4da/McjxbIIE+UK/hhvOYn6CeVtC9v2VW1i
|
||||||
|
hZlWkyiIlMwcV4QNR6i9aFRJ4QKBgQDbdnKixXCKinNuKLAsu3MXWVBk3YT4u16P
|
||||||
|
+D8FCWm859nV2i69+FFVG7zTvzsvjK41Ya9OGggaK3jrLbEwsNu0R4FVaqSLs34c
|
||||||
|
fdq2ZegU/TUTZvnkWAiQuFd9CEcSGr3qnLEC3nFCuUFLyYLZ80zuYWjOE4vZdrQ8
|
||||||
|
Eu0bHHvn6wKBgBMIpoUFap6opmFshRcGQYWaRhVAQf0l9r1gm5HIuTL69WFYTLkO
|
||||||
|
av9RupPhz0ycwIDZQt/rtDa3P+7UdUjsEaKbzwP+qwQrk3izAB2u/1D1D0IY+JLN
|
||||||
|
eaUkiExyEQAKSNkoCuBQcU3ukA+HSH/kL/fC1Mofcc55+qJ8BlhiMalBAoGASMUu
|
||||||
|
3+A+IAImollliX+iexSHftqhM+TVR0HWi7ICWLw8VBfjteQ3+9OVulTHqE2qmlLI
|
||||||
|
0Un6c8sEbl8ZSP7r6wxmy07wPs6Gu6XTtvV1jjgjuEpGBDxYorwtbm0nO86YOMo6
|
||||||
|
O6xMvAY3q4ynEeQGF2k/Wk3K6pHc06qm6n14bH8CgYEAn86QvwgBRT7zoVO1CPRu
|
||||||
|
hwkaxYctJq72skNzsiNn/uC8GtxQcUWyqIU4Sc3pNDpLYPLeMpTD9E//BCUUAum1
|
||||||
|
kD0LB8e48Lua72LF3fy0XZGKUsmKm5YXqtDf2lh1mPt47c4pMooJrn2f9RBP2cdW
|
||||||
|
7R2cJtcxu+Y4Sgxp2N9EcfE=
|
||||||
|
-----END PRIVATE KEY-----
|
0
pkgs/clan-vm-manager/tests/helpers/__init__.py
Normal file
0
pkgs/clan-vm-manager/tests/helpers/__init__.py
Normal file
2493
pkgs/clan-vm-manager/tests/helpers/libvncclient.py
Normal file
2493
pkgs/clan-vm-manager/tests/helpers/libvncclient.py
Normal file
File diff suppressed because it is too large
Load Diff
312
pkgs/clan-vm-manager/tests/helpers/vnc_client.py
Normal file
312
pkgs/clan-vm-manager/tests/helpers/vnc_client.py
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import threading
|
||||||
|
from ctypes import (
|
||||||
|
CDLL,
|
||||||
|
POINTER,
|
||||||
|
Structure,
|
||||||
|
addressof,
|
||||||
|
c_char_p,
|
||||||
|
c_int,
|
||||||
|
c_size_t,
|
||||||
|
c_uint8,
|
||||||
|
c_void_p,
|
||||||
|
cast,
|
||||||
|
memmove,
|
||||||
|
sizeof,
|
||||||
|
)
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
import libvncclient
|
||||||
|
from libvncclient import (
|
||||||
|
GetCredentialProc,
|
||||||
|
GotCursorShapeProc,
|
||||||
|
GotFrameBufferUpdateProc,
|
||||||
|
GotXCutTextProc,
|
||||||
|
HandleKeyboardLedStateProc,
|
||||||
|
HandleRFBServerMessage,
|
||||||
|
MallocFrameBufferProc,
|
||||||
|
SendFramebufferUpdateRequest,
|
||||||
|
SetFormatAndEncodings,
|
||||||
|
String,
|
||||||
|
WaitForMessage,
|
||||||
|
rfbClient,
|
||||||
|
rfbClientCleanup,
|
||||||
|
rfbCredential,
|
||||||
|
rfbCredentialTypeUser,
|
||||||
|
rfbCredentialTypeX509,
|
||||||
|
rfbGetClient,
|
||||||
|
rfbInitClient,
|
||||||
|
struct__rfbClient,
|
||||||
|
)
|
||||||
|
|
||||||
|
path_to_lib = libvncclient._libs["libvncclient.so"].access["cdecl"]._name
|
||||||
|
if path_to_lib.startswith("/nix/store/"):
|
||||||
|
print("Using libvncclient from nix store")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
libc = CDLL("libc.so.6") # Use the correct path for your libc
|
||||||
|
libc.malloc.argtypes = [c_size_t]
|
||||||
|
libc.malloc.restype = c_void_p
|
||||||
|
|
||||||
|
|
||||||
|
def alloc_str(data: str) -> c_char_p:
|
||||||
|
bdata = data.encode("ascii") + b"\0"
|
||||||
|
data_buf = libc.malloc(len(bdata)) # +1 for null terminator
|
||||||
|
|
||||||
|
memmove(data_buf, c_char_p(bdata), len(bdata))
|
||||||
|
|
||||||
|
return data_buf
|
||||||
|
|
||||||
|
|
||||||
|
StructType = TypeVar("StructType", bound="Structure")
|
||||||
|
|
||||||
|
|
||||||
|
def got_cursor_shape(
|
||||||
|
cl: rfbClient,
|
||||||
|
width: int,
|
||||||
|
height: int,
|
||||||
|
xhot: int,
|
||||||
|
yhot: int,
|
||||||
|
data: POINTER(c_uint8), # type: ignore[valid-type]
|
||||||
|
) -> None:
|
||||||
|
print(f"got_cursor_shape: {width} {height} {xhot} {yhot}")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def alloc_struct(data: StructType) -> int:
|
||||||
|
data_buf = libc.malloc(sizeof(data))
|
||||||
|
memmove(data_buf, addressof(data), sizeof(data))
|
||||||
|
return data_buf
|
||||||
|
|
||||||
|
|
||||||
|
def get_credential(
|
||||||
|
rfb_client: POINTER(struct__rfbClient), # type: ignore[valid-type]
|
||||||
|
credential_type: c_int,
|
||||||
|
) -> int | None:
|
||||||
|
print(f"==> get_credential: {credential_type}")
|
||||||
|
|
||||||
|
if credential_type == rfbCredentialTypeUser:
|
||||||
|
creds = rfbCredential()
|
||||||
|
username = os.environ.get("USER")
|
||||||
|
if not username:
|
||||||
|
print("ERROR: USER environment variable is not set")
|
||||||
|
return None
|
||||||
|
creds.userCredential.username = alloc_str(username)
|
||||||
|
creds.userCredential.password = None
|
||||||
|
creds_buf = alloc_struct(creds)
|
||||||
|
|
||||||
|
# Return a integer to the creds obj
|
||||||
|
return creds_buf
|
||||||
|
|
||||||
|
if credential_type == rfbCredentialTypeX509:
|
||||||
|
ca_dir = (
|
||||||
|
Path(os.environ.get("GIT_ROOT", ""))
|
||||||
|
/ "pkgs"
|
||||||
|
/ "clan-vm-manager"
|
||||||
|
/ "tests"
|
||||||
|
/ "data"
|
||||||
|
/ "vnc-security"
|
||||||
|
)
|
||||||
|
ca_cert = ca_dir / "ca.crt"
|
||||||
|
if not ca_cert.exists():
|
||||||
|
print(f"ERROR: ca_cert does not exist: {ca_cert}")
|
||||||
|
return None
|
||||||
|
ca_crl = ca_dir / "ca.key"
|
||||||
|
if not ca_crl.exists():
|
||||||
|
print(f"ERROR: ca_crl does not exist: {ca_crl}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Instantiate the credential union and populate it
|
||||||
|
creds = rfbCredential()
|
||||||
|
creds.x509Credential.x509CACertFile = alloc_str(str(ca_cert))
|
||||||
|
creds.x509Credential.x509CrlVerifyMode = False
|
||||||
|
print("===> Alloc struct")
|
||||||
|
creds_buf = alloc_struct(creds)
|
||||||
|
print("====> Done alloc struct")
|
||||||
|
|
||||||
|
# Return a integer to the creds obj
|
||||||
|
return creds_buf
|
||||||
|
|
||||||
|
print(f"ERROR: Unknown credential type: {credential_type}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def got_selection(cl: rfbClient, text: str, text_len: int) -> None:
|
||||||
|
print(f"got_selection: {text}")
|
||||||
|
|
||||||
|
|
||||||
|
def resize(client: rfbClient) -> bool:
|
||||||
|
width = client.contents.width
|
||||||
|
height = client.contents.height
|
||||||
|
bits_per_pixel = client.contents.format.bitsPerPixel
|
||||||
|
print(f"Size: {width}x{height}")
|
||||||
|
|
||||||
|
if client.contents.frameBuffer:
|
||||||
|
libc.free(client.contents.frameBuffer)
|
||||||
|
client.contents.frameBuffer = None
|
||||||
|
|
||||||
|
new_buf = libc.malloc(int(width * height * bits_per_pixel / 8))
|
||||||
|
if not new_buf:
|
||||||
|
print("malloc failed")
|
||||||
|
return False
|
||||||
|
|
||||||
|
casted_buf = cast(new_buf, POINTER(c_uint8))
|
||||||
|
client.contents.frameBuffer = casted_buf
|
||||||
|
|
||||||
|
request = SendFramebufferUpdateRequest(client, 0, 0, width, height, False)
|
||||||
|
if not request:
|
||||||
|
print("SendFramebufferUpdateRequest failed")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def update(cl: rfbClient, x: int, y: int, w: int, h: int) -> None:
|
||||||
|
print(f"update: {x} {y} {w} {h}")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def kbd_leds(cl: rfbClient, value: int, pad: int) -> None:
|
||||||
|
print(f"kbd_leds: {value} {pad}")
|
||||||
|
|
||||||
|
|
||||||
|
# /*****************************************************************************
|
||||||
|
# *
|
||||||
|
# * Encoding types
|
||||||
|
# *
|
||||||
|
# *****************************************************************************/
|
||||||
|
|
||||||
|
# #define rfbEncodingRaw 0
|
||||||
|
# #define rfbEncodingCopyRect 1
|
||||||
|
# #define rfbEncodingRRE 2
|
||||||
|
# #define rfbEncodingCoRRE 4
|
||||||
|
# #define rfbEncodingHextile 5
|
||||||
|
# #define rfbEncodingZlib 6
|
||||||
|
# #define rfbEncodingTight 7
|
||||||
|
# #define rfbEncodingTightPng 0xFFFFFEFC /* -260 */
|
||||||
|
# #define rfbEncodingZlibHex 8
|
||||||
|
# #define rfbEncodingUltra 9
|
||||||
|
# #define rfbEncodingTRLE 15
|
||||||
|
# #define rfbEncodingZRLE 16
|
||||||
|
# #define rfbEncodingZYWRLE 17
|
||||||
|
|
||||||
|
# #define rfbEncodingH264 0x48323634
|
||||||
|
|
||||||
|
# /* Cache & XOR-Zlib - rdv@2002 */
|
||||||
|
# #define rfbEncodingCache 0xFFFF0000
|
||||||
|
# #define rfbEncodingCacheEnable 0xFFFF0001
|
||||||
|
# #define rfbEncodingXOR_Zlib 0xFFFF0002
|
||||||
|
# #define rfbEncodingXORMonoColor_Zlib 0xFFFF0003
|
||||||
|
# #define rfbEncodingXORMultiColor_Zlib 0xFFFF0004
|
||||||
|
# #define rfbEncodingSolidColor 0xFFFF0005
|
||||||
|
# #define rfbEncodingXOREnable 0xFFFF0006
|
||||||
|
# #define rfbEncodingCacheZip 0xFFFF0007
|
||||||
|
# #define rfbEncodingSolMonoZip 0xFFFF0008
|
||||||
|
# #define rfbEncodingUltraZip 0xFFFF0009
|
||||||
|
|
||||||
|
|
||||||
|
class VncError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class VncClient:
|
||||||
|
client: rfbClient
|
||||||
|
stop_event: threading.Event
|
||||||
|
thread: threading.Thread | None
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
bits_per_sample = 8
|
||||||
|
samples_per_pixel = 3
|
||||||
|
bytes_per_pixel = 4
|
||||||
|
self.client: rfbClient = rfbGetClient(
|
||||||
|
bits_per_sample, samples_per_pixel, bytes_per_pixel
|
||||||
|
)
|
||||||
|
if not self.client:
|
||||||
|
raise VncError("rfbGetClient failed")
|
||||||
|
|
||||||
|
self.stop_event = threading.Event() # Initialize the stop event.
|
||||||
|
self.thread = None
|
||||||
|
|
||||||
|
self._client_settings()
|
||||||
|
self._set_color()
|
||||||
|
self._set_encoding()
|
||||||
|
|
||||||
|
def _client_settings(self) -> None:
|
||||||
|
# client settings
|
||||||
|
self.client.contents.MallocFrameBuffer = MallocFrameBufferProc(resize)
|
||||||
|
self.client.contents.canHandleNewFBSize = True
|
||||||
|
self.client.contents.GotFrameBufferUpdate = GotFrameBufferUpdateProc(update)
|
||||||
|
self.client.contents.HandleKeyboardLedState = HandleKeyboardLedStateProc(
|
||||||
|
kbd_leds
|
||||||
|
)
|
||||||
|
self.client.contents.GotXCutText = GotXCutTextProc(got_selection)
|
||||||
|
self.client.contents.GotCursorShape = GotCursorShapeProc(got_cursor_shape)
|
||||||
|
self.client.contents.GetCredential = GetCredentialProc(get_credential)
|
||||||
|
self.client.contents.listenPort = 5900
|
||||||
|
self.client.contents.listenAddress = String.from_param("127.0.0.1")
|
||||||
|
|
||||||
|
def _set_color(self) -> None:
|
||||||
|
# Set client encoding to (equal to remmina)
|
||||||
|
# TRUE colour: max red 255 green 255 blue 255, shift red 16 green 8 blue 0
|
||||||
|
self.client.contents.format.bitsPerPixel = 32
|
||||||
|
self.client.contents.format.depth = 24
|
||||||
|
self.client.contents.format.redShift = 16
|
||||||
|
self.client.contents.format.blueShift = 0
|
||||||
|
self.client.contents.format.greenShift = 8
|
||||||
|
self.client.contents.format.blueMax = 0xFF
|
||||||
|
self.client.contents.format.redMax = 0xFF
|
||||||
|
self.client.contents.format.greenMax = 0xFF
|
||||||
|
SetFormatAndEncodings(self.client)
|
||||||
|
|
||||||
|
def _set_encoding(self) -> None:
|
||||||
|
# Set client compression to remminas quality 9 (best) and compress level 1 (lowest)
|
||||||
|
# BUG: tight encoding is crashing (looks exploitable)
|
||||||
|
self.client.contents.appData.shareDesktop = True
|
||||||
|
self.client.contents.appData.useBGR233 = False
|
||||||
|
self.client.contents.appData.encodingsString = String.from_param(
|
||||||
|
"copyrect zlib hextile raw"
|
||||||
|
)
|
||||||
|
self.client.contents.appData.compressLevel = 1
|
||||||
|
self.client.contents.appData.qualityLevel = 9
|
||||||
|
SetFormatAndEncodings(self.client)
|
||||||
|
|
||||||
|
def start_blocking(self) -> None:
|
||||||
|
print("Initializing connection")
|
||||||
|
argc = c_int(0)
|
||||||
|
argv = None
|
||||||
|
if not rfbInitClient(self.client, argc, argv):
|
||||||
|
raise VncError("rfbInitClient failed")
|
||||||
|
|
||||||
|
# Main loop
|
||||||
|
while not self.stop_event.is_set():
|
||||||
|
res = WaitForMessage(self.client, 500)
|
||||||
|
if res < 0:
|
||||||
|
rfbClientCleanup(self.client)
|
||||||
|
raise VncError("WaitForMessage failed")
|
||||||
|
|
||||||
|
if res > 0:
|
||||||
|
if not HandleRFBServerMessage(self.client):
|
||||||
|
rfbClientCleanup(self.client)
|
||||||
|
raise VncError("HandleRFBServerMessage failed")
|
||||||
|
|
||||||
|
rfbClientCleanup(self.client)
|
||||||
|
|
||||||
|
def start(self) -> None:
|
||||||
|
self.thread = threading.Thread(target=self.start_blocking)
|
||||||
|
self.thread.start()
|
||||||
|
|
||||||
|
def stop(self) -> None:
|
||||||
|
self.stop_event.set() # Signal the thread to stop.
|
||||||
|
if self.thread is not None:
|
||||||
|
self.thread.join() # Wait for the thread to finish.
|
||||||
|
print("VNC client stopped.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
vnc = VncClient()
|
||||||
|
vnc.start()
|
||||||
|
assert vnc.thread is not None
|
||||||
|
vnc.thread.join()
|
5
pkgs/clan-vm-manager/tests/pygdb.sh
Executable file
5
pkgs/clan-vm-manager/tests/pygdb.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
PYTHON_DIR=$(dirname "$(which python3)")/..
|
||||||
|
gdb --quiet -ex "source $PYTHON_DIR/share/gdb/libpython.py" -ex "run" --args python "$1"
|
@ -16,12 +16,21 @@ def temporary_home(monkeypatch: pytest.MonkeyPatch) -> Iterator[Path]:
|
|||||||
path = Path(env_dir).resolve()
|
path = Path(env_dir).resolve()
|
||||||
log.debug("Temp HOME directory: %s", str(path))
|
log.debug("Temp HOME directory: %s", str(path))
|
||||||
monkeypatch.setenv("HOME", str(path))
|
monkeypatch.setenv("HOME", str(path))
|
||||||
|
monkeypatch.setenv("XDG_CONFIG_HOME", str(path / ".config"))
|
||||||
|
runtime_dir = path / "xdg-runtime-dir"
|
||||||
|
runtime_dir.mkdir()
|
||||||
|
runtime_dir.chmod(0o700)
|
||||||
|
monkeypatch.setenv("XDG_RUNTIME_DIR", str(runtime_dir))
|
||||||
monkeypatch.chdir(str(path))
|
monkeypatch.chdir(str(path))
|
||||||
yield path
|
yield path
|
||||||
else:
|
else:
|
||||||
with tempfile.TemporaryDirectory(prefix="pytest-") as dirpath:
|
with tempfile.TemporaryDirectory(prefix="pytest-") as dirpath:
|
||||||
monkeypatch.setenv("HOME", str(dirpath))
|
monkeypatch.setenv("HOME", str(dirpath))
|
||||||
monkeypatch.setenv("XDG_CONFIG_HOME", str(Path(dirpath) / ".config"))
|
monkeypatch.setenv("XDG_CONFIG_HOME", str(Path(dirpath) / ".config"))
|
||||||
|
runtime_dir = Path(dirpath) / "xdg-runtime-dir"
|
||||||
|
runtime_dir.mkdir()
|
||||||
|
runtime_dir.chmod(0o700)
|
||||||
|
monkeypatch.setenv("XDG_RUNTIME_DIR", str(runtime_dir))
|
||||||
monkeypatch.chdir(str(dirpath))
|
monkeypatch.chdir(str(dirpath))
|
||||||
log.debug("Temp HOME directory: %s", str(dirpath))
|
log.debug("Temp HOME directory: %s", str(dirpath))
|
||||||
yield Path(dirpath)
|
yield Path(dirpath)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from cli import Cli
|
from helpers.cli import Cli
|
||||||
|
|
||||||
|
|
||||||
def test_help(capfd: pytest.CaptureFixture) -> None:
|
def test_help(capfd: pytest.CaptureFixture) -> None:
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from wayland import GtkProc
|
from wayland import GtkApp
|
||||||
|
|
||||||
|
|
||||||
def test_open(app: GtkProc) -> None:
|
def test_join(app: GtkApp) -> None:
|
||||||
time.sleep(0.5)
|
while app.poll() is None:
|
||||||
assert app.poll() is None
|
time.sleep(0.1)
|
||||||
|
assert True
|
||||||
|
13
pkgs/clan-vm-manager/tests/vnc.sh
Executable file
13
pkgs/clan-vm-manager/tests/vnc.sh
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
|
||||||
|
# For vnc debugging
|
||||||
|
export VNC_USERNAME="$USER"
|
||||||
|
export VNC_PASSWORD=""
|
||||||
|
|
||||||
|
CA_CRT=$GIT_ROOT/pkgs/clan-vm-manager/tests/data/vnc-security/ca.crt
|
||||||
|
|
||||||
|
vncviewer 127.0.0.1 -Shared -X509CA "$CA_CRT"
|
||||||
|
|
@ -1,27 +1,137 @@
|
|||||||
|
# Import necessary modules
|
||||||
|
import os
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
from subprocess import Popen
|
from pathlib import Path
|
||||||
from typing import NewType
|
|
||||||
|
|
||||||
|
# Assuming NewType is already imported or defined somewhere
|
||||||
import pytest
|
import pytest
|
||||||
|
from helpers.vnc_client import VncClient
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
def write_script(cmd: list[str], new_env: dict[str, str], name: str) -> None:
|
||||||
def wayland_compositor() -> Generator[Popen, None, None]:
|
# Create the bash script content
|
||||||
# Start the Wayland compositor (e.g., Weston)
|
script_content = "#!/bin/bash\n"
|
||||||
# compositor = Popen(["weston", "--backend=headless-backend.so"])
|
for key, value in new_env.items():
|
||||||
compositor = Popen(["weston"])
|
if '"' in value:
|
||||||
yield compositor
|
value = value.replace('"', '\\"')
|
||||||
# Cleanup: Terminate the compositor
|
if "'" in value:
|
||||||
compositor.terminate()
|
value = value.replace("'", "\\'")
|
||||||
|
if "`" in value:
|
||||||
|
value = value.replace("`", "\\`")
|
||||||
|
script_content += f'export {key}="{value}"\n'
|
||||||
|
strace_cmd = ["strace", "-f", "-o", "file-access.log", "-e", "trace=file", *cmd]
|
||||||
|
script_content += shlex.join(strace_cmd)
|
||||||
|
|
||||||
|
# Write the bash script to a file
|
||||||
|
script_filename = name
|
||||||
|
with open(script_filename, "w") as script_file:
|
||||||
|
script_file.write(script_content)
|
||||||
|
|
||||||
|
print(f"You can find the script at {os.getcwd()}/{script_filename}")
|
||||||
|
# Make the script executable
|
||||||
|
os.chmod(script_filename, 0o755)
|
||||||
|
|
||||||
|
|
||||||
GtkProc = NewType("GtkProc", Popen)
|
class Compositor:
|
||||||
|
def __init__(self, proc: subprocess.Popen, env: dict[str, str]) -> None:
|
||||||
|
self.proc = proc
|
||||||
|
self.env = env
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture
|
||||||
def app() -> Generator[GtkProc, None, None]:
|
def weston_override_lib(
|
||||||
rapp = Popen([sys.executable, "-m", "clan_vm_manager"], text=True)
|
temporary_home: Path, test_root: Path
|
||||||
yield GtkProc(rapp)
|
) -> Generator[Path, None, None]:
|
||||||
# Cleanup: Terminate your application
|
# with TemporaryDirectory() as tmpdir:
|
||||||
rapp.terminate()
|
# This enforces a login shell by overriding the login shell of `getpwnam(3)`
|
||||||
|
lib_path = Path(temporary_home) / "libweston_auth_override.so"
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
os.environ.get("CC", "cc"),
|
||||||
|
"-shared",
|
||||||
|
"-fPIC",
|
||||||
|
"-o",
|
||||||
|
lib_path,
|
||||||
|
str(test_root / "weston_auth_override.c"),
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
yield lib_path
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def wayland_comp(
|
||||||
|
test_root: Path, weston_override_lib: Path
|
||||||
|
) -> Generator[Compositor, None, None]:
|
||||||
|
tls_key = test_root / "data" / "vnc-security" / "tls.key"
|
||||||
|
tls_cert = test_root / "data" / "vnc-security" / "tls.crt"
|
||||||
|
wayland_display = "wayland-1" # Define a unique WAYLAND_DISPLAY value
|
||||||
|
new_env = os.environ.copy()
|
||||||
|
new_env["WAYLAND_DISPLAY"] = wayland_display
|
||||||
|
# Uncomment the next line if you need to force software rendering
|
||||||
|
new_env["LIBGL_ALWAYS_SOFTWARE"] = "true"
|
||||||
|
new_env["LD_PRELOAD"] = str(weston_override_lib)
|
||||||
|
cmd = [
|
||||||
|
"weston",
|
||||||
|
"--debug",
|
||||||
|
"--width=1920",
|
||||||
|
"--height=1080",
|
||||||
|
"--port=5900",
|
||||||
|
f"--vnc-tls-key={tls_key}",
|
||||||
|
f"--vnc-tls-cert={tls_cert}",
|
||||||
|
"--backend=vnc",
|
||||||
|
f"--socket={wayland_display}",
|
||||||
|
]
|
||||||
|
|
||||||
|
compositor = subprocess.Popen(cmd, env=new_env, text=True)
|
||||||
|
time.sleep(0.4)
|
||||||
|
if compositor.poll() is not None:
|
||||||
|
raise Exception(f"Failed to start {cmd}")
|
||||||
|
client = VncClient()
|
||||||
|
client.start()
|
||||||
|
yield Compositor(compositor, new_env)
|
||||||
|
compositor.kill()
|
||||||
|
client.stop()
|
||||||
|
|
||||||
|
|
||||||
|
class GtkApp:
|
||||||
|
def __init__(self, proc: subprocess.Popen) -> None:
|
||||||
|
self.proc = proc
|
||||||
|
|
||||||
|
def kill(self) -> None:
|
||||||
|
self.proc.kill()
|
||||||
|
|
||||||
|
def wait(self) -> None:
|
||||||
|
self.proc.wait()
|
||||||
|
|
||||||
|
def poll(self) -> int | None:
|
||||||
|
return self.proc.poll()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def app(wayland_comp: Compositor) -> Generator[GtkApp, None, None]:
|
||||||
|
# Define the new environment variables
|
||||||
|
new_env = wayland_comp.env
|
||||||
|
new_env["GDK_BACKEND"] = "wayland"
|
||||||
|
|
||||||
|
# Define the command to be executed
|
||||||
|
cmd = [f"{sys.executable}", "-m", "clan_vm_manager"]
|
||||||
|
|
||||||
|
write_script(cmd, new_env, "weston.sh")
|
||||||
|
breakpoint()
|
||||||
|
# Execute the script
|
||||||
|
rapp = subprocess.Popen(
|
||||||
|
cmd,
|
||||||
|
text=True,
|
||||||
|
env=new_env,
|
||||||
|
stdin=sys.stdin,
|
||||||
|
)
|
||||||
|
time.sleep(0.4)
|
||||||
|
if rapp.poll() is not None:
|
||||||
|
raise Exception(f"Failed to start {cmd}")
|
||||||
|
yield GtkApp(rapp)
|
||||||
|
rapp.kill()
|
||||||
|
12
pkgs/clan-vm-manager/tests/weston_auth_override.c
Normal file
12
pkgs/clan-vm-manager/tests/weston_auth_override.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Overriding the weston_authenticate_user function
|
||||||
|
bool weston_authenticate_user(const char *username, const char *password) {
|
||||||
|
printf("=====>Overridden weston_authenticate_user called with username: %s\n", username);
|
||||||
|
return true; // Always return true
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user