Skip to content

Commit e73bd6b

Browse files
authored
Adding certbot-aws-store back, smaller deps. Updated other major deps (#18)
1 parent 411e6bd commit e73bd6b

File tree

9 files changed

+621
-210
lines changed

9 files changed

+621
-210
lines changed

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ repos:
2323
exclude: ^ecs_files_composer/input.py|docs/
2424

2525
- repo: https://github.com/psf/black
26-
rev: 22.12.0
26+
rev: 23.1.0
2727
hooks:
2828
- id: black
2929
- repo: https://github.com/pycqa/isort
30-
rev: 5.11.4
30+
rev: 5.12.0
3131
hooks:
3232
- id: isort
3333
args: ["--profile", "black", "--filter-files"]

Dockerfile

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,23 @@ ARG ARCH=
22
ARG PY_VERSION=3.9
33
ARG BASE_IMAGE=public.ecr.aws/docker/library/python:${PY_VERSION}-slim
44
ARG LAMBDA_IMAGE=public.ecr.aws/lambda/python:latest
5-
FROM $BASE_IMAGE as builder
5+
6+
FROM $LAMBDA_IMAGE AS builder
67

78
WORKDIR /opt
89
COPY ecs_files_composer /opt/ecs_files_composer
910
COPY poetry.lock pyproject.toml MANIFEST.in README.rst LICENSE /opt/
11+
RUN yum install gcc -y
1012
RUN python -m pip install pip -U; python -m pip install poetry; poetry build
13+
RUN pip install wheel --no-cache-dir && pip install dist/*.whl --no-cache-dir -t /opt/venv
14+
RUN find /opt/venv -type d -name "*pycache*" | xargs -i -P10 rm -rf {}
1115

1216
FROM $BASE_IMAGE
1317

14-
RUN yum upgrade -y; yum install -y tar unzip xz gzip;\
15-
yum autoremove -y; yum clean packages; yum clean headers; yum clean metadata; yum clean all; rm -rfv /var/cache/yum
16-
ENV PATH=/app/.local/bin:${PATH}
17-
COPY --from=builder /opt/dist/ecs_files_composer-*.whl ${LAMBDA_TASK_ROOT:-/app/}/
18-
WORKDIR /app
19-
RUN echo $PATH ; pip install pip -U --no-cache-dir && pip install wheel --no-cache-dir && pip install *.whl --no-cache-dir
18+
COPY --from=builder /opt/venv ${LAMBDA_TASK_ROOT:-/app/}/venv
19+
ENV PYTHONPATH /app/venv:$PYTHONPATH
20+
ENV LD_LIBRARY_PATH=/app/venv:$LD_LIBRARY_PATH
21+
ENV PATH /app/venv/bin:$PATH
2022
WORKDIR /
21-
ENTRYPOINT ["ecs_files_composer"]
23+
ENTRYPOINT ["python", "-m", "ecs_files_composer.cli"]
24+
CMD ["-h"]

ecs-files-input.json

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,24 @@
2828
},
2929
"certbot_store": {
3030
"type": "object",
31-
"uniqueItems": true,
32-
"patternProperties": {
33-
"[\\W\\-\\.]+$": {
34-
"$ref": "#/definitions/CertbotAwsStoreCertificate"
31+
"properties": {
32+
"certificates_registry_table_name": {
33+
"type": "string",
34+
"description": "default table name to query for certificates.",
35+
"default": "certbot-registry"
36+
},
37+
"certificates_registry_table_region": {
38+
"type": "string",
39+
"description": "region to use for the certificates_registry_table_name. Defaults to current region found in profiles chain."
40+
},
41+
"certificates": {
42+
"type": "object",
43+
"uniqueItems": true,
44+
"patternProperties": {
45+
"[\\W\\-\\.]+$": {
46+
"$ref": "#/definitions/CertbotAwsStoreCertificate"
47+
}
48+
}
3549
}
3650
}
3751
},
@@ -360,23 +374,26 @@
360374
"default": "root"
361375
},
362376
"jksConfig": {
363-
"description": "Configuration that will allow to transform the certificate into a JKS for java applications",
364-
"type": "object",
365-
"additionalProperties": false,
366-
"required": [
367-
"fileName",
368-
"passphrase"
369-
],
370-
"properties": {
371-
"fileName": {
372-
"type": "string",
373-
"description": "Name of the JKS file to create"
374-
},
375-
"passphrase": {
376-
"type": "string",
377-
"description": "The passphrase to use to secure the jks and certificate"
378-
}
379-
}
377+
"$ref": "#/definitions/jksConfig"
378+
}
379+
}
380+
},
381+
"jksConfig": {
382+
"description": "Configuration that will allow to transform the certificate into a JKS for java applications",
383+
"type": "object",
384+
"additionalProperties": false,
385+
"required": [
386+
"fileName",
387+
"passphrase"
388+
],
389+
"properties": {
390+
"fileName": {
391+
"type": "string",
392+
"description": "Name of the JKS file to create"
393+
},
394+
"passphrase": {
395+
"type": "string",
396+
"description": "The passphrase to use to secure the jks and certificate"
380397
}
381398
}
382399
},
@@ -391,14 +408,17 @@
391408
"pattern": "^/[\\x00-\\x7F]+$",
392409
"description": "path to folder to store the certbot certificates into."
393410
},
394-
"table_name": {
411+
"certificates_registry_table_name": {
395412
"type": "string",
396-
"description": "dynamodb table name of the certbot-aws-store registry",
397-
"default": "certbot-registry"
413+
"description": "Override the top level certificates_registry_table_name"
398414
},
399-
"table_region_name": {
415+
"certificates_registry_table_region": {
400416
"type": "string",
401-
"description": "Region in which the table_name is. Defaults to profile default region, or eu-west-1"
417+
"description": "Override the top level certificates_registry_table_region"
418+
},
419+
"jksConfig": {
420+
"$ref": "#/definitions/jksConfig",
421+
"description": "Automatically creates a JKS with the retrieved certificate."
402422
}
403423
}
404424
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Copyright 2020-2022 John Mille<[email protected]>
3+
4+
"""
5+
Manages certbot certificates download from certbot-aws-store
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import os
11+
from typing import TYPE_CHECKING
12+
13+
from boto3.session import Session
14+
15+
if TYPE_CHECKING:
16+
from .input import Model, CertbotAwsStoreCertificate
17+
18+
from os import makedirs, path
19+
20+
import jks as pyjks
21+
from certbot_aws_store.certificate import AcmeCertificate
22+
from OpenSSL import crypto
23+
24+
from ecs_files_composer.common import LOG
25+
26+
27+
def create_jks_config(
28+
certificate_name: str, certificate_job: CertbotAwsStoreCertificate
29+
):
30+
with open(
31+
f"{certificate_job.storage_path}/{AcmeCertificate.full_chain_file_name}"
32+
) as full_chain_fd:
33+
full_chain = crypto.load_certificate(crypto.FILETYPE_PEM, full_chain_fd.read())
34+
with open(
35+
f"{certificate_job.storage_path}/{AcmeCertificate.private_key_file_name}"
36+
) as priv_key_fd:
37+
private_key = priv_key_fd.read()
38+
39+
jks_path = path.abspath(
40+
f"{certificate_job.storage_path}/{certificate_job.jks_config.file_name}"
41+
)
42+
pkey = pyjks.jks.PrivateKeyEntry.new(
43+
certificate_name,
44+
certs=[crypto.dump_certificate(crypto.FILETYPE_ASN1, full_chain)],
45+
key=private_key,
46+
key_format="rsa_raw",
47+
)
48+
pkey.encrypt(certificate_job.jks_config.passphrase)
49+
keystore = pyjks.KeyStore.new("jks", [pkey])
50+
keystore.save(jks_path, certificate_job.jks_config.passphrase)
51+
52+
53+
def process_certbot_aws_store_certificates(job: Model) -> None:
54+
"""
55+
Pulls certificates from certbot-aws-store to local filesystem
56+
If the path does not exist, creates a new directory for download.
57+
"""
58+
if not job.certbot_store:
59+
return
60+
default_table_name = job.certbot_store.certificates_registry_table_name
61+
if job.certbot_store.certificates_registry_table_region:
62+
default_table_region = job.certbot_store.certificates_registry_table_region
63+
else:
64+
default_table_region = os.getenv("AWS_DEFAULT_REGION", Session().region_name)
65+
for _hostname, _definition in job.certbot_store.certificates.items():
66+
certificate = AcmeCertificate(
67+
_hostname,
68+
None,
69+
table_name=_definition.certificates_registry_table_name
70+
if _definition.certificates_registry_table_name
71+
else default_table_name,
72+
region_name=_definition.certificates_registry_table_region
73+
if _definition.certificates_registry_table_region
74+
else default_table_region,
75+
)
76+
if not path.exists(_definition.storage_path):
77+
makedirs(_definition.storage_path, exist_ok=True)
78+
try:
79+
certificate.pull(_definition.storage_path)
80+
LOG.info("Successfully pulled certificates for %s", _hostname)
81+
except Exception as error:
82+
LOG.exception(error)
83+
LOG.error(
84+
"Failed to download certificate from certbot-aws-store", _hostname
85+
)
86+
if _definition.jks_config:
87+
create_jks_config(_hostname, _definition)

ecs_files_composer/ecs_files_composer.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from ecs_files_composer import input
2222
from ecs_files_composer.aws_mgmt import S3Fetcher
23+
from ecs_files_composer.certbot_aws_store import process_certbot_aws_store_certificates
2324
from ecs_files_composer.certificates_mgmt import process_x509_certs
2425
from ecs_files_composer.common import LOG
2526
from ecs_files_composer.files_mgmt import File
@@ -149,5 +150,7 @@ def start_jobs(config: dict, override_session=None):
149150
job = input.Model(**config)
150151
if job.certificates:
151152
process_x509_certs(job)
153+
if job.certbot_store:
154+
process_certbot_aws_store_certificates(job)
152155
if job.files:
153156
process_files(job, override_session)

ecs_files_composer/files_mgmt.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
"""Main module."""
55

6+
from __future__ import annotations
7+
68
import base64
79
import os
810
import pathlib
@@ -15,7 +17,6 @@
1517
import jinja2.exceptions
1618
import requests
1719
from botocore.response import StreamingBody
18-
from compose_x_common.compose_x_common import keyisset
1920
from jinja2 import Environment, FileSystemLoader
2021

2122
from ecs_files_composer.aws_mgmt import S3Fetcher, SecretFetcher, SsmFetcher
@@ -68,7 +69,6 @@ def handler(self, iam_override=None, session_override=None):
6869
self.post_processing()
6970

7071
def files_content_processing(self) -> None:
71-
7272
if self.content and self.encoding and self.encoding == Encoding["base64"]:
7373
self.content = base64.b64decode(self.content).decode()
7474
if self.templates_dir:
@@ -305,7 +305,6 @@ def set_unix_settings(self):
305305
raise
306306

307307
def exec_post_commands(self):
308-
309308
ignore_post_command_failure = (
310309
self.ignore_failure
311310
if self.ignore_failure and isinstance(self.ignore_failure, bool)

0 commit comments

Comments
 (0)