machineconfig 8.51__py3-none-any.whl → 8.61__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.
- machineconfig/jobs/installer/python_scripts/sysabc.py +13 -34
- machineconfig/profile/mapper_dotfiles.toml +3 -3
- machineconfig/scripts/python/devops.py +1 -1
- machineconfig/scripts/python/devops_navigator.py +1 -1
- machineconfig/scripts/python/helper_env/path_manager_tui.py +1 -1
- machineconfig/scripts/python/helpers/helper_env/env_manager_tui.py +1 -1
- machineconfig/scripts/python/helpers/helper_env/path_manager_tui.py +1 -1
- machineconfig/scripts/python/helpers/helpers_croshell/croshell_impl.py +8 -4
- machineconfig/scripts/python/helpers/helpers_devops/cli_config.py +33 -1
- machineconfig/scripts/python/helpers/helpers_devops/cli_config_mount.py +77 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_data.py +4 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_nw.py +90 -6
- machineconfig/scripts/python/helpers/helpers_devops/cli_repos.py +3 -3
- machineconfig/scripts/python/helpers/helpers_devops/cli_self.py +41 -15
- machineconfig/scripts/python/helpers/helpers_devops/cli_share_temp.py +69 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_ssh.py +4 -4
- machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/commands.py +25 -0
- machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/device_entry.py +17 -0
- machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/devices.py +17 -0
- machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/linux.py +103 -0
- machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/macos.py +100 -0
- machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/selection.py +47 -0
- machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/utils.py +28 -0
- machineconfig/scripts/python/helpers/helpers_devops/mount_helpers/windows.py +91 -0
- machineconfig/scripts/python/helpers/helpers_msearch/scripts_windows/fzfg.ps1 +1 -6
- machineconfig/scripts/python/helpers/helpers_network/ssh/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_add_key_windows.py +23 -0
- machineconfig/scripts/python/helpers/helpers_network/{ssh_add_ssh_key.py → ssh/ssh_add_ssh_key.py} +21 -27
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_cloud_init.py +33 -0
- machineconfig/scripts/python/helpers/helpers_network/{ssh_debug_linux.py → ssh/ssh_debug_linux.py} +70 -51
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_debug_linux_utils.py +35 -0
- machineconfig/scripts/python/helpers/helpers_network/{ssh_debug_windows.py → ssh/ssh_debug_windows.py} +12 -42
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_debug_windows_utils.py +34 -0
- machineconfig/scripts/python/helpers/helpers_repos/cloud_repo_sync.py +2 -3
- machineconfig/scripts/python/helpers/{helpers_terminal/terminal_impl.py → helpers_sessions/attach_impl.py} +16 -25
- machineconfig/scripts/python/helpers/helpers_sessions/sessions_impl.py +57 -129
- machineconfig/scripts/python/helpers/helpers_sessions/utils.py +69 -0
- machineconfig/scripts/python/mcfg_entry.py +0 -7
- machineconfig/scripts/python/sessions.py +95 -14
- machineconfig/scripts/python/utils.py +3 -2
- machineconfig/settings/shells/bash/init.sh +0 -7
- machineconfig/settings/shells/pwsh/init.ps1 +2 -4
- machineconfig/settings/shells/wezterm/wezterm.lua +1 -0
- machineconfig/settings/shells/wt/settings.json +13 -19
- machineconfig/settings/shells/zsh/init.sh +0 -1
- machineconfig/settings/zellij/__init__.py +0 -0
- machineconfig/settings/zellij/config.kdl +0 -295
- machineconfig/settings/zellij/layouts/__init__.py +0 -0
- machineconfig/settings/zellij/layouts/st.kdl +0 -1
- machineconfig/settings/zellij/layouts/st2.kdl +6 -2
- machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
- machineconfig/utils/installer_utils/installer_cli.py +6 -2
- machineconfig/utils/installer_utils/installer_helper.py +50 -34
- machineconfig/utils/installer_utils/installer_locator_utils.py +3 -13
- machineconfig/utils/options_utils/tv_options.py +1 -1
- machineconfig/utils/procs.py +35 -27
- machineconfig/utils/schemas/layouts/layout_types.py +10 -0
- machineconfig/utils/source_of_truth.py +1 -0
- machineconfig/utils/ssh_utils/abc.py +1 -1
- {machineconfig-8.51.dist-info → machineconfig-8.61.dist-info}/METADATA +2 -3
- {machineconfig-8.51.dist-info → machineconfig-8.61.dist-info}/RECORD +68 -72
- {machineconfig-8.51.dist-info → machineconfig-8.61.dist-info}/entry_points.txt +0 -1
- machineconfig/jobs/scripts/bash_scripts/android.sh +0 -2
- machineconfig/jobs/scripts/bash_scripts/mount_drive +0 -128
- machineconfig/jobs/scripts/bash_scripts/mount_nfs +0 -49
- machineconfig/jobs/scripts/bash_scripts/mount_nw_drive +0 -61
- machineconfig/jobs/scripts/bash_scripts/mount_smb +0 -3
- machineconfig/jobs/scripts/bash_scripts/share_cloud.sh +0 -64
- machineconfig/jobs/scripts/bash_scripts/share_nfs +0 -49
- machineconfig/jobs/scripts/bash_scripts/start_docker +0 -23
- machineconfig/jobs/scripts/powershell_scripts/Restore-ThunderbirdProfile.ps1 +0 -92
- machineconfig/jobs/scripts/powershell_scripts/docker.ps1 +0 -7
- machineconfig/jobs/scripts/powershell_scripts/mount_nfs.ps1 +0 -42
- machineconfig/jobs/scripts/powershell_scripts/mount_nw.ps1 +0 -9
- machineconfig/jobs/scripts/powershell_scripts/mount_smb.ps1 +0 -2
- machineconfig/jobs/scripts/powershell_scripts/mount_ssh.ps1 +0 -13
- machineconfig/jobs/scripts/powershell_scripts/obs.ps1 +0 -4
- machineconfig/jobs/scripts/powershell_scripts/power_options.ps1 +0 -7
- machineconfig/jobs/scripts/powershell_scripts/share_cloud.cmd +0 -34
- machineconfig/jobs/scripts/powershell_scripts/share_smb.ps1 +0 -16
- machineconfig/scripts/python/helpers/helpers_network/mount_nfs.py +0 -85
- machineconfig/scripts/python/helpers/helpers_network/mount_nw_drive.py +0 -48
- machineconfig/scripts/python/helpers/helpers_network/mount_ssh.py +0 -64
- machineconfig/scripts/python/terminal.py +0 -58
- machineconfig/settings/zellij/config.orig.kdl +0 -295
- /machineconfig/{scripts/python/helpers/helpers_terminal → cluster/sessions_managers/wt_utils/examples}/__init__.py +0 -0
- /machineconfig/scripts/python/helpers/helpers_network/{ssh_add_identity.py → ssh/ssh_add_identity.py} +0 -0
- {machineconfig-8.51.dist-info → machineconfig-8.61.dist-info}/WHEEL +0 -0
- {machineconfig-8.51.dist-info → machineconfig-8.61.dist-info}/top_level.txt +0 -0
machineconfig/scripts/python/helpers/helpers_network/{ssh_debug_linux.py → ssh/ssh_debug_linux.py}
RENAMED
|
@@ -6,45 +6,13 @@ from rich.console import Console
|
|
|
6
6
|
from rich.panel import Panel
|
|
7
7
|
from rich.table import Table
|
|
8
8
|
from rich import box
|
|
9
|
-
import subprocess
|
|
10
9
|
import os
|
|
11
10
|
import re
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
console = Console()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def _run(cmd: list[str]) -> tuple[bool, str]:
|
|
18
|
-
try:
|
|
19
|
-
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
20
|
-
return result.returncode == 0, result.stdout.strip()
|
|
21
|
-
except FileNotFoundError:
|
|
22
|
-
return False, ""
|
|
12
|
+
from machineconfig.scripts.python.helpers.helpers_network.ssh.ssh_debug_linux_utils import check_sshd_installed, detect_package_manager, run_cmd
|
|
23
13
|
|
|
24
14
|
|
|
25
|
-
|
|
26
|
-
sshd_paths = ["/usr/sbin/sshd", "/usr/bin/sshd", "/sbin/sshd"]
|
|
27
|
-
for path in sshd_paths:
|
|
28
|
-
if Path(path).exists():
|
|
29
|
-
return True, path
|
|
30
|
-
ok, which_out = _run(["which", "sshd"])
|
|
31
|
-
if ok and which_out:
|
|
32
|
-
return True, which_out
|
|
33
|
-
return False, ""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def _detect_package_manager() -> tuple[str, str]:
|
|
37
|
-
if Path("/usr/bin/apt").exists() or Path("/usr/bin/apt-get").exists():
|
|
38
|
-
return "apt", "sudo apt update && sudo apt install -y openssh-server"
|
|
39
|
-
if Path("/usr/bin/dnf").exists():
|
|
40
|
-
return "dnf", "sudo dnf install -y openssh-server"
|
|
41
|
-
if Path("/usr/bin/yum").exists():
|
|
42
|
-
return "yum", "sudo yum install -y openssh-server"
|
|
43
|
-
if Path("/usr/bin/pacman").exists():
|
|
44
|
-
return "pacman", "sudo pacman -S --noconfirm openssh"
|
|
45
|
-
if Path("/usr/bin/zypper").exists():
|
|
46
|
-
return "zypper", "sudo zypper install -y openssh"
|
|
47
|
-
return "unknown", "# Install openssh-server using your package manager"
|
|
15
|
+
console = Console()
|
|
48
16
|
|
|
49
17
|
|
|
50
18
|
def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
@@ -57,12 +25,12 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
57
25
|
ssh_port = "22"
|
|
58
26
|
ip_addresses: list[str] = []
|
|
59
27
|
|
|
60
|
-
ok, hostname =
|
|
28
|
+
ok, hostname = run_cmd(["hostname"])
|
|
61
29
|
hostname = hostname if ok else "unknown"
|
|
62
30
|
|
|
63
31
|
install_info: list[str] = []
|
|
64
|
-
sshd_installed, sshd_path =
|
|
65
|
-
_pkg_manager, install_cmd =
|
|
32
|
+
sshd_installed, sshd_path = check_sshd_installed()
|
|
33
|
+
_pkg_manager, install_cmd = detect_package_manager()
|
|
66
34
|
if not sshd_installed:
|
|
67
35
|
results["installation"] = {"status": "error", "message": "OpenSSH Server not installed"}
|
|
68
36
|
issues.append(("sshd not installed", "Cannot accept incoming SSH connections", install_cmd))
|
|
@@ -124,8 +92,8 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
124
92
|
console.print(Panel("\n".join(perm_info), title="[bold]Permissions[/bold]", border_style="blue"))
|
|
125
93
|
|
|
126
94
|
svc_info: list[str] = []
|
|
127
|
-
ssh_ok, _ =
|
|
128
|
-
sshd_ok, _ =
|
|
95
|
+
ssh_ok, _ = run_cmd(["systemctl", "is-active", "ssh"])
|
|
96
|
+
sshd_ok, _ = run_cmd(["systemctl", "is-active", "sshd"])
|
|
129
97
|
if ssh_ok or sshd_ok:
|
|
130
98
|
svc_name = "ssh" if ssh_ok else "sshd"
|
|
131
99
|
results["ssh_service"] = {"status": "ok", "message": f"{svc_name} running"}
|
|
@@ -138,7 +106,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
138
106
|
console.print(Panel("\n".join(svc_info), title="[bold]Service[/bold]", border_style="blue"))
|
|
139
107
|
|
|
140
108
|
net_info: list[str] = []
|
|
141
|
-
ok, ip_out =
|
|
109
|
+
ok, ip_out = run_cmd(["ip", "addr", "show"])
|
|
142
110
|
if ok:
|
|
143
111
|
ip_addresses = re.findall(r'inet\s+(\d+\.\d+\.\d+\.\d+)/\d+.*scope\s+global', ip_out)
|
|
144
112
|
if ip_addresses:
|
|
@@ -151,6 +119,34 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
151
119
|
sshd_config = p
|
|
152
120
|
break
|
|
153
121
|
|
|
122
|
+
sshd_config_d = Path("/etc/ssh/sshd_config.d")
|
|
123
|
+
cloud_init_overrides: dict[str, Path] = {}
|
|
124
|
+
cloud_init_files: list[Path] = []
|
|
125
|
+
if sshd_config_d.exists():
|
|
126
|
+
for conf_file in sorted(sshd_config_d.glob("*.conf")):
|
|
127
|
+
cloud_init_files.append(conf_file)
|
|
128
|
+
try:
|
|
129
|
+
conf_text = conf_file.read_text(encoding="utf-8")
|
|
130
|
+
for line in conf_text.split("\n"):
|
|
131
|
+
line_stripped = line.strip()
|
|
132
|
+
if line_stripped and not line_stripped.startswith("#"):
|
|
133
|
+
key = line_stripped.split()[0] if line_stripped.split() else ""
|
|
134
|
+
if key:
|
|
135
|
+
cloud_init_overrides[key] = conf_file
|
|
136
|
+
except Exception:
|
|
137
|
+
pass
|
|
138
|
+
|
|
139
|
+
if cloud_init_files:
|
|
140
|
+
cloud_info: list[str] = []
|
|
141
|
+
cloud_info.append(f"⚠️ Found [yellow]{len(cloud_init_files)}[/yellow] override file(s) in /etc/ssh/sshd_config.d/")
|
|
142
|
+
cloud_info.append(" [dim]These files can override settings in the main sshd_config![/dim]")
|
|
143
|
+
for cf in cloud_init_files:
|
|
144
|
+
cloud_info.append(f" \u2022 [cyan]{cf.name}[/cyan]")
|
|
145
|
+
if cloud_init_overrides:
|
|
146
|
+
overridden_keys = list(cloud_init_overrides.keys())[:5]
|
|
147
|
+
cloud_info.append(f" Overriding: {', '.join(overridden_keys)}{'...' if len(cloud_init_overrides) > 5 else ''}")
|
|
148
|
+
console.print(Panel("\n".join(cloud_info), title="[bold yellow]Cloud-Init SSH Overrides[/bold yellow]", border_style="yellow"))
|
|
149
|
+
|
|
154
150
|
if sshd_config:
|
|
155
151
|
try:
|
|
156
152
|
config_text = sshd_config.read_text(encoding="utf-8")
|
|
@@ -160,22 +156,45 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
160
156
|
net_info.append(f"🔌 Port: [cyan]{ssh_port}[/cyan]")
|
|
161
157
|
|
|
162
158
|
pubkey_lines = [line for line in config_text.split("\n") if "PubkeyAuthentication" in line and not line.strip().startswith("#")]
|
|
159
|
+
pubkey_override_file: Path | None = cloud_init_overrides.get("PubkeyAuthentication")
|
|
160
|
+
if pubkey_override_file:
|
|
161
|
+
try:
|
|
162
|
+
override_text = pubkey_override_file.read_text(encoding="utf-8")
|
|
163
|
+
override_pubkey_lines = [line for line in override_text.split("\n") if "PubkeyAuthentication" in line and not line.strip().startswith("#")]
|
|
164
|
+
if override_pubkey_lines:
|
|
165
|
+
pubkey_lines = override_pubkey_lines
|
|
166
|
+
except Exception:
|
|
167
|
+
pass
|
|
163
168
|
if pubkey_lines and "no" in pubkey_lines[-1].lower():
|
|
164
169
|
results["pubkey_auth"] = {"status": "error", "message": "PubkeyAuthentication disabled"}
|
|
165
|
-
|
|
166
|
-
|
|
170
|
+
fix_target = pubkey_override_file if pubkey_override_file else sshd_config
|
|
171
|
+
issues.append(("PubkeyAuthentication disabled", "Key-based login won't work", f"Edit {fix_target}: set PubkeyAuthentication yes, then sudo systemctl restart ssh"))
|
|
172
|
+
override_note = f" (overridden in {pubkey_override_file.name})" if pubkey_override_file else ""
|
|
173
|
+
net_info.append(f"❌ PubkeyAuthentication: [red]disabled[/red]{override_note}")
|
|
167
174
|
else:
|
|
168
175
|
net_info.append("✅ PubkeyAuthentication: enabled")
|
|
169
176
|
|
|
170
177
|
password_lines = [line for line in config_text.split("\n") if "PasswordAuthentication" in line and not line.strip().startswith("#")]
|
|
178
|
+
password_override_file: Path | None = cloud_init_overrides.get("PasswordAuthentication")
|
|
179
|
+
if password_override_file:
|
|
180
|
+
try:
|
|
181
|
+
override_text = password_override_file.read_text(encoding="utf-8")
|
|
182
|
+
override_password_lines = [line for line in override_text.split("\n") if "PasswordAuthentication" in line and not line.strip().startswith("#")]
|
|
183
|
+
if override_password_lines:
|
|
184
|
+
password_lines = override_password_lines
|
|
185
|
+
except Exception:
|
|
186
|
+
pass
|
|
171
187
|
if password_lines:
|
|
172
188
|
password_enabled = "yes" in password_lines[-1].lower()
|
|
189
|
+
override_note = f" (from {password_override_file.name})" if password_override_file else ""
|
|
173
190
|
if password_enabled:
|
|
174
191
|
results["password_auth"] = {"status": "ok", "message": "PasswordAuthentication enabled"}
|
|
175
|
-
net_info.append("✅ PasswordAuthentication: [green]enabled[/green]")
|
|
192
|
+
net_info.append(f"✅ PasswordAuthentication: [green]enabled[/green]{override_note}")
|
|
176
193
|
else:
|
|
177
194
|
results["password_auth"] = {"status": "info", "message": "PasswordAuthentication disabled"}
|
|
178
|
-
net_info.append("ℹ️ PasswordAuthentication: [yellow]disabled[/yellow] (key-only)")
|
|
195
|
+
net_info.append(f"ℹ️ PasswordAuthentication: [yellow]disabled[/yellow] (key-only){override_note}")
|
|
196
|
+
if password_override_file:
|
|
197
|
+
issues.append((f"PasswordAuth disabled by {password_override_file.name}", "Password login blocked by cloud-init config", f"Edit {password_override_file}: set PasswordAuthentication yes, then sudo systemctl restart ssh"))
|
|
179
198
|
else:
|
|
180
199
|
results["password_auth"] = {"status": "ok", "message": "PasswordAuthentication enabled (default)"}
|
|
181
200
|
net_info.append("✅ PasswordAuthentication: [green]enabled[/green] (default)")
|
|
@@ -187,7 +206,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
187
206
|
except Exception:
|
|
188
207
|
pass
|
|
189
208
|
|
|
190
|
-
ok, ss_out =
|
|
209
|
+
ok, ss_out = run_cmd(["ss", "-tlnp"])
|
|
191
210
|
if ok:
|
|
192
211
|
listening = [line for line in ss_out.split("\n") if f":{ssh_port}" in line]
|
|
193
212
|
if not listening:
|
|
@@ -203,7 +222,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
203
222
|
net_info.append(f"✅ Listening: 0.0.0.0:{ssh_port}")
|
|
204
223
|
|
|
205
224
|
fw_checked = False
|
|
206
|
-
ok, ufw_out =
|
|
225
|
+
ok, ufw_out = run_cmd(["ufw", "status"])
|
|
207
226
|
if ok and "Status: active" in ufw_out:
|
|
208
227
|
fw_checked = True
|
|
209
228
|
if f"{ssh_port}/tcp" in ufw_out.lower() or "ssh" in ufw_out.lower() or f" {ssh_port} " in ufw_out:
|
|
@@ -216,10 +235,10 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
216
235
|
net_info.append(" [dim]Active firewall without SSH rule = blocked[/dim]")
|
|
217
236
|
|
|
218
237
|
if not fw_checked:
|
|
219
|
-
ok, fwd_out =
|
|
238
|
+
ok, fwd_out = run_cmd(["firewall-cmd", "--state"])
|
|
220
239
|
if ok and "running" in fwd_out.lower():
|
|
221
240
|
fw_checked = True
|
|
222
|
-
ok2, svc_out =
|
|
241
|
+
ok2, svc_out = run_cmd(["firewall-cmd", "--list-services"])
|
|
223
242
|
if ok2 and "ssh" in svc_out.lower():
|
|
224
243
|
results["firewall"] = {"status": "ok", "message": "firewalld allows SSH"}
|
|
225
244
|
net_info.append("✅ Firewall (firewalld): allows SSH")
|
|
@@ -229,7 +248,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
229
248
|
net_info.append("❌ Firewall (firewalld): [red]blocking SSH[/red]")
|
|
230
249
|
|
|
231
250
|
if not fw_checked:
|
|
232
|
-
ok, ipt_out =
|
|
251
|
+
ok, ipt_out = run_cmd(["iptables", "-L", "INPUT", "-n"])
|
|
233
252
|
if ok and ipt_out:
|
|
234
253
|
has_drop_policy = "policy DROP" in ipt_out or "policy REJECT" in ipt_out
|
|
235
254
|
has_ssh_allow = f"dpt:{ssh_port}" in ipt_out or "dpt:ssh" in ipt_out
|
|
@@ -264,7 +283,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
264
283
|
except Exception:
|
|
265
284
|
pass
|
|
266
285
|
|
|
267
|
-
ok, se_out =
|
|
286
|
+
ok, se_out = run_cmd(["getenforce"])
|
|
268
287
|
if ok and se_out:
|
|
269
288
|
if se_out == "Enforcing":
|
|
270
289
|
other_info.append("ℹ️ SELinux: Enforcing (run [cyan]restorecon -Rv ~/.ssh[/cyan] if issues)")
|
|
@@ -274,7 +293,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
274
293
|
log_files = [Path("/var/log/auth.log"), Path("/var/log/secure")]
|
|
275
294
|
for lf in log_files:
|
|
276
295
|
if lf.exists():
|
|
277
|
-
ok, tail =
|
|
296
|
+
ok, tail = run_cmd(["tail", "-n", "20", str(lf)])
|
|
278
297
|
if ok:
|
|
279
298
|
errors = [line for line in tail.split("\n") if any(k in line.lower() for k in ["error", "failed", "refused", "denied"]) and "ssh" in line.lower()]
|
|
280
299
|
if errors:
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def run_cmd(cmd: list[str]) -> tuple[bool, str]:
|
|
6
|
+
try:
|
|
7
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
8
|
+
return result.returncode == 0, result.stdout.strip()
|
|
9
|
+
except FileNotFoundError:
|
|
10
|
+
return False, ""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def check_sshd_installed() -> tuple[bool, str]:
|
|
14
|
+
sshd_paths = ["/usr/sbin/sshd", "/usr/bin/sshd", "/sbin/sshd"]
|
|
15
|
+
for path in sshd_paths:
|
|
16
|
+
if Path(path).exists():
|
|
17
|
+
return True, path
|
|
18
|
+
ok, which_out = run_cmd(["which", "sshd"])
|
|
19
|
+
if ok and which_out:
|
|
20
|
+
return True, which_out
|
|
21
|
+
return False, ""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def detect_package_manager() -> tuple[str, str]:
|
|
25
|
+
if Path("/usr/bin/apt").exists() or Path("/usr/bin/apt-get").exists():
|
|
26
|
+
return "apt", "sudo apt update && sudo apt install -y openssh-server"
|
|
27
|
+
if Path("/usr/bin/dnf").exists():
|
|
28
|
+
return "dnf", "sudo dnf install -y openssh-server"
|
|
29
|
+
if Path("/usr/bin/yum").exists():
|
|
30
|
+
return "yum", "sudo yum install -y openssh-server"
|
|
31
|
+
if Path("/usr/bin/pacman").exists():
|
|
32
|
+
return "pacman", "sudo pacman -S --noconfirm openssh"
|
|
33
|
+
if Path("/usr/bin/zypper").exists():
|
|
34
|
+
return "zypper", "sudo zypper install -y openssh"
|
|
35
|
+
return "unknown", "# Install openssh-server using your package manager"
|
|
@@ -9,40 +9,10 @@ from rich import box
|
|
|
9
9
|
import subprocess
|
|
10
10
|
import os
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
console = Console()
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def _run_ps(cmd: str) -> tuple[bool, str]:
|
|
17
|
-
result = subprocess.run(["powershell", "-Command", cmd], capture_output=True, text=True, check=False)
|
|
18
|
-
return result.returncode == 0, result.stdout.strip()
|
|
12
|
+
from machineconfig.scripts.python.helpers.helpers_network.ssh.ssh_debug_windows_utils import check_sshd_binary_exists, detect_openssh, run_powershell
|
|
19
13
|
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
sshd_locations = [
|
|
23
|
-
Path("C:/Windows/System32/OpenSSH/sshd.exe"),
|
|
24
|
-
Path("C:/Program Files/OpenSSH/sshd.exe"),
|
|
25
|
-
Path("C:/Program Files (x86)/OpenSSH/sshd.exe"),
|
|
26
|
-
]
|
|
27
|
-
for loc in sshd_locations:
|
|
28
|
-
if loc.exists():
|
|
29
|
-
return True, str(loc)
|
|
30
|
-
ok, which_out = _run_ps("Get-Command sshd -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source")
|
|
31
|
-
if ok and which_out:
|
|
32
|
-
return True, which_out
|
|
33
|
-
return False, ""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def _detect_openssh() -> tuple[str, Path | None, Path | None]:
|
|
37
|
-
capability_sshd = Path("C:/Windows/System32/OpenSSH/sshd.exe")
|
|
38
|
-
winget_sshd = Path("C:/Program Files/OpenSSH/sshd.exe")
|
|
39
|
-
programdata_config = Path("C:/ProgramData/ssh")
|
|
40
|
-
capability_config = Path("C:/ProgramData/ssh")
|
|
41
|
-
if capability_sshd.exists():
|
|
42
|
-
return ("capability", capability_sshd, capability_config)
|
|
43
|
-
if winget_sshd.exists():
|
|
44
|
-
return ("winget", winget_sshd, programdata_config)
|
|
45
|
-
return ("not_found", None, None)
|
|
15
|
+
console = Console()
|
|
46
16
|
|
|
47
17
|
|
|
48
18
|
def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
@@ -55,9 +25,9 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
|
55
25
|
ssh_port = "22"
|
|
56
26
|
ip_addresses: list[str] = []
|
|
57
27
|
|
|
58
|
-
sshd_exists, sshd_path =
|
|
59
|
-
install_type, _sshd_exe, config_dir =
|
|
60
|
-
ok, hostname =
|
|
28
|
+
sshd_exists, sshd_path = check_sshd_binary_exists()
|
|
29
|
+
install_type, _sshd_exe, config_dir = detect_openssh()
|
|
30
|
+
ok, hostname = run_powershell("hostname")
|
|
61
31
|
hostname = hostname if ok else "unknown"
|
|
62
32
|
|
|
63
33
|
install_info: list[str] = []
|
|
@@ -76,7 +46,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
|
76
46
|
install_info.append(f" Binary: {sshd_path}")
|
|
77
47
|
install_info.append(f" Config: {config_dir}")
|
|
78
48
|
|
|
79
|
-
ok, status =
|
|
49
|
+
ok, status = run_powershell("Get-Service -Name sshd -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Status")
|
|
80
50
|
if not ok or not status:
|
|
81
51
|
results["ssh_service"] = {"status": "error", "message": "sshd service not found"}
|
|
82
52
|
issues.append(("sshd service missing", "SSH daemon not installed", "Install OpenSSH Server first"))
|
|
@@ -87,7 +57,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
|
87
57
|
install_info.append(f"❌ sshd service: [yellow]{status}[/yellow]")
|
|
88
58
|
else:
|
|
89
59
|
results["ssh_service"] = {"status": "ok", "message": "sshd running"}
|
|
90
|
-
ok, startup =
|
|
60
|
+
ok, startup = run_powershell("Get-Service -Name sshd | Select-Object -ExpandProperty StartType")
|
|
91
61
|
startup_note = f" (startup: {startup})" if ok else ""
|
|
92
62
|
install_info.append(f"✅ sshd service: [green]Running[/green]{startup_note}")
|
|
93
63
|
|
|
@@ -98,7 +68,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
|
98
68
|
admin_auth_keys = Path("C:/ProgramData/ssh/administrators_authorized_keys")
|
|
99
69
|
perm_info: list[str] = []
|
|
100
70
|
|
|
101
|
-
ok, is_admin_str =
|
|
71
|
+
ok, is_admin_str = run_powershell("([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)")
|
|
102
72
|
is_admin = "True" in is_admin_str
|
|
103
73
|
|
|
104
74
|
if is_admin:
|
|
@@ -124,7 +94,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
|
124
94
|
perm_info.append(f"\n⚠️ Could not read {target_auth_keys.name}: {e}")
|
|
125
95
|
|
|
126
96
|
if is_admin and admin_auth_keys.exists():
|
|
127
|
-
ok, icacls_out =
|
|
97
|
+
ok, icacls_out = run_powershell(f'icacls "{admin_auth_keys}"')
|
|
128
98
|
if ok:
|
|
129
99
|
needs_fix = "BUILTIN\\Users" in icacls_out or "Everyone" in icacls_out
|
|
130
100
|
if needs_fix:
|
|
@@ -137,7 +107,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
|
137
107
|
console.print(Panel("\n".join(perm_info), title="[bold]Keys & Permissions[/bold]", border_style="blue"))
|
|
138
108
|
|
|
139
109
|
net_info: list[str] = []
|
|
140
|
-
ok, ip_out =
|
|
110
|
+
ok, ip_out = run_powershell("Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Dhcp,Manual | Where-Object {$_.IPAddress -notlike '127.*' -and $_.IPAddress -notlike '169.254.*'} | Select-Object -ExpandProperty IPAddress")
|
|
141
111
|
if ok and ip_out:
|
|
142
112
|
ip_addresses = [ip.strip() for ip in ip_out.split("\n") if ip.strip()]
|
|
143
113
|
net_info.append(f"🌐 IP addresses: [cyan]{', '.join(ip_addresses)}[/cyan]")
|
|
@@ -148,7 +118,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
|
148
118
|
try:
|
|
149
119
|
config_text = sshd_config.read_text(encoding="utf-8")
|
|
150
120
|
except PermissionError:
|
|
151
|
-
ok, config_text_ps =
|
|
121
|
+
ok, config_text_ps = run_powershell(f'Get-Content "{sshd_config}" -Raw')
|
|
152
122
|
config_text = config_text_ps if ok and config_text_ps else None
|
|
153
123
|
except Exception:
|
|
154
124
|
config_text = None
|
|
@@ -219,7 +189,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
|
219
189
|
$rules | Select-Object Name, DisplayName, Enabled, Action | Format-List
|
|
220
190
|
}}
|
|
221
191
|
"""
|
|
222
|
-
ok, fw_out =
|
|
192
|
+
ok, fw_out = run_powershell(fw_cmd)
|
|
223
193
|
if ok and fw_out.strip():
|
|
224
194
|
has_allow = "Enabled : True" in fw_out and "Action : Allow" in fw_out
|
|
225
195
|
if has_allow:
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def run_powershell(cmd: str) -> tuple[bool, str]:
|
|
6
|
+
result = subprocess.run(["powershell", "-Command", cmd], capture_output=True, text=True, check=False)
|
|
7
|
+
return result.returncode == 0, result.stdout.strip()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def check_sshd_binary_exists() -> tuple[bool, str]:
|
|
11
|
+
sshd_locations = [
|
|
12
|
+
Path("C:/Windows/System32/OpenSSH/sshd.exe"),
|
|
13
|
+
Path("C:/Program Files/OpenSSH/sshd.exe"),
|
|
14
|
+
Path("C:/Program Files (x86)/OpenSSH/sshd.exe"),
|
|
15
|
+
]
|
|
16
|
+
for loc in sshd_locations:
|
|
17
|
+
if loc.exists():
|
|
18
|
+
return True, str(loc)
|
|
19
|
+
ok, which_out = run_powershell("Get-Command sshd -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source")
|
|
20
|
+
if ok and which_out:
|
|
21
|
+
return True, which_out
|
|
22
|
+
return False, ""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def detect_openssh() -> tuple[str, Path | None, Path | None]:
|
|
26
|
+
capability_sshd = Path("C:/Windows/System32/OpenSSH/sshd.exe")
|
|
27
|
+
winget_sshd = Path("C:/Program Files/OpenSSH/sshd.exe")
|
|
28
|
+
programdata_config = Path("C:/ProgramData/ssh")
|
|
29
|
+
capability_config = Path("C:/ProgramData/ssh")
|
|
30
|
+
if capability_sshd.exists():
|
|
31
|
+
return ("capability", capability_sshd, capability_config)
|
|
32
|
+
if winget_sshd.exists():
|
|
33
|
+
return ("winget", winget_sshd, programdata_config)
|
|
34
|
+
return ("not_found", None, None)
|
|
@@ -20,8 +20,8 @@ def get_tmp_file():
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def main(
|
|
23
|
+
repo: Annotated[str, typer.Argument(..., help="Path to the local repository. Defaults to current working directory.")],
|
|
23
24
|
cloud: Annotated[Optional[str], typer.Option(..., "--cloud", "-c", help="Cloud storage profile name. If not provided, uses default from config.")] = None,
|
|
24
|
-
repo: Annotated[Optional[str], typer.Option(..., "--repo", "-r", help="Path to the local repository. Defaults to current working directory.")] = None,
|
|
25
25
|
message: Annotated[Optional[str], typer.Option(..., "--message", "-m", help="Commit message for local changes.")] = None,
|
|
26
26
|
on_conflict: Annotated[Literal["ask", "a",
|
|
27
27
|
"push-local-merge", "p",
|
|
@@ -66,7 +66,6 @@ def main(
|
|
|
66
66
|
if cloud is None:
|
|
67
67
|
try:
|
|
68
68
|
from machineconfig.utils.io import read_ini
|
|
69
|
-
|
|
70
69
|
cloud_resolved = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
|
|
71
70
|
console.print(Panel(f"⚠️ Using default cloud: `{cloud_resolved}` from {DEFAULTS_PATH}", title="Default Cloud", border_style="yellow"))
|
|
72
71
|
except FileNotFoundError:
|
|
@@ -74,7 +73,7 @@ def main(
|
|
|
74
73
|
return ""
|
|
75
74
|
else:
|
|
76
75
|
cloud_resolved = cloud
|
|
77
|
-
repo_local_root = PathExtended.cwd() if repo
|
|
76
|
+
repo_local_root = PathExtended.cwd() if repo == "." else PathExtended(repo).expanduser().absolute()
|
|
78
77
|
try:
|
|
79
78
|
repo_local_obj = git.Repo(repo_local_root, search_parent_directories=True)
|
|
80
79
|
except git.InvalidGitRepositoryError:
|
|
@@ -1,23 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
import subprocess
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
from machineconfig.settings.zellij import layouts
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
root = layouts.__path__[0]
|
|
7
|
+
STANDARD = Path(root).joinpath("st2.kdl")
|
|
8
|
+
# STANDARD = "st2.kdl"
|
|
9
|
+
# a = 1 + "sd"
|
|
10
10
|
|
|
11
11
|
def strip_ansi_codes(text: str) -> str:
|
|
12
|
+
import re
|
|
13
|
+
_ANSI_ESCAPE_RE = re.compile(
|
|
14
|
+
r"(?:\x1B|\u001B|\033)\[[0-?]*[ -/]*[@-~]|\[[0-9;?]+[ -/]*[@-~]|\[m"
|
|
15
|
+
)
|
|
12
16
|
return _ANSI_ESCAPE_RE.sub("", text)
|
|
13
17
|
|
|
14
18
|
|
|
15
19
|
def choose_zellij_session(name: str | None, new_session: bool, kill_all: bool) -> tuple[str, str | None]:
|
|
16
20
|
"""Choose a Zellij session. Returns tuple of (action, script_to_run) where action is 'run_script', 'exit', or 'error'."""
|
|
21
|
+
# from machineoncif
|
|
22
|
+
|
|
17
23
|
if name is not None:
|
|
18
24
|
return ("run_script", f"zellij attach {name}")
|
|
19
25
|
if new_session:
|
|
20
|
-
cmd = "zellij --layout
|
|
26
|
+
cmd = f"zellij --layout {STANDARD}"
|
|
21
27
|
if kill_all:
|
|
22
28
|
cmd = f"zellij kill-all-sessions --yes\n{cmd}"
|
|
23
29
|
return ("run_script", cmd)
|
|
@@ -32,7 +38,7 @@ def choose_zellij_session(name: str | None, new_session: bool, kill_all: bool) -
|
|
|
32
38
|
if "current" in sessions:
|
|
33
39
|
return ("error", "Already in a Zellij session, avoiding nesting and exiting.")
|
|
34
40
|
if len(sessions) == 0:
|
|
35
|
-
return ("run_script", "zellij --layout
|
|
41
|
+
return ("run_script", f"zellij --layout {STANDARD}")
|
|
36
42
|
if len(sessions) == 1:
|
|
37
43
|
sn = strip_ansi_codes(sessions[0])
|
|
38
44
|
session_name = sn.split(" [Created")[0]
|
|
@@ -46,12 +52,12 @@ def choose_zellij_session(name: str | None, new_session: bool, kill_all: bool) -
|
|
|
46
52
|
except Exception as e:
|
|
47
53
|
return ("error", f"Error choosing Zellij session: {e}")
|
|
48
54
|
if session_name == NEW_SESSION_LABEL:
|
|
49
|
-
cmd = "zellij --layout
|
|
55
|
+
cmd = f"zellij --layout {STANDARD}"
|
|
50
56
|
if kill_all:
|
|
51
57
|
cmd = f"zellij kill-all-sessions --yes\n{cmd}"
|
|
52
58
|
return ("run_script", cmd)
|
|
53
59
|
if session_name == KILL_ALL_AND_NEW_LABEL:
|
|
54
|
-
return ("run_script", "zellij kill-all-sessions --yes\nzellij --layout
|
|
60
|
+
return ("run_script", f"zellij kill-all-sessions --yes\nzellij --layout {STANDARD}")
|
|
55
61
|
session_name_clean = strip_ansi_codes(session_name)
|
|
56
62
|
session_name_clean = session_name_clean.split(" [Created")[0]
|
|
57
63
|
return ("run_script", f"zellij attach {session_name_clean}")
|
|
@@ -79,18 +85,3 @@ def get_session_tabs() -> list[tuple[str, str]]:
|
|
|
79
85
|
return result
|
|
80
86
|
|
|
81
87
|
|
|
82
|
-
def start_wt(layout_name: str) -> tuple[str, str | None]:
|
|
83
|
-
"""Start a Windows Terminal layout by name. Returns tuple of (status, message) where status is 'success' or 'error'."""
|
|
84
|
-
import json
|
|
85
|
-
from machineconfig.utils.schemas.layouts.layout_types import LayoutsFile
|
|
86
|
-
from machineconfig.cluster.sessions_managers.wt_local import run_wt_layout
|
|
87
|
-
layouts_file = Path.home().joinpath("dotfiles/machineconfig/layouts.json")
|
|
88
|
-
if not layouts_file.exists():
|
|
89
|
-
return ("error", f"❌ Layouts file not found: {layouts_file}")
|
|
90
|
-
layouts_data: LayoutsFile = json.loads(layouts_file.read_text(encoding="utf-8"))
|
|
91
|
-
chosen_layout = next((a_layout for a_layout in layouts_data["layouts"] if a_layout["layoutName"] == layout_name), None)
|
|
92
|
-
if not chosen_layout:
|
|
93
|
-
available_layouts = [a_layout["layoutName"] for a_layout in layouts_data["layouts"]]
|
|
94
|
-
return ("error", f"❌ Layout '{layout_name}' not found in layouts file.\nAvailable layouts: {', '.join(available_layouts)}")
|
|
95
|
-
run_wt_layout(layout_config=chosen_layout)
|
|
96
|
-
return ("success", None)
|