#!/usr/bin/env python3 # import configparser import json import logging import os import re import subprocess import sys import syslog # class SSHConfiger: # _action_interface = None _action_command = None # _config_file_path = None _targets = None _config = None # def __init__(self, action_interface, action_command, config_file_path): # Grab args self._action_interface = action_interface self._action_command = action_command self._config_file_path = config_file_path # def log(self, s): # print ("[SSHConfiger]", s) syslog.syslog("[SSHConfiger] " + s) # def complain(self, s): # syslog.syslog(syslog.LOG_ERR, "[SSHConfiger] " + s) print("[SSHConfiger]", s, file=sys.stderr) # def die(self, s): # self.complain(s) raise Exception(s) def quit(self, s): # self.log("Quitting because: " + s) sys.exit(0) # def run(self): # self.log("Running for interface \""+ self._action_interface +"\": " + self._action_command) # Only run if an interface is coming up if ( self._action_command != "up" ): self.quit("We don't need to run for action command: " + str(self._action_command)) # Parse the config self.parse_config() # Grab the specified ssh directory if ( "ssh_dir" not in self._config.keys() ): self.die("Config file needs key \"ssh_dir\" inside \"config\" section") ssh_dir = self._config["ssh_dir"] self.log("SSH Dir: " + str(ssh_dir)) # Determine which ssh config file we should use ssh_config_name = self.determine_ssh_config_name() self.log("Determined ssh config name: " + str(ssh_config_name)) if not ssh_config_name: self.die("Unable to determine appropriate ssh config name; Quitting") # Make paths ssh_config_path_link = os.path.join(ssh_dir, "config") ssh_config_path_target = os.path.join(ssh_dir, ssh_config_name) self.log("Selecting source config file \"" + ssh_config_path_target + "\" for link \"" + ssh_config_path_link + "\"") # Don't run unless current ssh config is a symlink or not a file (for safety) self.require_symlink_or_none(ssh_config_path_link) # Set the symlink try: os.unlink(ssh_config_path_link) except: pass os.symlink(ssh_config_path_target, ssh_config_path_link) # self.log("Finished") # def require_symlink_or_none(self, file_path): # if ( not os.path.isfile(file_path) or os.path.islink(file_path) ): return True # self.die("For safety, we cannot continue if the target link exists and is a file (" + file_path + ")") # def determine_ssh_config_name(self): # self.log("Attempting to determine SSH config name") # Check each section found_ssh_config_name = None for section_name in self._targets: # section = self._targets[section_name] self.log("Examining section: " + str(section_name)) # Don't examine default if anything is picked already if section_name == "default" and found_ssh_config_name: self.log("Skipping default section because we've already found at least one match: " + str(found_ssh_config_name)) continue # Check the interface interface_matched = False if ( # Matches, if current interface found in adapters self._action_interface in section["adapters"] # Can also match if we're in section "default" or section_name == "default" ): interface_matched = True if not interface_matched: self.log("Section \"" + str(section_name) + "\" didn't match any interfaces; Skipping") continue # Grab the SSID this adapter is currently connected to interface_ssid = self.get_interface_ssid(self._action_interface) if not interface_ssid: self.log("Interface \"" + str(interface_ssid) + "\" isn't connected to anything ... ") self.log("Interface \"" + str(self._action_interface) + "\" is currently connected to: \"" + str(interface_ssid) + "\"") # Must also match at least one SSID, # OR we're in the default section if interface_ssid not in section["ssids"] and section_name != "default": self.log("Did not find SSID \"" + interface_ssid + "\" in section ssids: " + str(section["ssids"])) continue # Found a match! found_ssh_config_name = section["ssh_config_name"] self.log("Found matching ssh config name: " + str(found_ssh_config_name)) # Didn't find anything? Go default ... if (not found_ssh_config_name): if ( "default" in self._targets.keys() ): if ( "ssh_config_name" in self._targets["default"].keys() ): found_ssh_config_name = self._targets["default"]["ssh_config_name"] self.log("No matches found; Defaulting to:" + found_ssh_config_name) # return found_ssh_config_name # def get_interface_ssid(self, interface): # p = subprocess.Popen(["nmcli", "dev", "show", interface], stdout=subprocess.PIPE) (stdout, stderr) = p.communicate() stdout = stdout.decode() # r = re.compile("""^GENERAL.CONNECTION:\s+(?P[^\s].+)$""", re.MULTILINE) match = r.search(stdout) return match.group("ssid") # def parse_config(self): # self.log("Parsing config: " + self._config_file_path) parser = configparser.ConfigParser() parser.read( self._config_file_path ) # Attempt to grab global config if ( "config" not in parser.sections() ): self.die("config section not found") config = parser["config"] # Targets targets = {} for section in parser.sections(): # Skip the config section if ( section == "config" ): continue # self.log("Parsing config target: \"" + section + "\"") target = { "adapters" : [], "ssids" : [] } # Adapters if ( parser.has_option(section, "adapters") ): target["adapters"] = json.loads( parser[section]["adapters"] ) if ( parser.has_option(section, "adapter") ): target["adapters"].append( parser[section]["adapter"] ) # SSIDs if ( parser.has_option(section, "ssids") ): target["ssids"] = json.loads( parser[section]["ssids"] ) if ( parser.has_option(section, "ssid") ): target["ssids"].append( parser[section]["ssid"] ) # ssh_config_name if ( parser.has_option(section, "ssh_config_name") ): target["ssh_config_name"] = parser[section]["ssh_config_name"] else: raise Exception("ssh_config_name key missing from section: " + section) # targets[section] = target self.log("Parsed config for config target \"" + section + "\": " + str(target)) # self._config = config self._targets = targets return True # Main Entry if (__name__ == "__main__"): # Script name script_name = sys.argv[0] # Network Manager action info action_interface = sys.argv[1] action_command = sys.argv[2] # Config file config_file_path = sys.argv[3] # configger = SSHConfiger(action_interface, action_command, config_file_path) configger.run()