Streamline Your SSH Configurations: A Journey into Developing an SSH Profile Switcher

Dhiman Seal
6 min readJun 3, 2023

--

Streamline Your SSH Configurations: A Journey into Developing an SSH Profile Switcher
Streamline Your SSH Configurations: A Journey into Developing an SSH Profile Switcher

In the world of software development and system administration, managing multiple SSH configurations can be a challenging task. Whether you switch between work and personal environments or juggle various projects, activating the right SSH profile quickly and efficiently is crucial.

In this article, I will take you on a journey through the development of an SSH profile switcher script and explore how it simplifies the management of SSH configurations. I’ll also provide a comprehensive guide on how to use the script effectively.

The Inspiration and Challenges

The need for an SSH profile switcher arose from the desire to streamline SSH configurations while maintaining security and convenience. Manually switching between SSH profiles and remembering the appropriate keys for each environment can lead to errors and inefficiencies.

To address these challenges, I embarked on a development journey to create a versatile script that automates the process of activating a specific SSH profile while disabling others.

Using the SSH Profile Switcher Script

To effectively use the SSH profile switcher script, follow these steps:

  1. Ensure you have Python 3 installed for the Python script, or Golang installed for the Go script.
  2. Set up your SSH profiles with their respective private key files.
  3. Clone or download the SSH profile switcher repository.
  4. Navigate to the repository directory.
  5. Change the PROFILE_MAP/profileMap in the relevant script you want to use. Add the SSH profile names (can be anything) and the corresponding the exact path where the private key of this profile is located on my system.
Setting up the static profile map config in Python and Go respectively

You can then begin using the profile names to switch SSH profiles on the go as needed.

Python Script Usage

Open a terminal and execute the following command:

python __main__.py -p <profile_name>

Replace <profile_name> with the name of the profile you want to activate.

Golang Script Usage

Open a terminal and execute the following command:

go run main.go <profile_name>

Replace <profile_name> with the name of the profile you want to activate.

Switched to work SSH profile Successfully (in Golang)
Switched to work SSH profile Successfully (in Golang)

The Development Journey

Find the complete Open-Source code for SSH Profile Switcher at Github: https://github.com/Dhi13man/change_ssh_profiles/
Find the complete Open-Source code at Github: https://github.com/Dhi13man/change_ssh_profiles/

Choosing the Right Language: Python and Go

During the development process, we considered different programming languages to implement the SSH profile switcher. Ultimately, I settled on two popular choices: Python and Go.

Both languages offer robust support for system operations and provide the necessary tools to interact with SSH configurations, although Golang will perform faster, as expected.

Why both? To support people who have not just python or Golang, but any of the two languages installed on their system.

Implementing the Python Solution

I began by developing the SSH profile switcher script in Python.

I introduced the imports and the static data which this script needs, as constants. This ended up mostly being PROFILE_MAP: the map of SSH profile name (can be anything) to the exact path where the private key of this profile is located on my system.


from argparse import ArgumentParser
from subprocess import call, DEVNULL
import sys
from os import environ

HOME_DIR: str = environ["HOME"]
PROFILE_MAP: dict = {
"work": f"{HOME_DIR}/.ssh/work_rsa",
"personal": f"{HOME_DIR}/.ssh/id_rsa",
}

Leveraging the argparse module and creating a wrapper over it, I created a command-line interface that allows users to specify the profile they want to activate.

class SshConfigureArgParseWrapper:
"""
Wrapper class for the argparse module.
"""

def __init__(self) -> None:
self.parser = ArgumentParser()
self.parser.add_argument(
"-p",
"--profiles",
help="Names of the profiles to activate. Valid values are: " +
", ".join(PROFILE_MAP.keys()) + ".",
default=["None"],
nargs="+"
)

def get_arg_profile(self) -> list:
"""
Get the profile name from the command line arguments.
"""
return self.parser.parse_args().profiles

def print_help(self) -> None:
"""
Print the help message.
"""
self.parser.print_help()

I utilized the subprocess module to interact with the ssh-add and ssh-agent CLIs, enabling us to disable other profiles and activate the selected one seamlessly.

if __name__ == "__main__":
# Create an instance of the argparse wrapper
argparse_wrapper: SshConfigureArgParseWrapper = SshConfigureArgParseWrapper()
profiles: list = argparse_wrapper.get_arg_profile()
for profile in profiles:
if profile not in PROFILE_MAP:
print("Invalid profile name: " + profile)
argparse_wrapper.print_help()
sys.exit(1)

# Disable all other SSH profiles.
for path in PROFILE_MAP.values():
call(
["ssh-add", "-d", path],
stderr=DEVNULL
)

# Enable the specified SSH profile.
for profile in profiles:
# Get the path to the SSH profile.
ssh_profile_path: str = PROFILE_MAP[profile]
call(["ssh-add", "--apple-use-keychain", ssh_profile_path])

call(["ssh-agent", "-s"])

Introducing the Golang Implementation

Analogous to the Python implementation, I proceeded to the Golang implementation, starting with the introduction of imports and constants.

This ended up mostly being profileMap: the map of SSH profile name (can be anything) to the exact path where the private key of this profile is located on my system and the various CLI Commands to manipulate SSH configs in the system.

import (
"fmt"
"os"
"os/exec"
)

var (
// The name of the environment variable that stores the path to the user's home directory.
homeDirEnvVariable string = os.Getenv("HOME")

// A map of SSH profile names to their absolute paths.
profileMap map[string]string = map[string]string{
"work": homeDirEnvVariable + "/.ssh/work_rsa",
"personal": homeDirEnvVariable + "/.ssh/id_rsa",
}

// The command to add an SSH profile to the ssh-agent.
sshAddCmd string = "ssh-add"

// The command to restart the ssh-agent.
sshAgentCmd string = "ssh-agent"

// The argument to use the macOS keychain with ssh-add.
sshAddUseKeychainArg string = "--apple-use-keychain"
)

Then we need to get the command line argument of the SSH profiles to be used.

// Get the profile names from the command line arguments.
profiles := os.Args[1:]
for _, profile := range profiles {
if _, ok := profileMap[profile]; !ok {
fmt.Println("Invalid profile name: " + profile)
fmt.Println("Valid profiles are: " + fmt.Sprintf("%v", profileMap))
os.Exit(1)
}
}

Utilizing the os/exec module, we can start making calls to the ssh-add and ssh-agent CLIs, enabling us to disable other profiles and activate the selected one seamlessly.

// Disable all other SSH profiles. Ignore exit code 1, which means the
// profile is not currently enabled.
for _, path := range profileMap {
sshAddCmdOutput := exec.Command(sshAddCmd, "-d", path)
sshAddCmdOutput.Run()
}

// Enable the specified SSH profile.
for _, profile := range profiles {
// Get the path to the SSH profile.
sshProfilePath := profileMap[profile]
sshAddArgs := []string{sshAddUseKeychainArg, sshProfilePath}
sshAddCmdOutput := exec.Command(sshAddCmd, sshAddArgs...)
if err := sshAddCmdOutput.Run(); err != nil {
fmt.Println("Error while enabling profile: " + err.Error())
os.Exit(1)
}
}

// Restart the ssh-agent.
sshAgentCmd := exec.Command(sshAgentCmd, "-s")
if err := sshAgentCmd.Run(); err != nil {
fmt.Println("Error while restarting ssh-agent: " + err.Error())
os.Exit(1)
}
fmt.Printf("SSH profiles %s activated successfully.", profiles)

Conclusion

The journey of developing an SSH profile switcher script has provided us with a powerful tool to simplify SSH configuration management. By automating the process of activating a specific SSH profile while disabling others, we’ve significantly reduced the chances of errors and improved overall productivity.

Whether you choose the Python or Go implementation, integrating the script into your workflow will streamline your SSH configurations and enhance your development experience.

Remember to regularly update and maintain your SSH profiles to ensure optimal security and efficiency. With the SSH profile activator script in your toolkit, you’ll effortlessly navigate between different environments and projects, focusing on what matters most: your work.

Happy coding and secure SSH sessions!

Source Code on GitHub

Buy me a coffee at: https://www.buymeacoffee.com/dhi13man
dhi13man is developing Software, Apps, Libraries, Open Source Contributions etc. (buymeacoffee.com)

#python #golang #python3 #go #ssh #automation

--

--

Dhiman Seal

Head Empty. Just Vibes. Attempting to write articles about tech, on my journey to be THE KING OF SOFTWARE ENGINEERING and make people's lives easier (I guess)