Ejemplo n.º 1
0
 def download_ssh_pubkey(self, ec2_keypair):
     try:
         bucket = self.s3.get_bucket(self.s3_bucket_name)
         s3_entry = S3Key(bucket)
         s3_entry.key = self.ssh_pubkey_s3_key_prefix + ec2_keypair.fingerprint
         ssh_pubkey = s3_entry.get_contents_as_string()
     except S3ResponseError as e:
         if e.status == 404:
             raise UserError(
                 "There is no matching SSH pub key stored in S3 for EC2 key pair %s. Has "
                 "it been registered, e.g using the cgcloud's register-key command?"
                 % ec2_keypair.name)
         else:
             raise
     fingerprint_len = len(ec2_keypair.fingerprint.split(':'))
     if fingerprint_len == 20:  # 160 bit SHA-1
         # The fingerprint is that of a private key. We can't get at the private key so we
         # can't verify the public key either. So this is inherently insecure. However,
         # remember that the only reason why we are dealing with n EC2-generated private
         # key is that the Jenkins' EC2 plugin expects a 20 byte fingerprint. See
         # https://issues.jenkins-ci.org/browse/JENKINS-20142 for details. Once that issue
         # is fixed, we can switch back to just using imported keys and 16-byte fingerprints.
         pass
     elif fingerprint_len == 16:  # 128 bit MD5
         fingerprint = ec2_keypair_fingerprint(ssh_pubkey)
         if ec2_keypair.fingerprint != fingerprint:
             raise UserError(
                 "Fingerprint mismatch for key %s! Expected %s but got %s. The EC2 keypair "
                 "doesn't match the public key stored in S3." %
                 (ec2_keypair.name, ec2_keypair.fingerprint, fingerprint))
     return ssh_pubkey
Ejemplo n.º 2
0
    def register_ssh_pubkey(self, ec2_keypair_name, ssh_pubkey, force=False):
        """
        Import the given OpenSSH public key  as a 'key pair' into EC2.

        There is no way to get to the actual public key once it has been imported to EC2.
        Openstack lets you do that and I don't see why Amazon decided to omit this functionality.
        To work around this, we store the public key in S3, identified by the public key's
        fingerprint. As long as we always check the fingerprint of the downloaded public SSH key
        against that of the EC2 keypair key, this method is resilient against malicious
        modifications of the keys stored in S3.

        :param ec2_keypair_name: the desired name of the EC2 key pair

        :param ssh_pubkey: the SSH public key in OpenSSH's native format, i.e. format that is used in ~/
        .ssh/authorized_keys

        :param force: overwrite existing EC2 keypair of the given name
        """
        fingerprint = ec2_keypair_fingerprint(ssh_pubkey,
                                              reject_private_keys=True)
        ec2_keypair = self.ec2.get_key_pair(ec2_keypair_name)
        if ec2_keypair is not None:
            if ec2_keypair.name != ec2_keypair_name:
                raise AssertionError("Key pair names don't match.")
            if ec2_keypair.fingerprint != fingerprint:
                if force:
                    self.ec2.delete_key_pair(ec2_keypair_name)
                    ec2_keypair = None
                else:
                    raise UserError(
                        "Key pair %s already exists in EC2, but its fingerprint %s is "
                        "different from the fingerprint %s of the key to be imported. Use "
                        "the force option to overwrite the existing key pair."
                        % (ec2_keypair.name, ec2_keypair.fingerprint,
                           fingerprint))

        if ec2_keypair is None:
            ec2_keypair = self.ec2.import_key_pair(ec2_keypair_name,
                                                   ssh_pubkey)
        assert ec2_keypair.fingerprint == fingerprint

        self.upload_ssh_pubkey(ssh_pubkey, fingerprint)
        self.__publish_key_update_agent_message()
        return ec2_keypair
Ejemplo n.º 3
0
 def resolve_me(self, s, drop_hostname=True):
     placeholder = self.current_user_placeholder
     if placeholder in s:
         try:
             me = os.environ['CGCLOUD_ME']
         except KeyError:
             me = self.iam_user_name
         if not me:
             raise UserError(
                 "Can't determine current IAM user name. Be sure to put valid AWS credentials "
                 "in ~/.boto or ~/.aws/credentials. For details, refer to %s. On an EC2 "
                 "instance that is authorized via IAM roles, you can set the CGCLOUD_ME "
                 "environment variable (uncommon)." %
                 'http://boto.readthedocs.org/en/latest/boto_config_tut.html')
         if drop_hostname:
             me = self.drop_hostname(me)
         return s.replace(placeholder, me)
     else:
         return s