From 05cf3b38400f75c92b02e0bdfdfab263cf49ac3c Mon Sep 17 00:00:00 2001 From: James Bhattarai Date: Thu, 9 Apr 2026 14:49:44 +0545 Subject: [PATCH] Feat: added --install-known-hosts & Fix: Permissons for ssh-cert This allows users to copy the Host CA Pub key hosts directly into their ~/.ssh/known_hosts Implemented chmod 600 for /tmp/ssh-cert (CERT_FILE_PATH) --- client/gatehouse-cli.py | 60 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/client/gatehouse-cli.py b/client/gatehouse-cli.py index 2060c5a..bc96ab3 100755 --- a/client/gatehouse-cli.py +++ b/client/gatehouse-cli.py @@ -279,6 +279,12 @@ def request_certificate(): json_result = response.json().get('data', response.json()) with open(CERT_FILE_PATH, 'w') as f: f.write(json_result['certificate']) + + try: + os.chmod(CERT_FILE_PATH, 0o600) + except OSError: + pass + logger.info(f"Certificate signed successfully, located at {CERT_FILE_PATH}") logger.info(f"Valid for principals: {', '.join(json_result.get('principals', principals))}") logger.info("You can login to your destination server with the following command") @@ -455,6 +461,51 @@ def checkCert(): logger.warning("Certificate is not valid, renewal required") return 1 +def install_known_hosts(): + """Fetch Host CA from the upstream server and install it into ~/.ssh/known_hosts.""" + try: + response = requests.get(f"{SIGN_URL}/api/v1/ssh/ca/public-key?ca_type=host", headers=auth_headers()) + if response.status_code != 200: + logger.error(f"Failed to fetch host CA public key: {response.status_code} - {response.text}") + exit(1) + + ca_data = response.json().get('data', {}) + public_key = ca_data.get('public_key', '').strip() + if not public_key: + logger.error("No public key found in the response.") + exit(1) + + known_hosts_path = os.path.expanduser("~/.ssh/known_hosts") + ssh_dir = os.path.dirname(known_hosts_path) + + if not os.path.exists(ssh_dir): + os.makedirs(ssh_dir, mode=0o700) + + # Standard format for OpenSSH cert-authority + entry = f"@cert-authority * {public_key}\n" + + # Check if already present + if os.path.exists(known_hosts_path): + with open(known_hosts_path, 'r') as f: + content = f.read() + if public_key in content: + logger.info("Host CA public key is already in ~/.ssh/known_hosts. No changes made.") + return + + with open(known_hosts_path, 'a') as f: + f.write(entry) + + try: + os.chmod(known_hosts_path, 0o600) + except OSError: + pass # May not have permission to chmod if owned by root, but let's try + + logger.info(f"Successfully installed Host CA public key to {known_hosts_path} for all hosts (*)") + except Exception as e: + logger.error(f"Error during Host CA installation: {e}") + exit(1) + + if __name__ == "__main__": parser = argparse.ArgumentParser(description='Sign an SSH key via a web service') parser.add_argument("-k", "--ssh-key", type=argparse.FileType('rb'), dest="sshkeyfile", help="Add an SSH Public Key to your user profile in gatehouse") @@ -465,11 +516,12 @@ if __name__ == "__main__": parser.add_argument("--clear-cache", action='store_true', default=False, help="Remove the cached authentication token") parser.add_argument("--remove-key", nargs='?', const='', metavar='KEY_ID', help="Remove an SSH key from your profile. Omit KEY_ID to pick interactively.") parser.add_argument("--list-keys", action='store_true', default=False, help="List SSH keys in your profile") + parser.add_argument("--install-known-hosts", action='store_true', default=False, help="Fetch Host CA public key and install into ~/.ssh/known_hosts") args = parser.parse_args() if not (args.check_cert or args.request_cert or args.add_key or args.clear_cache - or args.remove_key is not None or args.list_keys): - parser.error("At least one of --check-cert, --request-cert, --add-key, --list-keys, --remove-key, or --clear-cache must be provided.") + or args.remove_key is not None or args.list_keys or args.install_known_hosts): + parser.error("At least one of --check-cert, --request-cert, --add-key, --list-keys, --remove-key, --clear-cache, or --install-known-hosts must be provided.") # Retrieve SSH key from environment variables if not provided via CLI @@ -518,6 +570,10 @@ if __name__ == "__main__": add_ssh_key(ssh_key_file) exit(0) + if args.install_known_hosts: + request_token() + install_known_hosts() + exit(0) if args.request_cert: request_token()