Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge develop to master #139

Merged
merged 39 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
82084e3
Bump requests from 2.28.1 to 2.31.0 in /requirements
dependabot[bot] Jun 19, 2023
47d34c7
Merge pull request #123 from candango/dependabot/pip/requirements/req…
piraz Jun 19, 2023
5ab5887
chore(build): package project using the build module
piraz Aug 7, 2023
4bb3843
build: release 0.9.11
piraz Aug 7, 2023
c972ba1
Added arg output_filename to cli issue command
strautvetter Jul 25, 2023
d56bb1a
build: release 0.9.12
piraz Aug 7, 2023
52e6353
Merge branch 'master' into develop
piraz Jan 18, 2024
d126736
build(requirements): set peasant or greater or equal 0.6
piraz Mar 28, 2024
a986148
docs: remove broken requirements badge
piraz Mar 29, 2024
83bcf55
Merge branch 'develop' of github.com:candango/automatoes into develop
piraz Mar 29, 2024
1cf491f
build(deps): bump peasant to 0.7.3
piraz Jun 21, 2024
9c28ba4
build(deps): bump taskio to 0.0.7
piraz Jun 21, 2024
cd4b27e
build: remove python 3.7 and add 3.12 to the build
piraz Jun 21, 2024
898cf8f
build(deps): bump actions/checkout to v4
piraz Jun 21, 2024
0e6736f
build(deps): bump actions/setup-python to v5
piraz Jun 21, 2024
f8df7c6
build(deps): add tornado tests requirements
piraz Jun 21, 2024
6a16a7a
refactor(protocol): implement new nonce to acmev2 peasant
piraz Jun 21, 2024
952f910
fix(tests): fix AcmeRequestsTransport importing
piraz Jun 21, 2024
5876d3d
build(deps): bump minimal pesant to 0.7.4
piraz Jun 24, 2024
a4f4323
build(tests): bump actions/setup-go to v5
piraz Jun 30, 2024
fe8a389
Bump tornado from 6.4.1 to 6.4.2 in /requirements
dependabot[bot] Jan 19, 2025
966d181
build: run build script after tests
piraz Feb 12, 2025
af424e9
Merge branch 'develop' of github.com:candango/automatoes into develop
piraz Feb 12, 2025
f40c3c4
build(tests): remove python 3.8 and add 3.13 to the build
piraz Feb 12, 2025
cd3bf82
build(tests): add cache-save as false option to setup-python
piraz Feb 12, 2025
f79bbd8
build(tests): remove cache from setup-python
piraz Feb 12, 2025
dd143d4
refactor: implement resolve_requires without any pip dependency
piraz Feb 12, 2025
fac7252
build: Set python_requires to 3.9 and newer
piraz Feb 12, 2025
b73e1a7
build(deps): bump peasant to 0.7.5
piraz Feb 12, 2025
054c49e
build(tests): remove cache-save from actions/setup-python
piraz Feb 12, 2025
cfba4c8
build(tests): fix tests
piraz Feb 12, 2025
b8215d8
build(tests): Use x509.SubjectAlternativeName as instance
piraz Feb 12, 2025
645cce9
build(tests): fix type at the script path
piraz Feb 12, 2025
e4cd08a
build: revert build script with no isolation
piraz Feb 12, 2025
c367480
feat(crypto): add functions necessary to generate the ari data
piraz Feb 13, 2025
43893e1
refactor(model): move all namedtupples to model
piraz Feb 13, 2025
1638dad
build(release): deliver 0.9.13
piraz Feb 13, 2025
d1dca5e
docs: update python version to 3.9 and newer
piraz Feb 13, 2025
d80ae4c
Fix typos
szepeviktor Feb 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '^1.13.1'
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Set up peeble
Expand All @@ -33,4 +33,6 @@ jobs:
export GOPATH=~/go
./scripts/pebble_service.sh start tests/conf/pebble-config.json
behave tests/features
./scripts/pebble_service.sh stop tests/conf/pebble-config.json
- name: Build packages
run: |
./scripts/build.sh
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2019-2022 Flávio Gonçalves Garcia
Copyright 2019-2025 Flavio Garcia
Copyright 2016-2017 Veeti Paananen under MIT License

Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
[![PyPI](https://img.shields.io/pypi/v/automatoes.svg)](https://pypi.org/project/automatoes/)
[![Number of PyPI downloads](https://img.shields.io/pypi/dm/automatoes.svg)](https://pypi.org/project/automatoes/#files)
[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fcandango%2Fautomatoes%2Fbadge&style=flat)](https://actions-badge.atrox.dev/candango/automatoes/goto)
[![Requirements Status](https://requires.io/github/candango/automatoes/requirements.svg?branch=develop)](https://requires.io/github/candango/automatoes/requirements/?branch=develop)


Automatoes is a [Let's Encrypt](https://letsencrypt.org)/[ACME](https://github.com/ietf-wg-acme/acme/)
Expand All @@ -16,7 +15,7 @@ original project and to be a direct replacement from

## Why?

Bacause Let's Encrypt's point is to be automatic and seamless and ManuaLE was
Because Let's Encrypt's point is to be automatic and seamless and ManuaLE was
designed to be manual.

Automatoes will add automatic workflows and new features to evolve ManuaLe's
Expand Down Expand Up @@ -45,7 +44,7 @@ still bring your own keys and/or CSR's. Everybody wins.

## Installation

Python 3.6 or above is required.
Python 3.9 or above is required.

### Using your package manager

Expand Down Expand Up @@ -200,7 +199,7 @@ this one will be deleted, and a new order will be created.
> 1. /acme/cert/<cert_id> is called, and we place keys like we use to do before
> 1. we're done!

* If you try to issue certificates for a domain sequence and an oder is pending
* If you try to issue certificates for a domain sequence and an order is pending
or invalid, automatoes will ask you to run authorize before.

After authorizing a domain sequence you need run issue with the same domain
Expand Down Expand Up @@ -291,5 +290,5 @@ initiatives. Available under the
This website and all documentation are licensed under
[Creative Commons 3.0](http://creativecommons.org/licenses/by/3.0/).

Copyright © 2019-2022 Flávio Gonçalves Garcia
Copyright © 2019-2025 Flavio Garcia
Copyright © 2016-2017 Veeti Paananen under MIT License
4 changes: 2 additions & 2 deletions automatoes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2019-2022 Flávio Gonçalves Garcia
# Copyright 2019-2025 Flavio Garcia
# Copyright 2016-2017 Veeti Paananen under MIT License
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -17,7 +17,7 @@

__author__ = "Flavio Garcia <piraz@candango.org>"

__version__ = (0, 9, 10)
__version__ = (0, 9, 13)

__licence__ = "Apache License V2.0"

Expand Down
29 changes: 10 additions & 19 deletions automatoes/acme.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#!/usr/bin/env python
#
# Copyright 2019-2020 Flavio Garcia
# Copyright 2019-2025 Flavio Garcia
# Copyright 2016-2017 Veeti Paananen under MIT License
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -19,12 +17,13 @@
ACME API client.
"""

from . import get_version
from .crypto import (export_certificate_for_acme, generate_header, jose_b64,
sign_request, sign_request_v2)
from .errors import AccountAlreadyExistsError, AcmeError
from .model import Order, Challenge
from collections import namedtuple
from automatoes import get_version
from automatoes.crypto import (export_certificate_for_acme, generate_header,
jose_b64, sign_request, sign_request_v2)
from automatoes.errors import AccountAlreadyExistsError, AcmeError
from automatoes.model import (Challenge, IssuanceResult,
NewAuthorizationResult, Order,
RegistrationResult)
import copy
import datetime
import hashlib
Expand Down Expand Up @@ -241,12 +240,6 @@ def path(self, path):
return urljoin(self.url, path)


RegistrationResult = namedtuple("RegistrationResult", "contents uri terms")
NewAuthorizationResult = namedtuple("NewAuthorizationResult", "contents uri")
IssuanceResult = namedtuple("IssuanceResult",
"certificate location intermediate")


class AcmeV2(Acme):

def __init__(self, url, account, directory="directory", verify=None,
Expand Down Expand Up @@ -413,8 +406,6 @@ def get_order_challenges(self, order: Order):
:param order: order to be challenged
:return: Order
"""
domains = [identifier['value'] for identifier in
order.contents['identifiers']]
order_challenges = []
for auth in order.contents['authorizations']:
auth_response = _json(self.post_as_get(auth, self.account.uri))
Expand Down Expand Up @@ -522,7 +513,7 @@ def post(self, path, body, headers=None, kid=None):
protected = self.get_headers(url=self.path(path))
if kid:
protected['kid'] = kid
protected.pop('jwk')
protected.pop("jwk")
body = sign_request_v2(self.account.key, protected, body)
kwargs = {
'headers': _headers
Expand All @@ -539,7 +530,7 @@ def post_as_get(self, path, kid, headers=None):

protected = self.get_headers(url=self.path(path))
protected['kid'] = kid
protected.pop('jwk')
protected.pop("jwk")
body = sign_request_v2(self.account.key, protected, None)
kwargs = {
'headers': _headers
Expand Down
4 changes: 4 additions & 0 deletions automatoes/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def _issue(args):
key_file=args.key_file,
csr_file=args.csr_file,
output_path=args.output,
output_filename=args.output_filename,
must_staple=args.ocsp_must_staple,
verbose=verbose
)
Expand Down Expand Up @@ -228,6 +229,9 @@ def manuale_main():
issue_sub.add_argument('--output', '-o',
help="The output directory for created objects",
default='.')
issue_sub.add_argument('--output-filename',
help="The filename base for created objects",
default=None)
issue_sub.add_argument('--ocsp-must-staple',
dest='ocsp_must_staple',
help="CSR: Request OCSP Must-Staple extension",
Expand Down
2 changes: 1 addition & 1 deletion automatoes/cli/commands/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@


@taskio.group(name="account", short_help="Group with commands related to "
"account managment")
"account management")
@pass_context
def account(ctx):
pass
Expand Down
2 changes: 1 addition & 1 deletion automatoes/cli/commands/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@


@taskio.group(name="order", short_help="Group with commands related to order "
"managment")
"management")
@pass_context
def order(ctx):
pass
Expand Down
45 changes: 33 additions & 12 deletions automatoes/crypto.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2019-2023 Flávio Gonçalves Garcia
# Copyright 2019-2025 Flavio Garcia
# Copyright 2016-2017 Veeti Paananen under MIT License
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -23,12 +21,8 @@
import binascii
import json
import logging
# TODO: Remove that after python 3.5 depreciation
import warnings
with warnings.catch_warnings():
warnings.simplefilter("ignore")
from cryptography import x509
from cryptography.x509 import NameOID
from cryptography import x509
from cryptography.x509 import NameOID, DNSName
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric.rsa import (
Expand Down Expand Up @@ -90,14 +84,21 @@ def certbot_key_data_to_int(key_data: dict) -> dict:
return key_data_int


def generate_ari_data(cert):
aki_b64 = base64.urlsafe_b64encode(get_certificate_aki(cert).encode())
serial_b64 = base64.urlsafe_b64encode(
get_certificate_serial(cert).encode())
return f"{aki_b64}.{serial_b64}"


def generate_header(account_key):
"""
Creates a new request header for the specified account key.
"""
numbers = account_key.public_key().public_numbers()
e = numbers.e.to_bytes((numbers.e.bit_length() // 8 + 1), byteorder='big')
n = numbers.n.to_bytes((numbers.n.bit_length() // 8 + 1), byteorder='big')
if n[0] == 0: # for strict JWK
if n[0] == 0: # for strict JWK
n = n[1:]
return {
'alg': 'RS256',
Expand Down Expand Up @@ -178,7 +179,8 @@ def export_private_key(key):
"""
Exports a private key in OpenSSL PEM format.
"""
return key.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption())
return key.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL,
NoEncryption())


def create_csr(key, domains, must_staple=False):
Expand Down Expand Up @@ -228,8 +230,27 @@ def load_pem_certificate(data):
return x509.load_pem_x509_certificate(data, default_backend())


def get_issuer_certificate_domain_name(cert):
for cn in cert.subject:
return cn.value


def get_certificate_aki(cert):
for ext in cert.extensions:
if isinstance(ext.value, x509.AuthorityKeyIdentifier):
hex = ext.value.key_identifier.hex()
return ":".join(hex[i:i+2] for i in range(0, len(hex), 2))


def get_certificate_serial(cert):
hex = format(cert.serial_number, "x")
return ":".join(hex[i:i+2] for i in range(0, len(hex), 2))


def get_certificate_domain_name(cert):
return cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
for ext in cert.extensions:
if isinstance(ext.value, x509.SubjectAlternativeName):
return ext.value.get_values_for_type(DNSName)[0]


def get_certificate_domains(cert):
Expand Down
11 changes: 6 additions & 5 deletions automatoes/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@


def issue(server, paths, account, domains, key_size, key_file=None,
csr_file=None, output_path=None, must_staple=False, verbose=False):
csr_file=None, output_path=None, output_filename=None, must_staple=False, verbose=False):
print("Candango Automatoes {}. Manuale replacement.\n\n".format(
get_version()))

Expand Down Expand Up @@ -195,11 +195,12 @@ def issue(server, paths, account, domains, key_size, key_file=None,

# 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')
cert_name = output_filename if output_filename else domains[0]
cert_path = os.path.join(output_path, cert_name + '.crt')
chain_path = os.path.join(output_path, cert_name + '.chain.crt')
intermediate_path = os.path.join(output_path,
domains[0] + '.intermediate.crt')
key_path = os.path.join(output_path, domains[0] + '.pem')
cert_name + '.intermediate.crt')
key_path = os.path.join(output_path, cert_name + '.pem')

if order.key is not None:
with open(key_path, 'wb') as f:
Expand Down
11 changes: 8 additions & 3 deletions automatoes/model.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#!/usr/bin/env python
#
# Copyright 2019-2022 Flávio Gonçalves Garcia
# Copyright 2019-2025 Flavio Garcia
# Copyright 2016-2017 Veeti Paananen under MIT License
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -20,6 +18,7 @@

from .crypto import (generate_jwk_thumbprint, load_private_key,
export_private_key)
from collections import namedtuple
from datetime import datetime


Expand Down Expand Up @@ -141,3 +140,9 @@ def deserialize(data):
return order
except (TypeError, ValueError, AttributeError) as e:
raise IOError("Invalid account structure: {}".format(e))


RegistrationResult = namedtuple("RegistrationResult", "contents uri terms")
NewAuthorizationResult = namedtuple("NewAuthorizationResult", "contents uri")
IssuanceResult = namedtuple("IssuanceResult",
"certificate location intermediate")
Loading
Loading