machineconfig 7.98__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/cluster/remote/run_cluster.py +1 -1
- machineconfig/cluster/remote/run_remote.py +1 -1
- machineconfig/cluster/sessions_managers/utils/maker.py +10 -8
- machineconfig/cluster/sessions_managers/wt_local.py +1 -1
- machineconfig/cluster/sessions_managers/wt_local_manager.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_local.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +1 -1
- machineconfig/jobs/installer/checks/check_installations.py +133 -0
- machineconfig/jobs/installer/checks/install_utils.py +132 -0
- machineconfig/jobs/installer/checks/report_utils.py +39 -0
- machineconfig/jobs/installer/checks/vt_utils.py +89 -0
- machineconfig/jobs/installer/installer_data.json +271 -152
- machineconfig/jobs/installer/linux_scripts/docker.sh +6 -9
- machineconfig/jobs/installer/package_groups.py +11 -9
- machineconfig/jobs/installer/{custom → python_scripts}/boxes.py +1 -2
- machineconfig/jobs/installer/{custom_dev → python_scripts}/brave.py +1 -1
- machineconfig/jobs/installer/{custom_dev → python_scripts}/code.py +10 -8
- machineconfig/jobs/installer/{custom → python_scripts}/hx.py +30 -13
- machineconfig/jobs/installer/{custom_dev → python_scripts}/nerdfont.py +1 -1
- machineconfig/jobs/installer/{custom_dev → python_scripts}/nerfont_windows_helper.py +6 -5
- machineconfig/jobs/installer/{custom_dev → python_scripts}/sysabc.py +28 -43
- machineconfig/jobs/installer/{custom_dev → python_scripts}/wezterm.py +1 -1
- machineconfig/jobs/installer/{custom → python_scripts}/yazi.py +39 -19
- machineconfig/jobs/scripts/powershell_scripts/cmatrix.ps1 +52 -0
- machineconfig/jobs/scripts_dynamic/a.py +428 -0
- machineconfig/logger.py +1 -1
- machineconfig/profile/create_helper.py +21 -10
- machineconfig/profile/create_links.py +77 -20
- machineconfig/profile/create_links_export.py +63 -58
- machineconfig/profile/create_shell_profile.py +14 -0
- machineconfig/profile/mapper_data.toml +45 -0
- machineconfig/profile/mapper_dotfiles.toml +249 -0
- machineconfig/scripts/python/agents.py +76 -171
- machineconfig/scripts/python/ai/initai.py +3 -1
- machineconfig/scripts/python/ai/scripts/__init__.py +1 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +2 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +8 -6
- machineconfig/scripts/python/ai/solutions/claude/claude.py +1 -1
- machineconfig/scripts/python/ai/solutions/cline/cline.py +1 -1
- machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +1 -1
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +29 -0
- machineconfig/scripts/python/ai/solutions/crush/crush.py +1 -1
- machineconfig/scripts/python/ai/solutions/cursor/cursors.py +1 -1
- machineconfig/scripts/python/ai/solutions/gemini/gemini.py +1 -1
- machineconfig/scripts/python/ai/solutions/gemini/settings.json +3 -0
- machineconfig/scripts/python/ai/{solutions → utils}/generic.py +2 -15
- machineconfig/scripts/python/ai/utils/vscode_tasks.py +6 -3
- machineconfig/scripts/python/cloud.py +58 -11
- machineconfig/scripts/python/croshell.py +4 -155
- machineconfig/scripts/python/devops.py +57 -38
- machineconfig/scripts/python/devops_navigator.py +17 -3
- machineconfig/scripts/python/fire_jobs.py +10 -193
- machineconfig/scripts/python/ftpx.py +5 -224
- machineconfig/scripts/python/graph/cli_graph.json +8743 -0
- machineconfig/scripts/python/{env_manager → helper_env}/path_manager_tui.py +2 -2
- machineconfig/scripts/python/{env_manager → helpers/helper_env}/env_manager_tui.py +1 -1
- machineconfig/scripts/python/helpers/helper_env/path_manager_tui.py +228 -0
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_cursor_agents.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_gemini.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_qwen.py +1 -1
- machineconfig/scripts/python/helpers/helpers_agents/agents_impl.py +168 -0
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_launch.py +10 -7
- machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/aichat/config.yaml +5 -0
- machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/aider/.aider.conf.yml +2 -0
- machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/copilot/config.yml +1 -0
- machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/crush/crush.json +10 -0
- machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/gemini/settings.json +12 -0
- machineconfig/scripts/python/helpers/helpers_agents/privacy/privacy.py +109 -0
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.sh +3 -1
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_copy.py +6 -6
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_mount.py +10 -5
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_sync.py +4 -4
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers2.py +1 -1
- machineconfig/scripts/python/helpers/helpers_croshell/croshell_impl.py +229 -0
- machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/scheduler.py +4 -4
- machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/start_slidev.py +7 -6
- machineconfig/scripts/python/helpers/helpers_devops/backup_config.py +149 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_backup_retrieve.py +262 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_config.py +130 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_config_dotfile.py +274 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_config_mount.py +77 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_data.py +71 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_nw.py +285 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_repos.py +274 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_self.py +84 -33
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_file.py +44 -30
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_server.py +26 -43
- machineconfig/scripts/python/helpers/helpers_devops/cli_share_temp.py +69 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_terminal.py +12 -6
- machineconfig/scripts/python/helpers/helpers_devops/cli_ssh.py +167 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_status.py +12 -6
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_update_repos.py +1 -1
- machineconfig/scripts/python/{interactive.py → helpers/helpers_devops/interactive.py} +64 -50
- 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_devops/run_script.py +197 -0
- machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.ps1 +41 -0
- machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.sh +48 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_wezterm_theme.py +3 -3
- machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/file_wrangler.py +1 -0
- machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_args_helper.py +1 -0
- machineconfig/scripts/python/helpers/helpers_fire_command/fire_jobs_impl.py +233 -0
- machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_route_helper.py +3 -3
- machineconfig/scripts/python/helpers/helpers_fire_command/fire_jobs_streamlit_helper.py +0 -0
- machineconfig/scripts/python/helpers/helpers_msearch/msearch_impl.py +248 -0
- machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_linux/fzfg +4 -3
- machineconfig/scripts/python/helpers/helpers_msearch/scripts_linux/search_with_context.sh +48 -0
- machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_windows/fzfg.ps1 +2 -7
- machineconfig/scripts/python/helpers/helpers_navigator/__init__.py +20 -0
- machineconfig/scripts/python/helpers/helpers_navigator/cli_graph_loader.py +234 -0
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/command_builder.py +61 -13
- machineconfig/scripts/python/helpers/helpers_navigator/command_detail.py +153 -0
- machineconfig/scripts/python/helpers/helpers_navigator/command_tree.py +45 -0
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/data_models.py +18 -11
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/main_app.py +5 -5
- machineconfig/scripts/python/helpers/helpers_network/__init__.py +0 -0
- machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/address.py +52 -10
- machineconfig/scripts/python/helpers/helpers_network/address_switch.py +78 -0
- machineconfig/scripts/python/helpers/helpers_network/ftpx_impl.py +276 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_add_identity.py +73 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_add_key_windows.py +23 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_add_ssh_key.py +169 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_cloud_init.py +33 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_debug_linux.py +338 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_debug_linux_utils.py +35 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_debug_windows.py +245 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh/ssh_debug_windows_utils.py +34 -0
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action.py +3 -3
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action_helper.py +3 -3
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/cloud_repo_sync.py +120 -37
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/grource.py +3 -2
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/record.py +33 -13
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_2.py +63 -19
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/update.py +0 -6
- machineconfig/scripts/python/helpers/helpers_search/script_help.py +81 -0
- machineconfig/scripts/python/helpers/helpers_sessions/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_sessions/attach_impl.py +87 -0
- machineconfig/scripts/python/helpers/helpers_sessions/sessions_impl.py +114 -0
- machineconfig/scripts/python/{helpers_sessions → helpers/helpers_sessions}/sessions_multiprocess.py +1 -2
- machineconfig/scripts/python/helpers/helpers_sessions/utils.py +69 -0
- machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/download.py +1 -1
- machineconfig/scripts/python/{helpers_devops/cli_utils.py → helpers/helpers_utils/pdf.py} +2 -2
- machineconfig/scripts/python/{helpers_utils/path.py → helpers/helpers_utils/python.py} +65 -40
- machineconfig/scripts/python/helpers/helpers_utils/specs.py +246 -0
- machineconfig/scripts/python/mcfg_entry.py +126 -48
- machineconfig/scripts/python/msearch.py +16 -61
- machineconfig/scripts/python/sessions.py +137 -191
- machineconfig/scripts/python/utils.py +104 -24
- machineconfig/settings/atuin/config.toml +294 -0
- machineconfig/settings/atuin/themes/catppuccin-mocha-mauve.toml +12 -0
- machineconfig/settings/linters/.ruff.toml +2 -1
- machineconfig/settings/mprocs/windows/mprocs.yaml +2 -2
- machineconfig/settings/shells/bash/init.sh +6 -10
- machineconfig/settings/shells/nushell/config.nu +23 -1
- machineconfig/settings/shells/nushell/env.nu +22 -48
- machineconfig/settings/shells/nushell/init.nu +64 -240
- machineconfig/settings/shells/pwsh/init.ps1 +71 -5
- machineconfig/settings/shells/pwsh/search_pwsh_history.ps1 +99 -0
- machineconfig/settings/shells/wezterm/wezterm.lua +4 -0
- machineconfig/settings/shells/wt/settings.json +31 -37
- machineconfig/settings/shells/zsh/init.sh +25 -5
- machineconfig/settings/television/cable_unix/bash-history.toml +1 -1
- machineconfig/settings/television/cable_windows/pwsh-history.toml +1 -1
- machineconfig/settings/tv/config.toml +234 -0
- machineconfig/settings/tv/themes/catppuccin-mocha-sky.toml +22 -0
- machineconfig/settings/wsl/.wslconfig +5 -30
- machineconfig/settings/wt/__init__.py +0 -0
- machineconfig/settings/yazi/yazi_linux.toml +18 -8
- 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 +39 -9
- machineconfig/settings/zellij/layouts/st2.kdl +6 -2
- machineconfig/setup_linux/__init__.py +0 -1
- machineconfig/setup_linux/apps_desktop.sh +8 -27
- machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
- machineconfig/setup_linux/web_shortcuts/live_from_github.sh +3 -0
- machineconfig/setup_mac/__init__.py +0 -2
- machineconfig/setup_windows/__init__.py +2 -5
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +14 -13
- machineconfig/setup_windows/web_shortcuts/live_from_github.ps1 +4 -3
- machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -3
- machineconfig/type_hinting/sql/__init__.py +1 -0
- machineconfig/type_hinting/sql/base.py +216 -0
- machineconfig/type_hinting/sql/core_schema.py +64 -0
- machineconfig/type_hinting/sql/core_schema_typeddict.py +41 -0
- machineconfig/type_hinting/sql/typeddict_codegen.py +222 -0
- machineconfig/type_hinting/typedict/__init__.py +1 -0
- machineconfig/type_hinting/typedict/ast_utils.py +130 -0
- machineconfig/type_hinting/typedict/generator_helpers.py +319 -0
- machineconfig/type_hinting/typedict/generators.py +231 -0
- machineconfig/type_hinting/typedict/polars_schema.py +24 -0
- machineconfig/type_hinting/typedict/polars_schema_typeddict.py +63 -0
- machineconfig/utils/accessories.py +24 -0
- machineconfig/utils/code.py +78 -33
- machineconfig/utils/files/ascii_art.py +10 -14
- machineconfig/utils/files/headers.py +3 -5
- machineconfig/utils/files/read.py +8 -1
- machineconfig/utils/installer_utils/github_release_bulk.py +11 -91
- machineconfig/utils/installer_utils/github_release_scraper.py +99 -0
- machineconfig/utils/installer_utils/install_from_url.py +1 -1
- machineconfig/utils/installer_utils/installer_class.py +12 -4
- machineconfig/utils/installer_utils/installer_cli.py +7 -17
- machineconfig/utils/installer_utils/installer_helper.py +52 -36
- machineconfig/utils/installer_utils/installer_locator_utils.py +15 -25
- machineconfig/utils/installer_utils/installer_runner.py +4 -4
- machineconfig/utils/io.py +25 -8
- machineconfig/utils/meta.py +6 -4
- machineconfig/utils/options.py +49 -19
- machineconfig/utils/options_utils/__init__.py +0 -0
- machineconfig/utils/options_utils/options_tv_linux.py +211 -0
- machineconfig/utils/options_utils/options_tv_windows.py +88 -0
- machineconfig/utils/options_utils/tv_options.py +37 -0
- machineconfig/utils/path_extended.py +8 -7
- machineconfig/utils/procs.py +35 -27
- machineconfig/utils/scheduler.py +8 -2
- machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
- machineconfig/utils/schemas/layouts/layout_types.py +10 -0
- machineconfig/utils/source_of_truth.py +7 -1
- machineconfig/utils/ssh.py +73 -23
- machineconfig/utils/ssh_utils/abc.py +1 -1
- machineconfig/utils/ssh_utils/copy_from_here.py +19 -14
- machineconfig/utils/ssh_utils/copy_to_here.py +2 -1
- machineconfig/utils/ssh_utils/utils.py +23 -7
- machineconfig/utils/ssh_utils/wsl.py +107 -170
- machineconfig/utils/ssh_utils/wsl_helper.py +217 -0
- machineconfig/utils/upgrade_packages.py +4 -8
- {machineconfig-7.98.dist-info → machineconfig-8.61.dist-info}/METADATA +30 -23
- machineconfig-8.61.dist-info/RECORD +539 -0
- {machineconfig-7.98.dist-info → machineconfig-8.61.dist-info}/entry_points.txt +0 -1
- machineconfig/jobs/installer/check_installations.py +0 -248
- machineconfig/profile/backup.toml +0 -49
- machineconfig/profile/mapper.toml +0 -263
- machineconfig/scripts/linux/other/share_cloud.sh +0 -64
- machineconfig/scripts/linux/other/share_nfs +0 -49
- machineconfig/scripts/linux/other/start_docker +0 -23
- machineconfig/scripts/linux/other/switch_ip +0 -20
- machineconfig/scripts/python/helpers/run_py_script.py +0 -79
- machineconfig/scripts/python/helpers/tmp_py_scripts/a.py +0 -26
- machineconfig/scripts/python/helpers_devops/cli_config.py +0 -105
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +0 -89
- machineconfig/scripts/python/helpers_devops/cli_data.py +0 -25
- machineconfig/scripts/python/helpers_devops/cli_nw.py +0 -214
- machineconfig/scripts/python/helpers_devops/cli_repos.py +0 -215
- machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +0 -80
- machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +0 -3
- machineconfig/scripts/python/helpers_navigator/__init__.py +0 -20
- machineconfig/scripts/python/helpers_navigator/command_detail.py +0 -44
- machineconfig/scripts/python/helpers_navigator/command_tree.py +0 -620
- machineconfig/scripts/python/helpers_network/devops_add_identity.py +0 -82
- machineconfig/scripts/python/helpers_network/devops_add_ssh_key.py +0 -153
- machineconfig/scripts/python/helpers_network/mount_drive +0 -128
- machineconfig/scripts/python/helpers_network/mount_nfs +0 -49
- machineconfig/scripts/python/helpers_network/mount_nfs.py +0 -85
- machineconfig/scripts/python/helpers_network/mount_nw_drive +0 -61
- machineconfig/scripts/python/helpers_network/mount_nw_drive.py +0 -48
- machineconfig/scripts/python/helpers_network/mount_smb +0 -3
- machineconfig/scripts/python/helpers_network/mount_ssh.py +0 -64
- machineconfig/scripts/python/helpers_network/ssh_debug_linux.py +0 -391
- machineconfig/scripts/python/helpers_network/ssh_debug_windows.py +0 -338
- machineconfig/scripts/python/helpers_network/wsl_windows_transfer.py +0 -67
- machineconfig/scripts/python/helpers_repos/entrypoint.py +0 -77
- machineconfig/scripts/python/terminal.py +0 -133
- machineconfig/scripts/windows/mounts/Restore-ThunderbirdProfile.ps1 +0 -92
- machineconfig/scripts/windows/mounts/mount_nfs.ps1 +0 -42
- machineconfig/scripts/windows/mounts/mount_nw.ps1 +0 -9
- machineconfig/scripts/windows/mounts/mount_smb.ps1 +0 -2
- machineconfig/scripts/windows/mounts/mount_ssh.ps1 +0 -13
- machineconfig/scripts/windows/mounts/share_cloud.cmd +0 -34
- machineconfig/scripts/windows/mounts/share_smb.ps1 +0 -16
- machineconfig/settings/zellij/config.orig.kdl +0 -295
- machineconfig/setup_linux/others/android.sh +0 -2
- machineconfig/setup_linux/others/mint_keyboard_shortcuts.sh +0 -30
- machineconfig/setup_linux/ssh/openssh_all.sh +0 -25
- machineconfig/setup_linux/ssh/openssh_wsl.sh +0 -38
- machineconfig/setup_mac/ssh/openssh_setup.sh +0 -114
- machineconfig/setup_windows/others/docker.ps1 +0 -7
- machineconfig/setup_windows/others/obs.ps1 +0 -4
- machineconfig/setup_windows/others/power_options.ps1 +0 -7
- machineconfig/setup_windows/ssh/add-sshkey.ps1 +0 -29
- machineconfig/setup_windows/ssh/add_identity.ps1 +0 -11
- machineconfig/setup_windows/ssh/openssh-server.ps1 +0 -37
- machineconfig/setup_windows/ssh/openssh-server_add_key.ps1 +0 -7
- machineconfig/setup_windows/ssh/openssh-server_copy-ssh-id.ps1 +0 -14
- machineconfig/utils/options_tv.py +0 -119
- machineconfig/utils/tst.py +0 -20
- machineconfig-7.98.dist-info/RECORD +0 -504
- /machineconfig/{jobs/installer/custom_dev → cluster/sessions_managers/wt_utils/examples}/__init__.py +0 -0
- /machineconfig/{scripts/python/helpers_agents → jobs/installer/checks}/__init__.py +0 -0
- /machineconfig/{scripts/python/helpers_agents/agentic_frameworks → jobs/installer/python_scripts}/__init__.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/alacritty.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/bypass_paywall.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/cloudflare_warp_cli.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/cursor.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/dubdb_adbc.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/espanso.py +0 -0
- /machineconfig/jobs/installer/{custom → python_scripts}/gh.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/goes.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/lvim.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/redis.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/winget.py +0 -0
- /machineconfig/jobs/{installer/linux_scripts → scripts/bash_scripts}/lid.sh +0 -0
- /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/unlock_bitlocker.ps1 +0 -0
- /machineconfig/scripts/python/ai/{solutions/_shared.py → utils/shared.py} +0 -0
- /machineconfig/scripts/python/{helpers_cloud → graph}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers}/__init__.py +0 -0
- /machineconfig/scripts/python/{env_manager → helpers/helper_env}/__init__.py +0 -0
- /machineconfig/scripts/python/{env_manager → helpers/helper_env}/path_manager_backend.py +0 -0
- /machineconfig/scripts/python/{helpers_devops → helpers/helpers_agents}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_devops/themes → helpers/helpers_agents/agentic_frameworks}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.json +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_search.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_helper_types.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_load_balancer.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/prompt.txt +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.ps1 +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_cloud}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_helpers.py +0 -0
- /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers5.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_croshell}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/crosh.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/pomodoro.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer_template.py +0 -0
- /machineconfig/scripts/python/{helpers_sessions → helpers/helpers_devops}/__init__.py +0 -0
- /machineconfig/{setup_windows/wt_and_pwsh → scripts/python/helpers/helpers_devops/mount_helpers}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_devops/themes/choose_starship_theme.ps1 → helpers/helpers_devops/themes/__init__.py} +0 -0
- /machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_pwsh_theme.ps1 +0 -0
- /machineconfig/scripts/python/{helpers_fire_command/f.py → helpers/helpers_fire_command/__init__.py} +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/cloud_manager.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command/fire_jobs_streamlit_helper.py → helpers/helpers_fire_command/f.py} +0 -0
- /machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/search_bar.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/onetimeshare.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/wifi_conn.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/clone.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_1.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/sync.py +0 -0
- /machineconfig/scripts/python/helpers/{ast_search.py → helpers_search/ast_search.py} +0 -0
- /machineconfig/scripts/python/helpers/{qr_code.py → helpers_search/qr_code.py} +0 -0
- /machineconfig/scripts/python/helpers/{repo_rag.py → helpers_search/repo_rag.py} +0 -0
- /machineconfig/scripts/python/helpers/{symantic_search.py → helpers_search/symantic_search.py} +0 -0
- /machineconfig/{setup_windows/wt_and_pwsh → settings/wt}/set_wt_settings.py +0 -0
- {machineconfig-7.98.dist-info → machineconfig-8.61.dist-info}/WHEEL +0 -0
- {machineconfig-7.98.dist-info → machineconfig-8.61.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""SSH"""
|
|
2
|
+
|
|
3
|
+
from platform import system
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich import box
|
|
8
|
+
from typing import Optional, Annotated
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
from machineconfig.scripts.python.helpers.helpers_network.ssh.ssh_add_key_windows import add_ssh_key_windows
|
|
12
|
+
from machineconfig.scripts.python.helpers.helpers_network.ssh.ssh_cloud_init import check_cloud_init_overrides, generate_cloud_init_fix_script
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_add_ssh_key_script(path_to_key: Path, verbose: bool = True) -> tuple[str, str]:
|
|
19
|
+
"""Returns (program_script, status_message) tuple. For Windows, program_script is empty because we handle it in Python."""
|
|
20
|
+
os_name = system()
|
|
21
|
+
if os_name == "Linux" or os_name == "Darwin":
|
|
22
|
+
authorized_keys = Path.home().joinpath(".ssh/authorized_keys")
|
|
23
|
+
os_icon, os_label = "🐧", "Linux/macOS"
|
|
24
|
+
elif os_name == "Windows":
|
|
25
|
+
authorized_keys = Path("C:/ProgramData/ssh/administrators_authorized_keys")
|
|
26
|
+
os_icon, os_label = "🪟", "Windows"
|
|
27
|
+
else:
|
|
28
|
+
raise NotImplementedError("Only Linux, macOS and Windows are supported")
|
|
29
|
+
|
|
30
|
+
status_lines: list[str] = [f"{os_icon} {os_label} │ Auth file: {authorized_keys}"]
|
|
31
|
+
program = ""
|
|
32
|
+
|
|
33
|
+
if authorized_keys.exists():
|
|
34
|
+
keys_text = authorized_keys.read_text(encoding="utf-8").split("\n")
|
|
35
|
+
key_count = len([k for k in keys_text if k.strip()])
|
|
36
|
+
status_lines.append(f"🔑 Existing keys: {key_count}")
|
|
37
|
+
if path_to_key.read_text(encoding="utf-8") in authorized_keys.read_text(encoding="utf-8"):
|
|
38
|
+
status_lines.append(f"⚠️ Key [yellow]{path_to_key.name}[/yellow] already authorized, skipping")
|
|
39
|
+
else:
|
|
40
|
+
status_lines.append(f"➕ Adding: [green]{path_to_key.name}[/green]")
|
|
41
|
+
if os_name == "Linux" or os_name == "Darwin":
|
|
42
|
+
program = f"cat {path_to_key} >> ~/.ssh/authorized_keys"
|
|
43
|
+
elif os_name == "Windows":
|
|
44
|
+
add_ssh_key_windows(path_to_key)
|
|
45
|
+
else:
|
|
46
|
+
raise NotImplementedError
|
|
47
|
+
else:
|
|
48
|
+
status_lines.append(f"📝 Creating auth file with: [green]{path_to_key.name}[/green]")
|
|
49
|
+
if os_name == "Linux" or os_name == "Darwin":
|
|
50
|
+
program = f"cat {path_to_key} > ~/.ssh/authorized_keys"
|
|
51
|
+
else:
|
|
52
|
+
add_ssh_key_windows(path_to_key)
|
|
53
|
+
|
|
54
|
+
if os_name == "Linux" or os_name == "Darwin":
|
|
55
|
+
override_files, auth_overrides = check_cloud_init_overrides()
|
|
56
|
+
if override_files:
|
|
57
|
+
status_lines.append(f"\n⚠️ [yellow]Cloud-init override files detected:[/yellow]")
|
|
58
|
+
for of in override_files:
|
|
59
|
+
status_lines.append(f" • {of.name}")
|
|
60
|
+
blocking_overrides: list[str] = []
|
|
61
|
+
for key, (file_path, value) in auth_overrides.items():
|
|
62
|
+
if key == "PubkeyAuthentication" and value == "no":
|
|
63
|
+
blocking_overrides.append(f" ❌ {key}={value} in {file_path.name} - [red]blocks key auth![/red]")
|
|
64
|
+
elif key == "PasswordAuthentication" and value == "no":
|
|
65
|
+
blocking_overrides.append(f" ⚠️ {key}={value} in {file_path.name}")
|
|
66
|
+
if blocking_overrides:
|
|
67
|
+
status_lines.extend(blocking_overrides)
|
|
68
|
+
cloud_init_fix = generate_cloud_init_fix_script(auth_overrides)
|
|
69
|
+
if cloud_init_fix:
|
|
70
|
+
program += f"\n# === Fix cloud-init SSH overrides ===\n{cloud_init_fix}\n"
|
|
71
|
+
program += """
|
|
72
|
+
sudo chmod 700 ~/.ssh
|
|
73
|
+
sudo chmod 644 ~/.ssh/authorized_keys
|
|
74
|
+
sudo chmod 644 ~/.ssh/*.pub
|
|
75
|
+
sudo service ssh --full-restart
|
|
76
|
+
# from superuser.com/questions/215504/permissions-on-private-key-in-ssh-folder
|
|
77
|
+
"""
|
|
78
|
+
return program, "\n".join(status_lines)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
Common pitfalls:
|
|
83
|
+
🚫 Wrong line endings (LF/CRLF) in config files
|
|
84
|
+
🌐 Network port conflicts (try 2222 -> 2223) between WSL and Windows
|
|
85
|
+
sudo service ssh restart
|
|
86
|
+
sudo service ssh status
|
|
87
|
+
sudo nano /etc/ssh/sshd_config
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def main(pub_path: Annotated[Optional[str], typer.Argument(help="Path to the public key file")] = None,
|
|
92
|
+
pub_choose: Annotated[bool, typer.Option("--choose", "-c", help="Choose from available public keys in ~/.ssh")] = False,
|
|
93
|
+
pub_val: Annotated[bool, typer.Option("--paste", "-p", help="Paste the public key content manually")] = False,
|
|
94
|
+
from_github: Annotated[Optional[str], typer.Option("--from-github", "-g", help="Fetch public keys from a GitHub username")] = None
|
|
95
|
+
) -> None:
|
|
96
|
+
info_lines: list[str] = []
|
|
97
|
+
program = ""
|
|
98
|
+
status_msg = ""
|
|
99
|
+
|
|
100
|
+
if pub_path:
|
|
101
|
+
key_path = Path(pub_path).expanduser().absolute()
|
|
102
|
+
key_path.parent.mkdir(parents=True, exist_ok=True)
|
|
103
|
+
if not key_path.exists():
|
|
104
|
+
console.print(Panel(f"❌ Key path does not exist: {key_path}", title="[bold red]Error[/bold red]", border_style="red"))
|
|
105
|
+
raise typer.Exit(code=1)
|
|
106
|
+
info_lines.append(f"📄 Source: Local file │ {key_path}")
|
|
107
|
+
program, status_msg = get_add_ssh_key_script(key_path)
|
|
108
|
+
|
|
109
|
+
elif pub_choose:
|
|
110
|
+
pub_keys = list(Path.home().joinpath(".ssh").glob("*.pub"))
|
|
111
|
+
if not pub_keys:
|
|
112
|
+
console.print(Panel("⚠️ No public keys found in ~/.ssh", title="[bold yellow]Warning[/bold yellow]", border_style="yellow"))
|
|
113
|
+
return
|
|
114
|
+
info_lines.append(f"📄 Source: Local ~/.ssh │ Found {len(pub_keys)} key(s)")
|
|
115
|
+
programs: list[str] = []
|
|
116
|
+
statuses: list[str] = []
|
|
117
|
+
for key in pub_keys:
|
|
118
|
+
p, s = get_add_ssh_key_script(key)
|
|
119
|
+
programs.append(p)
|
|
120
|
+
statuses.append(s)
|
|
121
|
+
program = "\n\n\n".join(programs)
|
|
122
|
+
status_msg = "\n".join(statuses)
|
|
123
|
+
|
|
124
|
+
elif pub_val:
|
|
125
|
+
key_filename = input("📝 File name (default: my_pasted_key.pub): ") or "my_pasted_key.pub"
|
|
126
|
+
key_path = Path.home().joinpath(f".ssh/{key_filename}")
|
|
127
|
+
key_path.parent.mkdir(parents=True, exist_ok=True)
|
|
128
|
+
key_path.write_text(input("🔑 Paste the public key here: "), encoding="utf-8")
|
|
129
|
+
info_lines.append(f"📄 Source: Pasted │ Saved to {key_path}")
|
|
130
|
+
program, status_msg = get_add_ssh_key_script(key_path)
|
|
131
|
+
|
|
132
|
+
elif from_github:
|
|
133
|
+
import requests
|
|
134
|
+
response = requests.get(f"https://api.github.com/users/{from_github}/keys")
|
|
135
|
+
if response.status_code != 200:
|
|
136
|
+
console.print(Panel(f"❌ GitHub API error for user '{from_github}' │ Status: {response.status_code}", title="[bold red]Error[/bold red]", border_style="red"))
|
|
137
|
+
raise typer.Exit(code=1)
|
|
138
|
+
keys = response.json()
|
|
139
|
+
if not keys:
|
|
140
|
+
console.print(Panel(f"⚠️ No public keys found for GitHub user: {from_github}", title="[bold yellow]Warning[/bold yellow]", border_style="yellow"))
|
|
141
|
+
return
|
|
142
|
+
key_path = Path.home().joinpath(f".ssh/{from_github}_github_keys.pub")
|
|
143
|
+
key_path.parent.mkdir(parents=True, exist_ok=True)
|
|
144
|
+
key_path.write_text("\n".join([key["key"] for key in keys]), encoding="utf-8")
|
|
145
|
+
info_lines.append(f"📄 Source: GitHub @{from_github} │ {len(keys)} key(s) → {key_path}")
|
|
146
|
+
program, status_msg = get_add_ssh_key_script(key_path)
|
|
147
|
+
|
|
148
|
+
else:
|
|
149
|
+
console.print(Panel("❌ No key source specified. Use --help for options.", title="[bold red]Error[/bold red]", border_style="red"))
|
|
150
|
+
raise typer.Exit(code=1)
|
|
151
|
+
|
|
152
|
+
combined_info = "\n".join(info_lines + [""] + status_msg.split("\n"))
|
|
153
|
+
console.print(Panel(combined_info, title="[bold blue]🔑 SSH Key Authorization[/bold blue]", border_style="blue"))
|
|
154
|
+
|
|
155
|
+
if program.strip():
|
|
156
|
+
from machineconfig.utils.code import run_shell_script
|
|
157
|
+
run_shell_script(script=program, display_script=True, clean_env=False)
|
|
158
|
+
|
|
159
|
+
import machineconfig.scripts.python.helpers.helpers_network.address as helper
|
|
160
|
+
res = helper.select_lan_ipv4(prefer_vpn=False)
|
|
161
|
+
if res is None:
|
|
162
|
+
console.print(Panel("❌ Could not determine local LAN IPv4 address", title="[bold red]Error[/bold red]", border_style="red"))
|
|
163
|
+
raise typer.Exit(code=1)
|
|
164
|
+
|
|
165
|
+
console.print(Panel(f"✅ Complete │ This machine accessible at: [green]{res}[/green]", title="[bold green]SSH Key Authorization[/bold green]", border_style="green", box=box.DOUBLE_EDGE))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if __name__ == "__main__":
|
|
169
|
+
pass
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def check_cloud_init_overrides() -> tuple[list[Path], dict[str, tuple[Path, str]]]:
|
|
5
|
+
sshd_config_d = Path("/etc/ssh/sshd_config.d")
|
|
6
|
+
override_files: list[Path] = []
|
|
7
|
+
auth_overrides: dict[str, tuple[Path, str]] = {}
|
|
8
|
+
if not sshd_config_d.exists():
|
|
9
|
+
return override_files, auth_overrides
|
|
10
|
+
for conf_file in sorted(sshd_config_d.glob("*.conf")):
|
|
11
|
+
override_files.append(conf_file)
|
|
12
|
+
try:
|
|
13
|
+
conf_text = conf_file.read_text(encoding="utf-8")
|
|
14
|
+
for line in conf_text.split("\n"):
|
|
15
|
+
line_stripped = line.strip()
|
|
16
|
+
if line_stripped and not line_stripped.startswith("#"):
|
|
17
|
+
parts = line_stripped.split(None, 1)
|
|
18
|
+
if len(parts) >= 2:
|
|
19
|
+
key, value = parts[0], parts[1]
|
|
20
|
+
if key in ("PasswordAuthentication", "PubkeyAuthentication", "PermitRootLogin", "ChallengeResponseAuthentication", "KbdInteractiveAuthentication"):
|
|
21
|
+
auth_overrides[key] = (conf_file, value.lower())
|
|
22
|
+
except Exception:
|
|
23
|
+
pass
|
|
24
|
+
return override_files, auth_overrides
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def generate_cloud_init_fix_script(auth_overrides: dict[str, tuple[Path, str]]) -> str:
|
|
28
|
+
fix_commands: list[str] = []
|
|
29
|
+
for key, (file_path, value) in auth_overrides.items():
|
|
30
|
+
if key in ("PasswordAuthentication", "PubkeyAuthentication") and value == "no":
|
|
31
|
+
fix_commands.append(f"# Fix {key} in {file_path.name}")
|
|
32
|
+
fix_commands.append(f"sudo sed -i 's/^{key}.*no/{key} yes/' {file_path}")
|
|
33
|
+
return "\n".join(fix_commands)
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from platform import system
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
from rich import box
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
from machineconfig.scripts.python.helpers.helpers_network.ssh.ssh_debug_linux_utils import check_sshd_installed, detect_package_manager, run_cmd
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
19
|
+
if system() != "Linux":
|
|
20
|
+
raise NotImplementedError("ssh_debug_linux is only supported on Linux")
|
|
21
|
+
|
|
22
|
+
results: dict[str, dict[str, str | bool]] = {}
|
|
23
|
+
issues: list[tuple[str, str, str]] = []
|
|
24
|
+
current_user = os.environ.get("USER", os.environ.get("USERNAME", "unknown"))
|
|
25
|
+
ssh_port = "22"
|
|
26
|
+
ip_addresses: list[str] = []
|
|
27
|
+
|
|
28
|
+
ok, hostname = run_cmd(["hostname"])
|
|
29
|
+
hostname = hostname if ok else "unknown"
|
|
30
|
+
|
|
31
|
+
install_info: list[str] = []
|
|
32
|
+
sshd_installed, sshd_path = check_sshd_installed()
|
|
33
|
+
_pkg_manager, install_cmd = detect_package_manager()
|
|
34
|
+
if not sshd_installed:
|
|
35
|
+
results["installation"] = {"status": "error", "message": "OpenSSH Server not installed"}
|
|
36
|
+
issues.append(("sshd not installed", "Cannot accept incoming SSH connections", install_cmd))
|
|
37
|
+
install_info.append("❌ OpenSSH Server: [red]NOT INSTALLED[/red]")
|
|
38
|
+
install_info.append(f" [dim]Install with: {install_cmd}[/dim]")
|
|
39
|
+
else:
|
|
40
|
+
results["installation"] = {"status": "ok", "message": f"sshd found at {sshd_path}"}
|
|
41
|
+
install_info.append(f"✅ OpenSSH Server: installed at [cyan]{sshd_path}[/cyan]")
|
|
42
|
+
console.print(Panel("\n".join(install_info), title="[bold]Installation[/bold]", border_style="blue"))
|
|
43
|
+
|
|
44
|
+
ssh_dir = Path.home().joinpath(".ssh")
|
|
45
|
+
authorized_keys = ssh_dir.joinpath("authorized_keys")
|
|
46
|
+
home_dir = Path.home()
|
|
47
|
+
|
|
48
|
+
perm_info: list[str] = []
|
|
49
|
+
home_stat = os.stat(home_dir)
|
|
50
|
+
home_perms = oct(home_stat.st_mode)[-3:]
|
|
51
|
+
if home_perms[2] in ["7", "6", "3", "2"]:
|
|
52
|
+
results["home_directory"] = {"status": "error", "message": f"Home world-writable: {home_perms}"}
|
|
53
|
+
issues.append((f"Home dir perms {home_perms}", "sshd refuses login if home is world-writable", f"chmod 755 {home_dir}"))
|
|
54
|
+
perm_info.append(f"❌ Home directory: [red]{home_perms}[/red] (world-writable)")
|
|
55
|
+
perm_info.append(" [dim]sshd will refuse key auth if home is writable by others[/dim]")
|
|
56
|
+
else:
|
|
57
|
+
perm_info.append(f"✅ Home directory: {home_perms}")
|
|
58
|
+
|
|
59
|
+
if not ssh_dir.exists():
|
|
60
|
+
results["ssh_directory"] = {"status": "error", "message": "~/.ssh missing"}
|
|
61
|
+
issues.append(("~/.ssh missing", "No place for authorized_keys", "mkdir -p ~/.ssh && chmod 700 ~/.ssh"))
|
|
62
|
+
perm_info.append("❌ ~/.ssh: [red]does not exist[/red]")
|
|
63
|
+
else:
|
|
64
|
+
ssh_perms = oct(os.stat(ssh_dir).st_mode)[-3:]
|
|
65
|
+
if ssh_perms != "700":
|
|
66
|
+
results["ssh_directory"] = {"status": "error", "message": f"~/.ssh perms {ssh_perms}"}
|
|
67
|
+
issues.append((f"~/.ssh perms {ssh_perms}", "sshd requires 700 on ~/.ssh", "chmod 700 ~/.ssh"))
|
|
68
|
+
perm_info.append(f"❌ ~/.ssh: [red]{ssh_perms}[/red] (must be 700)")
|
|
69
|
+
else:
|
|
70
|
+
perm_info.append(f"✅ ~/.ssh: {ssh_perms}")
|
|
71
|
+
|
|
72
|
+
if not authorized_keys.exists():
|
|
73
|
+
results["authorized_keys"] = {"status": "error", "message": "authorized_keys missing"}
|
|
74
|
+
issues.append(("authorized_keys missing", "No keys = no login", "Add public key: cat id_rsa.pub >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"))
|
|
75
|
+
perm_info.append("❌ authorized_keys: [red]does not exist[/red]")
|
|
76
|
+
perm_info.append(" [dim]No authorized keys = cannot login with SSH key[/dim]")
|
|
77
|
+
else:
|
|
78
|
+
ak_perms = oct(os.stat(authorized_keys).st_mode)[-3:]
|
|
79
|
+
try:
|
|
80
|
+
keys = [line for line in authorized_keys.read_text(encoding="utf-8").split("\n") if line.strip()]
|
|
81
|
+
key_count = len(keys)
|
|
82
|
+
except Exception:
|
|
83
|
+
key_count = 0
|
|
84
|
+
if ak_perms not in ["600", "644"]:
|
|
85
|
+
results["authorized_keys"] = {"status": "error", "message": f"authorized_keys perms {ak_perms}"}
|
|
86
|
+
issues.append((f"authorized_keys perms {ak_perms}", "sshd requires 600 or 644", "chmod 600 ~/.ssh/authorized_keys"))
|
|
87
|
+
perm_info.append(f"❌ authorized_keys: [red]{ak_perms}[/red] ({key_count} key(s)) - must be 600/644")
|
|
88
|
+
else:
|
|
89
|
+
results["authorized_keys"] = {"status": "ok", "message": f"{key_count} key(s)"}
|
|
90
|
+
perm_info.append(f"✅ authorized_keys: {ak_perms} ([green]{key_count} key(s)[/green])")
|
|
91
|
+
|
|
92
|
+
console.print(Panel("\n".join(perm_info), title="[bold]Permissions[/bold]", border_style="blue"))
|
|
93
|
+
|
|
94
|
+
svc_info: list[str] = []
|
|
95
|
+
ssh_ok, _ = run_cmd(["systemctl", "is-active", "ssh"])
|
|
96
|
+
sshd_ok, _ = run_cmd(["systemctl", "is-active", "sshd"])
|
|
97
|
+
if ssh_ok or sshd_ok:
|
|
98
|
+
svc_name = "ssh" if ssh_ok else "sshd"
|
|
99
|
+
results["ssh_service"] = {"status": "ok", "message": f"{svc_name} running"}
|
|
100
|
+
svc_info.append(f"✅ Service: [green]{svc_name} running[/green]")
|
|
101
|
+
else:
|
|
102
|
+
results["ssh_service"] = {"status": "error", "message": "sshd not running"}
|
|
103
|
+
issues.append(("sshd not running", "No SSH daemon = no connections", "sudo systemctl start ssh && sudo systemctl enable ssh"))
|
|
104
|
+
svc_info.append("❌ Service: [red]not running[/red]")
|
|
105
|
+
|
|
106
|
+
console.print(Panel("\n".join(svc_info), title="[bold]Service[/bold]", border_style="blue"))
|
|
107
|
+
|
|
108
|
+
net_info: list[str] = []
|
|
109
|
+
ok, ip_out = run_cmd(["ip", "addr", "show"])
|
|
110
|
+
if ok:
|
|
111
|
+
ip_addresses = re.findall(r'inet\s+(\d+\.\d+\.\d+\.\d+)/\d+.*scope\s+global', ip_out)
|
|
112
|
+
if ip_addresses:
|
|
113
|
+
net_info.append(f"🌐 IP: [cyan]{', '.join(ip_addresses)}[/cyan]")
|
|
114
|
+
|
|
115
|
+
sshd_config_paths = [Path("/etc/ssh/sshd_config"), Path("/etc/sshd_config")]
|
|
116
|
+
sshd_config: Path | None = None
|
|
117
|
+
for p in sshd_config_paths:
|
|
118
|
+
if p.exists():
|
|
119
|
+
sshd_config = p
|
|
120
|
+
break
|
|
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
|
+
|
|
150
|
+
if sshd_config:
|
|
151
|
+
try:
|
|
152
|
+
config_text = sshd_config.read_text(encoding="utf-8")
|
|
153
|
+
port_lines = [line for line in config_text.split("\n") if line.strip().startswith("Port") and not line.strip().startswith("#")]
|
|
154
|
+
if port_lines:
|
|
155
|
+
ssh_port = port_lines[0].split()[1]
|
|
156
|
+
net_info.append(f"🔌 Port: [cyan]{ssh_port}[/cyan]")
|
|
157
|
+
|
|
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
|
|
168
|
+
if pubkey_lines and "no" in pubkey_lines[-1].lower():
|
|
169
|
+
results["pubkey_auth"] = {"status": "error", "message": "PubkeyAuthentication disabled"}
|
|
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}")
|
|
174
|
+
else:
|
|
175
|
+
net_info.append("✅ PubkeyAuthentication: enabled")
|
|
176
|
+
|
|
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
|
|
187
|
+
if password_lines:
|
|
188
|
+
password_enabled = "yes" in password_lines[-1].lower()
|
|
189
|
+
override_note = f" (from {password_override_file.name})" if password_override_file else ""
|
|
190
|
+
if password_enabled:
|
|
191
|
+
results["password_auth"] = {"status": "ok", "message": "PasswordAuthentication enabled"}
|
|
192
|
+
net_info.append(f"✅ PasswordAuthentication: [green]enabled[/green]{override_note}")
|
|
193
|
+
else:
|
|
194
|
+
results["password_auth"] = {"status": "info", "message": "PasswordAuthentication disabled"}
|
|
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"))
|
|
198
|
+
else:
|
|
199
|
+
results["password_auth"] = {"status": "ok", "message": "PasswordAuthentication enabled (default)"}
|
|
200
|
+
net_info.append("✅ PasswordAuthentication: [green]enabled[/green] (default)")
|
|
201
|
+
|
|
202
|
+
permit_root = [line for line in config_text.split("\n") if "PermitRootLogin" in line and not line.strip().startswith("#")]
|
|
203
|
+
if permit_root:
|
|
204
|
+
val = permit_root[-1].split()[-1].lower()
|
|
205
|
+
net_info.append(f"ℹ️ PermitRootLogin: {val}")
|
|
206
|
+
except Exception:
|
|
207
|
+
pass
|
|
208
|
+
|
|
209
|
+
ok, ss_out = run_cmd(["ss", "-tlnp"])
|
|
210
|
+
if ok:
|
|
211
|
+
listening = [line for line in ss_out.split("\n") if f":{ssh_port}" in line]
|
|
212
|
+
if not listening:
|
|
213
|
+
results["ssh_listening"] = {"status": "error", "message": f"Not listening on {ssh_port}"}
|
|
214
|
+
issues.append((f"Not listening on port {ssh_port}", "No connections possible", "sudo systemctl restart ssh"))
|
|
215
|
+
net_info.append(f"❌ Listening: [red]NOT on port {ssh_port}[/red]")
|
|
216
|
+
elif all("127.0.0.1" in line or "[::1]" in line for line in listening):
|
|
217
|
+
results["ssh_listening"] = {"status": "error", "message": "Localhost only"}
|
|
218
|
+
issues.append(("SSH bound to localhost", "Only local connections", f"Edit {sshd_config}: remove/comment ListenAddress 127.0.0.1"))
|
|
219
|
+
net_info.append("❌ Listening: [red]localhost only[/red]")
|
|
220
|
+
else:
|
|
221
|
+
results["ssh_listening"] = {"status": "ok", "message": f"Listening on {ssh_port}"}
|
|
222
|
+
net_info.append(f"✅ Listening: 0.0.0.0:{ssh_port}")
|
|
223
|
+
|
|
224
|
+
fw_checked = False
|
|
225
|
+
ok, ufw_out = run_cmd(["ufw", "status"])
|
|
226
|
+
if ok and "Status: active" in ufw_out:
|
|
227
|
+
fw_checked = True
|
|
228
|
+
if f"{ssh_port}/tcp" in ufw_out.lower() or "ssh" in ufw_out.lower() or f" {ssh_port} " in ufw_out:
|
|
229
|
+
results["firewall"] = {"status": "ok", "message": "UFW allows SSH"}
|
|
230
|
+
net_info.append("✅ Firewall (UFW): allows SSH")
|
|
231
|
+
else:
|
|
232
|
+
results["firewall"] = {"status": "error", "message": "UFW blocking SSH"}
|
|
233
|
+
issues.append(("UFW blocking SSH", "Incoming connections dropped", f"sudo ufw allow {ssh_port}/tcp"))
|
|
234
|
+
net_info.append("❌ Firewall (UFW): [red]blocking SSH[/red]")
|
|
235
|
+
net_info.append(" [dim]Active firewall without SSH rule = blocked[/dim]")
|
|
236
|
+
|
|
237
|
+
if not fw_checked:
|
|
238
|
+
ok, fwd_out = run_cmd(["firewall-cmd", "--state"])
|
|
239
|
+
if ok and "running" in fwd_out.lower():
|
|
240
|
+
fw_checked = True
|
|
241
|
+
ok2, svc_out = run_cmd(["firewall-cmd", "--list-services"])
|
|
242
|
+
if ok2 and "ssh" in svc_out.lower():
|
|
243
|
+
results["firewall"] = {"status": "ok", "message": "firewalld allows SSH"}
|
|
244
|
+
net_info.append("✅ Firewall (firewalld): allows SSH")
|
|
245
|
+
else:
|
|
246
|
+
results["firewall"] = {"status": "error", "message": "firewalld blocking SSH"}
|
|
247
|
+
issues.append(("firewalld blocking SSH", "Incoming connections dropped", "sudo firewall-cmd --permanent --add-service=ssh && sudo firewall-cmd --reload"))
|
|
248
|
+
net_info.append("❌ Firewall (firewalld): [red]blocking SSH[/red]")
|
|
249
|
+
|
|
250
|
+
if not fw_checked:
|
|
251
|
+
ok, ipt_out = run_cmd(["iptables", "-L", "INPUT", "-n"])
|
|
252
|
+
if ok and ipt_out:
|
|
253
|
+
has_drop_policy = "policy DROP" in ipt_out or "policy REJECT" in ipt_out
|
|
254
|
+
has_ssh_allow = f"dpt:{ssh_port}" in ipt_out or "dpt:ssh" in ipt_out
|
|
255
|
+
if has_drop_policy and not has_ssh_allow:
|
|
256
|
+
results["firewall"] = {"status": "error", "message": "iptables blocking SSH"}
|
|
257
|
+
issues.append(("iptables blocking SSH", "DROP/REJECT policy without SSH allow", f"sudo iptables -I INPUT -p tcp --dport {ssh_port} -j ACCEPT"))
|
|
258
|
+
net_info.append("❌ Firewall (iptables): [red]DROP policy, no SSH rule[/red]")
|
|
259
|
+
fw_checked = True
|
|
260
|
+
elif has_drop_policy and has_ssh_allow:
|
|
261
|
+
results["firewall"] = {"status": "ok", "message": "iptables allows SSH"}
|
|
262
|
+
net_info.append("✅ Firewall (iptables): allows SSH")
|
|
263
|
+
fw_checked = True
|
|
264
|
+
|
|
265
|
+
if not fw_checked:
|
|
266
|
+
net_info.append("ℹ️ Firewall: none detected / not active")
|
|
267
|
+
|
|
268
|
+
console.print(Panel("\n".join(net_info), title="[bold]Network & Firewall[/bold]", border_style="blue"))
|
|
269
|
+
|
|
270
|
+
other_info: list[str] = []
|
|
271
|
+
hosts_deny = Path("/etc/hosts.deny")
|
|
272
|
+
if hosts_deny.exists():
|
|
273
|
+
try:
|
|
274
|
+
content = hosts_deny.read_text(encoding="utf-8")
|
|
275
|
+
active = [line for line in content.splitlines() if line.strip() and not line.strip().startswith("#")]
|
|
276
|
+
joined = " ".join(active).lower()
|
|
277
|
+
if "sshd" in joined or "all" in joined:
|
|
278
|
+
results["hosts_deny"] = {"status": "error", "message": "hosts.deny blocking"}
|
|
279
|
+
issues.append(("hosts.deny blocking SSH", "TCP wrappers deny before firewall", "Edit /etc/hosts.deny to remove sshd/ALL entries"))
|
|
280
|
+
other_info.append("❌ /etc/hosts.deny: [red]may block SSH[/red]")
|
|
281
|
+
else:
|
|
282
|
+
other_info.append("✅ /etc/hosts.deny: OK")
|
|
283
|
+
except Exception:
|
|
284
|
+
pass
|
|
285
|
+
|
|
286
|
+
ok, se_out = run_cmd(["getenforce"])
|
|
287
|
+
if ok and se_out:
|
|
288
|
+
if se_out == "Enforcing":
|
|
289
|
+
other_info.append("ℹ️ SELinux: Enforcing (run [cyan]restorecon -Rv ~/.ssh[/cyan] if issues)")
|
|
290
|
+
else:
|
|
291
|
+
other_info.append(f"ℹ️ SELinux: {se_out}")
|
|
292
|
+
|
|
293
|
+
log_files = [Path("/var/log/auth.log"), Path("/var/log/secure")]
|
|
294
|
+
for lf in log_files:
|
|
295
|
+
if lf.exists():
|
|
296
|
+
ok, tail = run_cmd(["tail", "-n", "20", str(lf)])
|
|
297
|
+
if ok:
|
|
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()]
|
|
299
|
+
if errors:
|
|
300
|
+
other_info.append(f"⚠️ Recent SSH errors in {lf.name}: {len(errors)}")
|
|
301
|
+
else:
|
|
302
|
+
other_info.append(f"✅ {lf.name}: no recent SSH errors")
|
|
303
|
+
break
|
|
304
|
+
|
|
305
|
+
if other_info:
|
|
306
|
+
console.print(Panel("\n".join(other_info), title="[bold]Additional[/bold]", border_style="blue"))
|
|
307
|
+
|
|
308
|
+
if issues:
|
|
309
|
+
fix_table = Table(title="Issues & Fixes", box=box.ROUNDED, show_lines=True, title_style="bold red")
|
|
310
|
+
fix_table.add_column("Issue", style="yellow", width=25)
|
|
311
|
+
fix_table.add_column("Impact", style="white", width=35)
|
|
312
|
+
fix_table.add_column("Fix Command", style="green", width=55)
|
|
313
|
+
for issue, impact, fix in issues:
|
|
314
|
+
fix_table.add_row(issue, impact, fix)
|
|
315
|
+
console.print(fix_table)
|
|
316
|
+
|
|
317
|
+
fix_script_path = Path("/tmp/ssh_fix.sh")
|
|
318
|
+
script_lines = ["#!/bin/bash", "set -e", "", "# SSH Fix Script - Generated by ssh_debug_linux", f"# {len(issues)} issue(s) to fix", ""]
|
|
319
|
+
for issue, _impact, fix in issues:
|
|
320
|
+
script_lines.append(f"# Fix: {issue}")
|
|
321
|
+
script_lines.append(fix)
|
|
322
|
+
script_lines.append("")
|
|
323
|
+
script_lines.append("echo 'All fixes applied. Re-run ssh_debug_linux to verify.'")
|
|
324
|
+
fix_script_path.write_text("\n".join(script_lines), encoding="utf-8")
|
|
325
|
+
fix_script_path.chmod(0o755)
|
|
326
|
+
|
|
327
|
+
console.print(Panel(f"[bold yellow]⚠️ {len(issues)} issue(s) found[/bold yellow]\n\nFix script generated: [cyan]{fix_script_path}[/cyan]\nRun: [green]sudo bash {fix_script_path}[/green]", title="[bold]Summary[/bold]", border_style="yellow"))
|
|
328
|
+
else:
|
|
329
|
+
conn_info = f"👤 {current_user} 🖥️ {hostname} 🔌 :{ssh_port}"
|
|
330
|
+
if ip_addresses:
|
|
331
|
+
conn_info += f"\n\n[bold]Connect:[/bold] ssh {current_user}@{ip_addresses[0]}"
|
|
332
|
+
console.print(Panel(f"[bold green]✅ All checks passed[/bold green]\n\n{conn_info}", title="[bold]Ready[/bold]", border_style="green"))
|
|
333
|
+
|
|
334
|
+
return results
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
if __name__ == "__main__":
|
|
338
|
+
ssh_debug_linux()
|
|
@@ -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"
|