zuul-jobs/roles/add-build-sshkey/library/sshagent_remove_keys.py
James E. Blair 46389b5187 add-build-sshkey: Remove only the master key
This implements a module to directly interact with the ssh-agent
so that the master key may be removed from the ssh-agent without
removing any per-project keys.

Change-Id: Ife91ad8afa9b41b0e779a832e298aca8d61ae98b
2018-09-05 09:26:35 -07:00

127 lines
3.3 KiB
Python

# Copyright 2018 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import argparse
import os
import socket
import struct
import sys
import re
from ansible.module_utils.basic import AnsibleModule
SSH_AGENT_FAILURE = 5
SSH_AGENT_SUCCESS = 6
SSH_AGENT_IDENTITIES_ANSWER = 12
SSH_AGENTC_REQUEST_IDENTITIES = 11
SSH_AGENTC_REMOVE_IDENTITY = 18
def unpack_string(data):
(l,) = struct.unpack('!i', data[:4])
d = data[4:4 + l]
return (d, data[4 + l:])
def pack_string(data):
ret = struct.pack('!i', len(data))
return ret + data
class Agent(object):
def __init__(self):
path = os.environ['SSH_AUTH_SOCK']
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.sock.connect(path)
def send(self, message_type, contents):
payload = struct.pack('!ib', len(contents) + 1, message_type)
payload += bytearray(contents)
self.sock.send(payload)
def recv(self):
buf = b''
while len(buf) < 5:
buf += self.sock.recv(1)
message_len, message_type = struct.unpack('!ib', buf[:5])
buf = buf[5:]
while len(buf) < message_len - 1:
buf += self.sock.recv(1)
return message_type, buf
def list(self):
self.send(SSH_AGENTC_REQUEST_IDENTITIES, b'')
mtype, data = self.recv()
if mtype != SSH_AGENT_IDENTITIES_ANSWER:
raise Exception("Invalid response to list")
(nkeys,) = struct.unpack('!i', data[:4])
data = data[4:]
keys = []
for i in range(nkeys):
blob, data = unpack_string(data)
comment, data = unpack_string(data)
keys.append((blob, comment))
return keys
def remove(self, blob):
self.send(SSH_AGENTC_REMOVE_IDENTITY, pack_string(blob))
mtype, data = self.recv()
if mtype != SSH_AGENT_SUCCESS:
raise Exception("Key was not removed")
def run(remove):
a = Agent()
keys = a.list()
removed = []
to_remove = re.compile(remove)
for blob, comment in keys:
if not to_remove.match(comment.decode('utf8')):
continue
a.remove(blob)
removed.append(comment)
return removed
def ansible_main():
module = AnsibleModule(
argument_spec=dict(
remove=dict(required=True, type='str')))
removed = run(module.params.get('remove'))
module.exit_json(changed=(removed != []),
removed=removed)
def cli_main():
parser = argparse.ArgumentParser(
description="Remove ssh keys from agent"
)
parser.add_argument('remove', nargs='+',
help='regex matching comments of keys to remove')
args = parser.parse_args()
removed = run(args.remove)
print(removed)
if __name__ == '__main__':
if sys.stdin.isatty():
cli_main()
else:
ansible_main()