zscams 2.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. zscams/__init__.py +3 -0
  2. zscams/__main__.py +43 -0
  3. zscams/agent/__init__.py +93 -0
  4. zscams/agent/certificates/.gitkeep +0 -0
  5. zscams/agent/config.yaml +103 -0
  6. zscams/agent/configuration/config.j2 +103 -0
  7. zscams/agent/configuration/service.j2 +12 -0
  8. zscams/agent/keys/autoport.key +27 -0
  9. zscams/agent/src/__init__.py +0 -0
  10. zscams/agent/src/core/__init__.py +1 -0
  11. zscams/agent/src/core/backend/bootstrap.py +76 -0
  12. zscams/agent/src/core/backend/client.py +281 -0
  13. zscams/agent/src/core/backend/exceptions.py +10 -0
  14. zscams/agent/src/core/backend/update_machine_info.py +16 -0
  15. zscams/agent/src/core/prerequisites.py +36 -0
  16. zscams/agent/src/core/service_health_check.py +49 -0
  17. zscams/agent/src/core/services.py +86 -0
  18. zscams/agent/src/core/tunnel/__init__.py +144 -0
  19. zscams/agent/src/core/tunnel/tls.py +56 -0
  20. zscams/agent/src/core/tunnels.py +55 -0
  21. zscams/agent/src/services/__init__.py +0 -0
  22. zscams/agent/src/services/reverse_ssh.py +73 -0
  23. zscams/agent/src/services/ssh_forwarder.py +75 -0
  24. zscams/agent/src/services/system_monitor.py +264 -0
  25. zscams/agent/src/support/__init__.py +0 -0
  26. zscams/agent/src/support/cli.py +50 -0
  27. zscams/agent/src/support/configuration.py +86 -0
  28. zscams/agent/src/support/filesystem.py +63 -0
  29. zscams/agent/src/support/logger.py +88 -0
  30. zscams/agent/src/support/mac.py +18 -0
  31. zscams/agent/src/support/network.py +49 -0
  32. zscams/agent/src/support/openssl.py +114 -0
  33. zscams/agent/src/support/os.py +138 -0
  34. zscams/agent/src/support/ssh.py +24 -0
  35. zscams/agent/src/support/yaml.py +37 -0
  36. zscams/deps.py +86 -0
  37. zscams/lib/.gitkeep +0 -0
  38. zscams-2.0.4.dist-info/METADATA +114 -0
  39. zscams-2.0.4.dist-info/RECORD +41 -0
  40. zscams-2.0.4.dist-info/WHEEL +4 -0
  41. zscams-2.0.4.dist-info/entry_points.txt +3 -0
zscams/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .deps import ensure_native_deps
2
+
3
+ ensure_native_deps()
zscams/__main__.py ADDED
@@ -0,0 +1,43 @@
1
+ """
2
+ Main entry point for ZSCAMs Agent
3
+ """
4
+
5
+ import asyncio
6
+ import sys
7
+ import os
8
+ from zscams.agent.src.core.backend.bootstrap import bootstrap
9
+ from zscams.agent.src.core.backend.update_machine_info import update_machine_info
10
+ from zscams.agent.src.support.logger import get_logger
11
+ from zscams.agent import init_parser, ensure_bootstrapped, run
12
+
13
+ logger = get_logger("ZSCAMs")
14
+
15
+
16
+ def main():
17
+ """Main function to run the asynchronous main."""
18
+ args = init_parser().parse_args()
19
+
20
+ if args.bootstrap:
21
+ try:
22
+ if os.geteuid() != 0:
23
+ logger.error("You are NOT running as root.")
24
+ sys.exit(1)
25
+ bootstrap()
26
+ sys.exit(0)
27
+ except Exception as exception:
28
+ logger.error(exception)
29
+ sys.exit(1)
30
+
31
+ if args.update_machine_info:
32
+ update_machine_info()
33
+ sys.exit(0)
34
+
35
+ try:
36
+ ensure_bootstrapped()
37
+ asyncio.run(run())
38
+ except KeyboardInterrupt:
39
+ logger.info("Exiting TLS Tunnel Client")
40
+
41
+
42
+ if __name__ == "__main__":
43
+ main()
@@ -0,0 +1,93 @@
1
+ import os
2
+ import asyncio
3
+ import sys
4
+ import argparse
5
+ from zscams.agent.src.support.configuration import get_config, CONFIG_PATH
6
+ from zscams.agent.src.support.filesystem import is_file_exists, resolve_path
7
+ from zscams.agent.src.core.tunnel.tls import create_ssl_context
8
+ from zscams.agent.src.core.tunnels import start_all_tunnels
9
+ from zscams.agent.src.core.services import start_all_services
10
+ from zscams.agent.src.core.service_health_check import monitor_services
11
+ from zscams.agent.src.core.backend.client import backend_client
12
+ from zscams.agent.src.support.logger import get_logger
13
+
14
+
15
+ logger = get_logger("tls_tunnel_main")
16
+
17
+
18
+ def init_parser():
19
+ parser = argparse.ArgumentParser(description="ZSCAMs Agent")
20
+ parser.add_argument(
21
+ "--bootstrap",
22
+ action="store_true",
23
+ help="Run bootstrap process and exit",
24
+ )
25
+ parser.add_argument(
26
+ "--update-machine-info",
27
+ action="store_true",
28
+ help="Run bootstrap process and exit",
29
+ )
30
+ return parser
31
+
32
+
33
+ def ensure_bootstrapped():
34
+ """Ensure the agent is bootstrapped with the backend."""
35
+
36
+ config = get_config()
37
+ remote_cfg = config["remote"]
38
+ config_dir = os.path.dirname(CONFIG_PATH)
39
+
40
+ files_to_ensure = [
41
+ resolve_path(remote_cfg.get("ca_cert"), config_dir),
42
+ resolve_path(remote_cfg.get("client_cert"), config_dir),
43
+ resolve_path(remote_cfg.get("client_key"), config_dir),
44
+ ]
45
+
46
+ has_missing_file = False
47
+
48
+ for file in files_to_ensure:
49
+ if not is_file_exists(file, logger):
50
+ has_missing_file = True
51
+ break
52
+
53
+ bootstrapped = backend_client.is_bootstrapped()
54
+
55
+ logger.debug("Agent has entry on server: %s", bootstrapped)
56
+
57
+ if not bootstrapped or has_missing_file:
58
+ logger.error(
59
+ "Agent is not bootstrapped. Please run `zscams --bootstrap` to start the bootstrap process.",
60
+ )
61
+ sys.exit(1)
62
+
63
+
64
+ async def run():
65
+ """Asynchronous main function to start tunnels and services."""
66
+
67
+ config = get_config()
68
+ config_dir = os.path.dirname(CONFIG_PATH)
69
+ ssl_context = create_ssl_context(config["remote"], config_dir=config_dir)
70
+ remote_host = config["remote"]["host"]
71
+ remote_port = config["remote"]["port"]
72
+
73
+ # Start tunnels and wait for readiness
74
+ tunnel_tasks = await start_all_tunnels(
75
+ config.get("forwards", []), remote_host, remote_port, ssl_context
76
+ )
77
+
78
+ # Start services that depend on tunnels
79
+ service_tasks = asyncio.create_task(
80
+ start_all_services(config.get("services", []), config_dir=config_dir)
81
+ )
82
+
83
+ # Start health checks
84
+ monitor_task = asyncio.create_task(
85
+ monitor_services(
86
+ config.get("services", []),
87
+ config_dir=config_dir,
88
+ interval=config.get("general", {}).get("health_check_interval", 300),
89
+ )
90
+ )
91
+
92
+ logger.info("[*] All tunnels and services started. Press Ctrl+C to stop.")
93
+ await asyncio.gather(*tunnel_tasks, service_tasks, monitor_task)
File without changes
@@ -0,0 +1,103 @@
1
+ ---
2
+ backend:
3
+ base_url: http://22.0.122.40:5000/api
4
+ cache_dir: .cache
5
+ bootstrap_info:
6
+ blacklist_ports: []
7
+ cert_issuer: ZSCAMs
8
+ csr:
9
+ country: FR
10
+ default_common_name: zscams-agent.orangecyberdefense.com
11
+ locality: Paris
12
+ organization: zscams-agent.orangecyberdefense.com
13
+ state: Ile-de-France
14
+ forwards:
15
+ - description: Forward to SSH backend
16
+ local_port: 4422
17
+ sni_hostname: ssh-tunnel
18
+ - description: Forward to Seculogs
19
+ local_port: 4423
20
+ sni_hostname: monitor-tunnel
21
+ general:
22
+ health_check_interval: 30
23
+ logging:
24
+ level: 10
25
+ remote:
26
+ ca_cert: certificates/ca_chain.pem
27
+ ca_chain: certificates/ca_chain.pem
28
+ client_cert: certificates/client.crt
29
+ client_key: certificates/client.key
30
+ host: 81.255.240.214
31
+ port: 443
32
+ verify_cert: false
33
+ services:
34
+ - args: []
35
+ custom_port_name: reverse_port
36
+ health:
37
+ host: localhost
38
+ port: 22
39
+ name: Reverse SSH
40
+ params:
41
+ local_port: 4422
42
+ private_key: zscams/agent/keys/autoport.key
43
+ remote_host: localhost
44
+ reverse_port: 10000
45
+ server_ssh_user: ssh_user
46
+ ssh_options:
47
+ - -o LogLevel=error
48
+ - -o UserKnownHostsFile=/dev/null
49
+ - -o StrictHostKeyChecking=no
50
+ - -o ServerAliveInterval=30
51
+ - -o ServerAliveCountMax=2
52
+ port: 22
53
+ prerequisites:
54
+ files:
55
+ - zscams/agent/keys/autoport.key
56
+ ports:
57
+ - 22
58
+ services: []
59
+ script: src/services/reverse_ssh.py
60
+ - args: []
61
+ custom_port_name: forwarder_port
62
+ health:
63
+ host: localhost
64
+ port: 10001
65
+ name: Monitor Forwarder
66
+ params:
67
+ forwarder_port: 10001
68
+ local_port: 4423
69
+ private_key: zscams/agent/keys/autoport.key
70
+ remote_host: 194.51.14.159
71
+ remote_port: 530
72
+ server_ssh_user: ssh_user
73
+ ssh_options:
74
+ - -o LogLevel=error
75
+ - -o UserKnownHostsFile=/dev/null
76
+ - -o StrictHostKeyChecking=no
77
+ - -o ServerAliveInterval=30
78
+ - -o ServerAliveCountMax=2
79
+ port: 530
80
+ prerequisites:
81
+ files:
82
+ - zscams/agent/keys/autoport.key
83
+ ports: []
84
+ services: []
85
+ script: src/services/ssh_forwarder.py
86
+ - args: []
87
+ health:
88
+ host: localhost
89
+ port: 10001
90
+ name: Monitor Service
91
+ params:
92
+ equipment_name: zpa-orange-swec-dev-01
93
+ equipment_type: zpa
94
+ remote_host: localhost
95
+ remote_port: 10001
96
+ service_name: Zscaler-AppConnector
97
+ port: 10001
98
+ prerequisites:
99
+ files: []
100
+ ports:
101
+ - 10001
102
+ services: []
103
+ script: src/services/system_monitor.py
@@ -0,0 +1,103 @@
1
+ ---
2
+ backend:
3
+ base_url: http://22.0.122.40:5000/api
4
+ cache_dir: .cache
5
+ bootstrap_info:
6
+ blacklist_ports: []
7
+ cert_issuer: ZSCAMs
8
+ csr:
9
+ country: FR
10
+ default_common_name: zscams-agent.orangecyberdefense.com
11
+ locality: Paris
12
+ organization: zscams-agent.orangecyberdefense.com
13
+ state: Ile-de-France
14
+ forwards:
15
+ - description: Forward to SSH backend
16
+ local_port: 4422
17
+ sni_hostname: ssh-tunnel
18
+ - description: Forward to Seculogs
19
+ local_port: 4423
20
+ sni_hostname: monitor-tunnel
21
+ general:
22
+ health_check_interval: 30
23
+ logging:
24
+ level: 10
25
+ remote:
26
+ ca_cert: certificates/ca_chain.pem
27
+ ca_chain: certificates/ca_chain.pem
28
+ client_cert: certificates/client.crt
29
+ client_key: certificates/client.key
30
+ host: 81.255.240.214
31
+ port: 443
32
+ verify_cert: false
33
+ services:
34
+ - args: []
35
+ health:
36
+ host: localhost
37
+ port: 22
38
+ name: Reverse SSH
39
+ custom_port_name: "reverse_port"
40
+ params:
41
+ local_port: 4422
42
+ private_key: zscams/agent/keys/autoport.key
43
+ remote_host: localhost
44
+ reverse_port: "{reverse_port}"
45
+ server_ssh_user: ssh_user
46
+ ssh_options:
47
+ - -o LogLevel=error
48
+ - -o UserKnownHostsFile=/dev/null
49
+ - -o StrictHostKeyChecking=no
50
+ - -o ServerAliveInterval=30
51
+ - -o ServerAliveCountMax=2
52
+ port: 22
53
+ prerequisites:
54
+ files:
55
+ - zscams/agent/keys/autoport.key
56
+ ports:
57
+ - 22
58
+ services: []
59
+ script: src/services/reverse_ssh.py
60
+ - args: []
61
+ health:
62
+ host: localhost
63
+ port: "{forwarder_port}"
64
+ name: Monitor Forwarder
65
+ custom_port_name: "forwarder_port"
66
+ params:
67
+ forwarder_port: "{forwarder_port}"
68
+ local_port: 4423
69
+ private_key: zscams/agent/keys/autoport.key
70
+ remote_host: 194.51.14.159
71
+ remote_port: 530
72
+ server_ssh_user: ssh_user
73
+ ssh_options:
74
+ - -o LogLevel=error
75
+ - -o UserKnownHostsFile=/dev/null
76
+ - -o StrictHostKeyChecking=no
77
+ - -o ServerAliveInterval=30
78
+ - -o ServerAliveCountMax=2
79
+ port: 530
80
+ prerequisites:
81
+ files:
82
+ - zscams/agent/keys/autoport.key
83
+ ports: []
84
+ services: []
85
+ script: src/services/ssh_forwarder.py
86
+ - args: []
87
+ health:
88
+ host: localhost
89
+ port: "{forwarder_port}"
90
+ name: Monitor Service
91
+ params:
92
+ equipment_name: "{equipment_name}"
93
+ equipment_type: "{equipment_type}"
94
+ remote_host: localhost
95
+ remote_port: "{forwarder_port}"
96
+ service_name: Zscaler-AppConnector
97
+ port: "{forwarder_port}"
98
+ prerequisites:
99
+ files: []
100
+ ports:
101
+ - "{forwarder_port}"
102
+ services: []
103
+ script: src/services/system_monitor.py
@@ -0,0 +1,12 @@
1
+ [Unit]
2
+ Description=ZSCAMs Service
3
+ After=network.target
4
+
5
+ [Service]
6
+ ExecStart={python_exec} -m zscams
7
+ Restart=always
8
+ User={user_to_run_as}
9
+ Environment="PYTHONPATH={pythonpath}"
10
+
11
+ [Install]
12
+ WantedBy=multi-user.target
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEpQIBAAKCAQEA4kpci9i3k9OhuYpC1+0OSQCpXVRThIjtmwl9Nl4oRSsse9zj
3
+ vkzPShMvlZwvEzzZ/Y3hVDHLD5W3hhy63pyi75uxC9RDzb/7ckc1SdunTzj0mtAt
4
+ FhV+cXFZfv79NfaluN/WZwrbpMO+7+uATuEFIGe/t05PgYX9CzhaagCsC355jFHt
5
+ t2z/9Ul40mJkpMZwr+pk6BOSo/DeW7LNikFljqU8BjoZmGuwGuIHzF6ivhDA8hRz
6
+ aKVfw3e+BuxSyOH3xWxzpHSYiZGU5x9hHk8VEbkP7lISxqYdncCw1Is0O6awfXs0
7
+ azx7yzfUumjDdNSmrwpb5/ard30x/aE96upnRwIDAQABAoIBAQC2ghUcMWD2WDTS
8
+ iSGaNzZTSLZQcKefeDRy23didxRxnP2WtLP2EssIymqRdtM859JFPr4igrpsymqn
9
+ Ptq0mo6LQ/3KNZuuAQ4SwD3JYOAS9DPL/OSwMAu4ARyWYZ/lexV0AwxQNSCrRbjk
10
+ lgL5G2FgHm0wsXdMVr2c5Al//yTDauzgOJtQfi/LzuHusnz05iHzEp0ByBfiRL8z
11
+ xKQ9LFW7pIYUsgn4kXry5tQokH6NwDZ7vGPcjAQLD0lLDfAHF9HOiwpkQYXw8q91
12
+ lNvhaFHxa9jyfvuG9e66Q72YCevsCGBEl8Hvmbpz5/9oBZ6Pt82BZKjSouYh7Cya
13
+ a+JVxQcxAoGBAPSKdJdMXH6hCi5DBYhWnGSpDKvRwvxb2PgntCwajkaEZmuITj4y
14
+ dk9IH89ryl0yAmF6q2pKLyWHOir0fVX1HsnC9m2SErhMuSrhSVpcOLiI09wFwcAM
15
+ PRbS/6g1h/Q+MoaVuD0h9ElO8GkONBIBrmiVYqOifotXuPXjMMGT8oNpAoGBAOzk
16
+ +OSilwjwtwv/MkLOXqGFymSnG7l5zmt/KGxMCThjxY9b1DzyvXcUvojCRjFl4L0W
17
+ OyDbKRiEJZHSH6jV1gj2N8/53b3C1ziCRvHWvix7MJ7DeDfZLp0QEYZBm6Xm7mG+
18
+ TZ27kya0Hprev/t960UfOpwhb8+MNd1dxdgspC8vAoGBALcGpcrTyWqxZ2hGm252
19
+ vKkOacBzyAePSu448T4NRi17TRjwtPcSV8BxD/X0DEsCcgu5f3CXQ4BIHP4nbWOX
20
+ icqi1EQgD0jHi9OPOJKb8YwURNUpreDqiBJ8LAMexbnFj5Vxm6qNrkPsBD3s9oX/
21
+ oiT+ogwtQ59RMcs/lq9b5yf5AoGAbPmYFXVGDXLOgdJPiLPujFdDl7HX6ybBcmn4
22
+ ank/9JTRGPWhWLhBuDnuvHLCX48CJ3nGkYLAEOsZbU9ACSb1YwIBAsdq3hR3dSNZ
23
+ B39F1KiG4UICV46tBsuRhDVCKLtnBcfJZLoZI0DQo2W84zA1voJzL8eh69QQI1kz
24
+ 3hILJTkCgYEAwT+XKnxX8jExNvpe71O2pm3mit6UyOHXB1KyT4Y3UuKjDxwZZ4w1
25
+ vPp6eQ3VcI+4+mpf0qs1cgDz8w35EInt60nKy0+0hwQaM7N0EmsguASHRprESZR9
26
+ dAPDJQ1UJlAMSJxZxFzTvMzDQHY+86GGh5mdoNwJDb0142etE7rSFUI=
27
+ -----END RSA PRIVATE KEY-----
File without changes
@@ -0,0 +1 @@
1
+ running_services = set()
@@ -0,0 +1,76 @@
1
+ import os
2
+ from pathlib import Path
3
+ import sysconfig
4
+ import sys
5
+ from typing import cast
6
+ from zscams.agent.src.core.backend.client import backend_client
7
+ from zscams.agent.src.support.configuration import reinitialize
8
+ from zscams.agent.src.support.logger import get_logger
9
+ from zscams.agent.src.support.os import create_system_user, install_service
10
+ from zscams.agent.src.support.ssh import add_to_authorized_keys
11
+ from zscams.agent.src.support.cli import prompt
12
+
13
+ logger = get_logger("bootstrap")
14
+
15
+
16
+ def bootstrap():
17
+ """Ensure the agent is bootstrapped with the backend."""
18
+
19
+ if backend_client.is_bootstrapped():
20
+ logger.info("Agent ID %s is already bootstrapped.", backend_client.agent_id)
21
+ return
22
+
23
+ username = input("Username: ")
24
+ totp = input("One Time Password (OTP): ")
25
+
26
+ backend_client.login(username, totp)
27
+
28
+ customer_name = prompt(
29
+ "Customer Name",
30
+ "Customer Name",
31
+ required=True,
32
+ startswith="",
33
+ )
34
+ connector_name = prompt(
35
+ "Connector Name",
36
+ "Connector Name [Unique name for this connector]",
37
+ required=True,
38
+ startswith="",
39
+ )
40
+ equipment_type = prompt(
41
+ "Equipment Type",
42
+ "Equipment Type [Can be ZPA|VZEN|PSE]",
43
+ required=True,
44
+ startswith="",
45
+ )
46
+ equipment_name = (
47
+ f"{equipment_type.lower()}-{customer_name.lower()}-{connector_name.lower()}"
48
+ )
49
+ enforced_id = prompt("Enforced ID", "Enforced Agent ID")
50
+ reinitialize(equipment_name=equipment_name, equipment_type=equipment_type)
51
+ cm_info = backend_client.bootstrap(equipment_name, enforced_id or None)
52
+
53
+ sys_user = cast(str, cm_info.get("ssh_user"))
54
+
55
+ create_system_user(sys_user)
56
+ add_to_authorized_keys(sys_user, cm_info.get("server_ssh_pub_key"))
57
+ install_zscams_systemd_service(sys_user)
58
+
59
+
60
+ def install_zscams_systemd_service(user_to_run_as: str):
61
+
62
+ data = {
63
+ "pythonpath": sysconfig.get_paths()["purelib"],
64
+ "python_exec": sys.executable,
65
+ "user_to_run_as": user_to_run_as,
66
+ }
67
+ import zscams
68
+
69
+ BASE_DIR = Path(zscams.__file__).resolve().parent
70
+ template_path = f"{BASE_DIR}/agent/configuration/service.j2"
71
+ with open(template_path) as f:
72
+ template = f.read()
73
+
74
+ rendered_config = template.format(**data)
75
+
76
+ install_service("zscams.service", rendered_config)