Пример #1
0
 def purge_expired_sessions(self):
     import time
     """ A file session contains a data separated by --
     The first part is the hexadecimal representation of the encoded session
     data. Second is the epoch of the last write.
     If current epoch - file epoch is lower than the session life time then
     we need to delete this file.
     """
     logger.debug("File handler looking for expired sessions.")
     self.engine.session_callback.stop()
     logging.debug("Session periodic callback stopped by the file "
                   "handler.")
     purge_count = 0
     purge_hiccup = False
     for dirname, dirnames, filenames in os.walk(self.path):
         for filename in filenames:
             file_path = os.path.join(self.path, filename)
             sess_id = filename.split(".")[0].split("_")[-1]
             timed_data = fs.read(file_path)
             last_write = timed_data.split("--")[1]
             age = int(time.time()) - int(last_write)
             if age > self.life_time:
                 logging.debug("Session %s is expired. Removing file "
                               "from the session path." % sess_id)
                 os.remove(file_path)
                 purge_count += 1
         if purge_count == firenado.conf.session['purge_limit']:
             purge_hiccup = True
             logger.warning(
                 "Expired 500 sessions. Exiting the call and waiting for "
                 "purge hiccup.")
             break
     if purge_hiccup:
         self.engine.set_purge_hiccup()
     else:
         self.engine.set_purge_normal()
     self.engine.session_callback.start()
     logging.debug("Session periodic callback resumed by the file "
                   "handler.")
Пример #2
0
 def test_binary_fs_write(self):
     """ Write in a file using data as bytes. """
     data = b"My text to write.\n"
     fs.write(self.file_to_write_path, data, True)
     self.assertTrue(os.path.exists(self.file_to_write_path))
     self.assertEqual(data, fs.read(self.file_to_write_path, True))
Пример #3
0
 def test_string_fs_write(self):
     """ Write in a file using data as string. """
     data = "My text to write.\n"
     fs.write(self.file_to_write_path, data)
     self.assertTrue(os.path.exists(self.file_to_write_path))
     self.assertEqual(data, fs.read(self.file_to_write_path))
Пример #4
0
 def test_binary_fs_read(self):
     """ Read a file returning bytes as result. """
     expected = b"Do not remove this file.\n"
     value = fs.read(self.file_to_read_path, True)
     self.assertIsInstance(value, bytes)
     self.assertEqual(expected, value)
Пример #5
0
 def test_string_fs_read(self):
     """ Read a file returning string as result. """
     expected = "Do not remove this file.\n"
     value = fs.read(self.file_to_read_path)
     self.assertIsInstance(value, str)
     self.assertEqual(expected, value)
Пример #6
0
def authorize(server, paths, account, domains, method, verbose=False):
    print("Candango Automatoes {}. Manuale replacement.\n\n".format(
        get_version()))

    current_path = paths['current']
    orders_path = paths['orders']
    domains_hash = hashlib.sha256(
        "_".join(domains).encode('ascii')).hexdigest()
    order_path = os.path.join(orders_path, domains_hash)
    order_file = os.path.join(order_path, "order.json".format(domains_hash))

    if not os.path.exists(orders_path):
        if verbose:
            print("Orders path not found creating it at {}."
                  "".format(orders_path))
        os.mkdir(orders_path)
        os.chmod(orders_path, 0o770)
    else:
        if verbose:
            print("Orders path found at {}.".format(orders_path))

    if not os.path.exists(order_path):
        if verbose:
            print("Current order {} path not found creating it at orders "
                  "path.\n".format(domains_hash))
        os.mkdir(order_path)
        os.chmod(order_path, 0o770)
    else:
        if verbose:
            print("Current order {} path found at orders path.\n".format(
                domains_hash))

    method = method
    acme = AcmeV2(server, account)

    try:
        print("Authorizing {}.\n".format(", ".join(domains)))
        # Creating orders for domains if not existent
        if not os.path.exists(order_file):
            if verbose:
                print("  Order file not found creating it.")
            order = create_order(acme, domains, method, order_file)
        else:
            if verbose:
                print("  Found order file. Querying ACME server for current "
                      "status.")
            order = Order.deserialize(fs.read(order_file))
            server_order = acme.query_order(order)
            order.contents = server_order.contents
            update_order(order, order_file)

            if not order.expired and not order.invalid:
                if order.contents['status'] == 'valid':
                    print("  Order is valid and expires at {}. Please run "
                          "the issue "
                          "command.\n".format(order.contents['expires']))
                    print("  {} domain(s) authorized. Let's Encrypt!".format(
                        len(domains)))
                    sys.exit(sysexits.EX_OK)
                else:
                    if verbose:
                        print("    Order still pending and expires "
                              "at {}.\n".format(order.contents['expires']))
            else:
                if order.invalid:
                    print("    WARNING: Invalid order, renewing it.\n    Just "
                          "continue with the authorization when all "
                          "verifications are in place.\n")
                else:
                    print("  WARNING: Expired order. Renewing order.\n")
                os.remove(order_file)
                order = create_order(acme, domains, method, order_file)
                update_order(order, order_file)

        pending_challenges = []

        for challenge in acme.get_order_challenges(order):
            print("  Requesting challenge for {}.".format(challenge.domain))
            if challenge.status == 'valid':
                print("    {} is already authorized until {}.".format(
                    challenge.domain, challenge.expires))
                continue
            else:
                challenge_file = os.path.join(order_path, challenge.file_name)
                if verbose:
                    print("    Creating challenge file {}.\n".format(
                        challenge.file_name))
                fs.write(challenge_file, challenge.serialize().decode())
                pending_challenges.append(challenge)

        # Quit if nothing to authorize
        if not pending_challenges:
            print("\nAll domains are already authorized, exiting.")
            sys.exit(sysexits.EX_OK)

        files = set()
        if method == 'dns':
            print("\n  DNS verification required. Make sure these TXT records"
                  " are in place:\n")
            for challenge in pending_challenges:
                print("    _acme-challenge.{}.  IN TXT  "
                      "\"{}\"".format(challenge.domain, challenge.key))
        elif method == 'http':
            print("\n  HTTP verification required. Make sure these files are "
                  "in place:\n")
            for challenge in pending_challenges:
                token = challenge.contents['token']

                # path sanity check
                assert (token and os.path.sep not in token
                        and '.' not in token)
                files.add(token)
                fs.write(os.path.join(current_path, token), challenge.key)
                print("    http://{}/.well-known/acme-challenge/{}".format(
                    challenge.domain, token))

            print("\n  The necessary files have been written to the current "
                  "directory.\n")
        # Wait for the user to complete the challenges
        input("\nPress Enter to continue.\n")

        # Validate challenges
        done, failed, pending = set(), set(), set()
        for challenge in pending_challenges:
            print("  {}: waiting for verification. Checking in 5 "
                  "seconds.".format(challenge.domain))
            response = acme.verify_order_challenge(challenge, 5, 1)
            if response['status'] == "valid":
                print("  {}: OK! Authorization lasts until {}.".format(
                    challenge.domain, challenge.expires))
                done.add(challenge.domain)
            elif response['status'] == 'invalid':
                print("  {}: {} ({})".format(challenge.domain,
                                             response['error']['detail'],
                                             response['error']['type']))
                failed.add(challenge.domain)
                break
            else:
                print("{}: Pending!".format(challenge.domain))
                pending.add(challenge.domain)
                break

        challenge_file = os.path.join(order_path, challenge.file_name)
        # Print results
        if failed:
            print("  {} domain(s) authorized, {} failed.".format(
                len(done),
                len(failed),
            ))
            print("  Authorized: {}".format(' '.join(done) or "N/A"))
            print("  Failed: {}".format(' '.join(failed)))
            print("  WARNING: The current order will be invalidated. "
                  "Try again.")
            if verbose:
                print("    Deleting invalid challenge file {}.\n".format(
                    challenge.file_name))
            clean_challenge_file(challenge_file)
            os.remove(order_file)
            os.rmdir(order_path)
            if method == 'http':
                print(files)
                clean_http_challenges(files)
            sys.exit(sysexits.EX_FATAL_ERROR)
        else:
            if pending:
                print("  {} domain(s) authorized, {} pending.".format(
                    len(done), len(pending)))
                print("  Authorized: {}".format(' '.join(done) or "N/A"))
                print("  Pending: {}".format(' '.join(pending)))
                print("  Try again.")
                sys.exit(sysexits.EX_CANNOT_EXECUTE)
            else:
                if verbose:
                    print("    Deleting valid challenge file {}.".format(
                        challenge.file_name))
                clean_challenge_file(challenge_file)
                if verbose:
                    print("    Querying ACME server for current status.\n")
                server_order = acme.query_order(order)
                order.contents = server_order.contents
                update_order(order, order_file)
                print("  {} domain(s) authorized. Let's Encrypt!".format(
                    len(done)))
        if method == 'http':
            clean_http_challenges(files)
        sys.exit(sysexits.EX_OK)
    except IOError as e:
        print("A connection or service error occurred. Aborting.")
        raise AutomatoesError(e)
Пример #7
0
def issue(server,
          paths,
          account,
          domains,
          key_size,
          key_file=None,
          csr_file=None,
          output_path=None,
          must_staple=False,
          verbose=False):
    print("Candango Automatoes {}. Manuale replacement.\n\n".format(
        get_version()))

    current_path = paths['current']
    orders_path = paths['orders']
    domains_hash = hashlib.sha256(
        "_".join(domains).encode('ascii')).hexdigest()
    order_path = os.path.join(orders_path, domains_hash)
    order_file = os.path.join(order_path, "order.json".format(domains_hash))

    if not os.path.exists(orders_path):
        print(" ERROR: Orders path not found. Please run before: manuale "
              "authorize {}".format(" ".join(domains)))
        sys.exit(sysexits.EX_CANNOT_EXECUTE)
    else:
        if verbose:
            print("Orders path found at {}.".format(orders_path))

    if verbose:
        print("Searching order file {}.".format(order_file))

    if not os.path.exists(order_path):
        print(" ERROR: Order file not found. Please run before: manuale "
              "authorize {}".format(" ".join(domains)))
        sys.exit(sysexits.EX_CANNOT_EXECUTE)
    else:
        if verbose:
            print("Current order {} path found at orders path.\n".format(
                domains_hash))

    acme = AcmeV2(server, account)
    order = Order.deserialize(fs.read(order_file))
    if order.contents['status'] == "pending":
        if verbose:
            print("Querying ACME server for current status.")
        server_order = acme.query_order(order)
        order.contents = server_order.contents
        update_order(order, order_file)
        if order.contents['status'] in ["pending", "invalid"]:
            print(" ERROR: Order not ready or invalid. Please re-run: manuale"
                  " authorize {}.".format(" ".join(domains)))
            sys.exit(sysexits.EX_CANNOT_EXECUTE)
    elif order.contents['status'] == "invalid":
        print(" ERROR: Invalid order. Please re-run: manuale authorize "
              "{}.".format(" ".join(domains)))
        sys.exit(sysexits.EX_CANNOT_EXECUTE)

    if not output_path or output_path == '.':
        output_path = os.getcwd()

    # Load key if given
    if key_file:
        try:
            with open(key_file, 'rb') as f:
                certificate_key = load_private_key(f.read())
            order.key = export_private_key(certificate_key).decode('ascii')
            update_order(order, order_file)
        except (ValueError, AttributeError, TypeError, IOError) as e:
            print("ERROR: Couldn't read certificate key.")
            raise AutomatoesError(e)
    else:
        certificate_key = None

    # Load CSR or generate
    if csr_file:
        try:
            with open(csr_file, 'rb') as f:
                csr = export_csr_for_acme(load_csr(f.read()))
        except (ValueError, AttributeError, TypeError, IOError) as e:
            print("ERROR: Couldn't read CSR.")
            raise AutomatoesError(e)
    else:
        # Generate key
        if not key_file:
            if order.key is None:
                print("Generating a {} bit RSA key. This might take a "
                      "second.".format(key_size))
                certificate_key = generate_rsa_key(key_size)
                print("  Key generated.")
                order.key = export_private_key(certificate_key).decode('ascii')
                update_order(order, order_file)
                print("  Order updated with generated key.")
            else:
                print("Previous RSA key found in the order. Loading the key.")
                certificate_key = load_private_key(order.key.encode('ascii'))

        csr = create_csr(certificate_key, domains, must_staple=must_staple)

    try:
        logger.info("Requesting certificate issuance...")
        if order.contents['status'] == "ready":
            final_order = acme.finalize_order(order, csr)
            order.contents = final_order
            update_order(order, order_file)
            if final_order['status'] in ["processing", "valid"]:
                if verbose:
                    print("  Order {} finalized. Certificate is being "
                          "issued.".format(domains_hash))
            else:
                print(" ERROR: Order not ready or invalid. Please re-run: "
                      "manuale authorize {}.".format(" ".join(domains)))
                sys.exit(sysexits.EX_CANNOT_EXECUTE)
        elif order.contents['status'] in ["valid", "processing"]:
            print("  Order {} is already processing or valid. Downloading "
                  "certificate.".format(domains_hash))
        else:
            print(" ERROR: Order not ready or invalid. Please re-run: manuale "
                  "authorize {}.".format(" ".join(domains)))
            sys.exit(sysexits.EX_CANNOT_EXECUTE)

        if order.certificate_uri is None:
            if verbose:
                print("  Checking order {} status.".format(domains_hash))
            fulfillment = acme.await_for_order_fulfillment(order)
            if fulfillment['status'] == "valid":
                order.contents = fulfillment
                update_order(order, order_file)
            else:
                print(" ERROR: Order not ready or invalid. Please re-run: "
                      "manuale authorize {}.".format(" ".join(domains)))
                sys.exit(sysexits.EX_CANNOT_EXECUTE)
        else:
            print("  We already know the certificate uri for order {}. "
                  "Downloading certificate.".format(domains_hash))

        result = acme.download_order_certificate(order)

        logger.info("  Certificate downloaded.")
    except IOError as e:
        print("Connection or service request failed. Aborting.")
        raise AutomatoesError(e)

    try:
        certificates = strip_certificates(result.content)
        certificate = load_pem_certificate(certificates[0])

        # Print some neat info
        print("  Expires: {}".format(
            certificate.not_valid_after.strftime(EXPIRATION_FORMAT)))
        print("   SHA256: {}".format(
            binascii.hexlify(certificate.fingerprint(
                SHA256())).decode('ascii')))

        # Write the key, certificate and full chain
        os.makedirs(output_path, exist_ok=True)
        cert_path = os.path.join(output_path, domains[0] + '.crt')
        chain_path = os.path.join(output_path, domains[0] + '.chain.crt')
        intermediate_path = os.path.join(output_path,
                                         domains[0] + '.intermediate.crt')
        key_path = os.path.join(output_path, domains[0] + '.pem')

        if order.key is not None:
            with open(key_path, 'wb') as f:
                os.chmod(key_path, 0o600)
                f.write(order.key.encode('ascii'))
                print("\n  Wrote key to {}".format(f.name))

        with open(cert_path, 'wb') as f:
            f.write(export_pem_certificate(certificate))
            print("  Wrote certificate to {}".format(f.name))

        with open(chain_path, 'wb') as f:
            f.write(export_pem_certificate(certificate))
            if len(certificates) > 1:
                f.write(
                    export_pem_certificate(
                        load_pem_certificate(certificates[1])))
            print("  Wrote certificate with intermediate to {}".format(f.name))

        if len(certificates) > 1:
            with open(intermediate_path, 'wb') as f:
                f.write(
                    export_pem_certificate(
                        load_pem_certificate(certificates[1])))
                print("  Wrote intermediate certificate to {}".format(f.name))
    except IOError as e:
        print("  ERROR: Failed to write certificate or key. Going to print "
              "them for you instead.")
        if order.key is not None:
            for line in order.key.split('\n'):
                print("ERROR: {}".format(line))
        for line in export_pem_certificate(certificate).decode('ascii').split(
                '\n'):
            print("ERROR: {}".format(line))
        raise AutomatoesError(e)
Пример #8
0
def user_file_exists_at(context, certificate_path):
    real_certificate_path = get_absolute_path(certificate_path)
    context.tester.assertTrue(os.path.exists(real_certificate_path))
    context.tester.assertTrue(os.path.isfile(real_certificate_path))
    context.certificate = fs.read(real_certificate_path)
Пример #9
0
 def read_stored_session(self, session_id):
     import binascii
     session_file = os.path.join(self.path, self.__get_filename(session_id))
     timed_data = fs.read(session_file)
     return binascii.unhexlify(timed_data.split("--")[0])