# AMI's are "endpoint aware", i.e. specific to a region.
EC2_ENDPOINT = "https://ec2.us-east-1.amazonaws.com/"

# Interval between requests in seconds
POLLING_INTERVAL = 5

# Total amount time spend querying in seconds.
WAIT_TIME = 15


def eb(f):
    print >> sys.stderr, "Error returned from wait_for_EC2_sshfp invocation!"
    print >> sys.stderr, f


def printer(x):
    print "x is %s" % (x,)
    return x


d = wait_for_EC2_sshfp(
    ec2accesskeyid, ec2secretkey, EC2_ENDPOINT, POLLING_INTERVAL, WAIT_TIME, sys.stdout, sys.stderr, instance_id
)

d.addCallbacks(printer, eb)
d.addCallbacks(lambda ign: os._exit(0), lambda ign: os._exit(1))
from twisted.internet import reactor

reactor.run()
ec2secretkey = FilePath(ec2secretpath).getContent().strip()

# AMI's are "endpoint aware", i.e. specific to a region.
EC2_ENDPOINT = 'https://ec2.us-east-1.amazonaws.com/'

# Interval between requests in seconds
POLLING_INTERVAL = 5

# Total amount time spend querying in seconds.
WAIT_TIME = 15


def eb(f):
    print >> sys.stderr, "Error returned from wait_for_EC2_sshfp invocation!"
    print >> sys.stderr, f


def printer(x):
    print "x is %s" % (x, )
    return x


d = wait_for_EC2_sshfp(ec2accesskeyid, ec2secretkey, EC2_ENDPOINT,
                       POLLING_INTERVAL, WAIT_TIME, sys.stdout, sys.stderr,
                       instance_id)

d.addCallbacks(printer, eb)
d.addCallbacks(lambda ign: os._exit(0), lambda ign: os._exit(1))
from twisted.internet import reactor
reactor.run()
def verify_and_store_serverssh_pubkey(ec2accesskeyid, ec2secretkey, endpoint_uri, pub_ipaddress,
                                      polling_interval, total_wait_time, stdout, stderr,
                                      instance_id):
    """
    Theory of Operation:  What the function intends.
    When a new ssh connection is established it is possible for a recipient-other-than-the-intended
    (a M.an I.n T.he M.iddle) to offer a counterfeit encryption key to the Connector.  This would
    enable the MITM to read the contents of the ssh traffic the Connector intended to transmit
    confidentially.

    If the Connector has an alternate connection (side-channel) to the intended recipient the
    Connector can check that the encryption key presented to it, is the same in both channels.
    This means that an attacker would have to control both channels to undetectably deceive the
    Connector.

    Theory of Operation:  How the function accomplishes its intention.
    When Least Authority (the "Connector") generates a new EC2 instance using the AWS REST interface
    it communicates over an https channel.  This means that Least Authority _COULD_ be assured by a
    'Certificate Authority' that 'AWS' is the initial recipient of Least Authority's confidential
    requests.

    Subsequent to initial generation, Least Authority uses ssh to communicate with its EC2s.

    When Least Authority establishes an initial ssh connection with an EC2 instance, it has
    generated a second, unauthenticated channel.  Least Authority uses the first, over-https,
    channel to obtain a copy of the ssh public key fingerprint that AWS asserts will be used
    by the new EC2.

    (NOTE: Although 'Least Authority' uses an https endpoint, i.e. protocol, certificate checking is
    not yet implemented so we do _not_ yet have cryptographic assurance of the authenticity of the
    AWS REST API provider.)

    The User that owns the Least Authority process then obtains the over-https public key
    fingerprint, uses it to check the pubkey offered over the ssh channel, and store it in its
    .ssh/known_hosts file, along with the hashed identity of the host. (This is not a 'security'
    check since, the over https fingerprint is not authenticated.)

    @param ec2accesskeyid:  An identifier AWS uses to lookup our credentials.
    @param ec2secretkey:    The symmetric secret we use to sign our queries.
    @param endpoint_uri:    An endpoint is a URL that is the entry point for a web service.
    @param addressparser:   An object that is referred to by an EC2 client and used to parse
    data returned from queries.
    @param polling_interval: An int that sets how long to wait between repeated queries.
    @param total_wait_time:  The total time allotted to a query type (same for both fingerprint
    and IP retrievals).
    @param instance_id:      An AWS internal id of an EC2.
    """
    d = wait_for_EC2_sshfp(ec2accesskeyid, ec2secretkey, endpoint_uri, polling_interval,
                                   total_wait_time, stdout, stderr, instance_id)

    def _got_fingerprintfromconsole(fingerprint):
        fingerprint_from_AWS = fingerprint
        d1 = wait_for_and_store_pubkeyfp_from_keyscan(pub_ipaddress, polling_interval,
                                                      total_wait_time, stdout)

        def _verifyfp_and_write_pubkey( (fingerprint_from_keyscan, hashed_pubkey) ):
            if fingerprint_from_AWS != fingerprint_from_keyscan:
                raise PublicKeyMismatch()
            print >>stderr, "The ssh public key on the server has fingerprint: %s" % (fingerprint_from_keyscan,)
            known_hosts_filepath = FilePath(os.path.expanduser('~')).child('.ssh').child('known_hosts')
            if not known_hosts_filepath.exists():
                known_hosts_filepath.create()
            known_hosts = known_hosts_filepath.getContent().rstrip('\n') + '\n'
            new_known_hosts = known_hosts + hashed_pubkey
            known_hosts_filepath.setContent(new_known_hosts)

        d1.addCallback(_verifyfp_and_write_pubkey)
        return d1

    d.addCallback(_got_fingerprintfromconsole)
    return d
Example #4
0
def verify_and_store_serverssh_pubkey(ec2accesskeyid, ec2secretkey, endpoint_uri, pub_ipaddress,
                                      polling_interval, total_wait_time, stdout, stderr,
                                      instance_id):
    """
    Theory of Operation:  What the function intends.
    When a new ssh connection is established it is possible for a recipient-other-than-the-intended
    (a M.an I.n T.he M.iddle) to offer a counterfeit encryption key to the Connector.  This would
    enable the MITM to read the contents of the ssh traffic the Connector intended to transmit
    confidentially.

    If the Connector has an alternate connection (side-channel) to the intended recipient the
    Connector can check that the encryption key presented to it, is the same in both channels.
    This means that an attacker would have to control both channels to undetectably deceive the
    Connector.

    Theory of Operation:  How the function accomplishes its intention.
    When Least Authority (the "Connector") generates a new EC2 instance using the AWS REST interface
    it communicates over an https channel.  This means that Least Authority _COULD_ be assured by a
    'Certificate Authority' that 'AWS' is the initial recipient of Least Authority's confidential
    requests.

    Subsequent to initial generation, Least Authority uses ssh to communicate with its EC2s.

    When Least Authority establishes an initial ssh connection with an EC2 instance, it has
    generated a second, unauthenticated channel.  Least Authority uses the first, over-https,
    channel to obtain a copy of the ssh public key fingerprint that AWS asserts will be used
    by the new EC2.

    (NOTE: Although 'Least Authority' uses an https endpoint, i.e. protocol, certificate checking is
    not yet implemented so we do _not_ yet have cryptographic assurance of the authenticity of the
    AWS REST API provider.)

    The User that owns the Least Authority process then obtains the over-https public key
    fingerprint, uses it to check the pubkey offered over the ssh channel, and store it in its
    .ssh/known_hosts file, along with the hashed identity of the host. (This is not a 'security'
    check since, the over https fingerprint is not authenticated.)

    @param ec2accesskeyid:  An identifier AWS uses to lookup our credentials.
    @param ec2secretkey:    The symmetric secret we use to sign our queries.
    @param endpoint_uri:    An endpoint is a URL that is the entry point for a web service.
    @param addressparser:   An object that is referred to by an EC2 client and used to parse
    data returned from queries.
    @param polling_interval: An int that sets how long to wait between repeated queries.
    @param total_wait_time:  The total time allotted to a query type (same for both fingerprint
    and IP retrievals).
    @param instance_id:      An AWS internal id of an EC2.
    """
    d = wait_for_EC2_sshfp(ec2accesskeyid, ec2secretkey, endpoint_uri, polling_interval,
                                   total_wait_time, stdout, stderr, instance_id)

    def _got_fingerprintfromconsole(fingerprint):
        fingerprint_from_AWS = fingerprint
        d1 = wait_for_and_store_pubkeyfp_from_keyscan(pub_ipaddress, polling_interval,
                                                      total_wait_time, stdout)

        def _verifyfp_and_write_pubkey( (fingerprint_from_keyscan, hashed_pubkey) ):
            if fingerprint_from_AWS != fingerprint_from_keyscan:
                raise PublicKeyMismatch()
            print >>stderr, "The ssh public key on the server has fingerprint: %s" % (fingerprint_from_keyscan,)
            known_hosts_filepath = FilePath(os.path.expanduser('~')).child('.ssh').child('known_hosts')
            if not known_hosts_filepath.exists():
                known_hosts_filepath.create()
            known_hosts = known_hosts_filepath.getContent().rstrip('\n') + '\n'
            new_known_hosts = known_hosts + hashed_pubkey
            known_hosts_filepath.setContent(new_known_hosts)

        d1.addCallback(_verifyfp_and_write_pubkey)
        return d1

    d.addCallback(_got_fingerprintfromconsole)
    return d