Skip to content

Commit

Permalink
Provide Docker images (#44)
Browse files Browse the repository at this point in the history
* Docker image build

* Update changelog

* Fix typo, recursively sign multi-arch image

* More cosign fixes

* Documentation updates

* Make Docker image usage examples prettier, update signing pubkey
  • Loading branch information
CBonnell authored Sep 28, 2023
1 parent 1c3c5ad commit a6f7fb7
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 7 deletions.
86 changes: 84 additions & 2 deletions .github/workflows/build_and_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ on:
types:
- published

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build_wheel:
name: Build pure-Python wheel and source distribution
Expand Down Expand Up @@ -37,10 +41,88 @@ jobs:
path: dist
- name: Test wheel
run: |
export WHEEL_FILE=`ls -1 dist/*.whl | head -n 1`
pip install "$WHEEL_FILE[dev]"
export WHEELFILE=`ls -1 dist/*.whl | head -n 1`
pip install "$WHEELFILE[dev]"
pytest
build_and_push_docker:
needs: [test_wheel]
name: Build and push Docker image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/download-artifact@v3
with:
name: artifact
path: docker/dist
- name: Set environment variables
run: |
cd docker/dist && echo WHEELFILE=$(ls -1 *.whl) >> $GITHUB_ENV
-
name: Setup QEMU
uses: docker/setup-qemu-action@v2
-
name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
-
name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
-
name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v5
with:
build-args:
WHEELFILE=${{ env.WHEELFILE }}
context: docker
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
-
name: Download and configure Software Trust Manager
if: github.event_name != 'pull_request'
run: |
sudo curl -H "X-API-Key:${{ secrets.SM_API_KEY }}" "https://${{ secrets.DC1_API_HOST }}/signingmanager/api-ui/v1/releases/smpkcs11-linux-x64/download" -o /usr/local/lib/smpkcs11.so
echo "name=signingmanager" | sudo tee -a /usr/local/lib/pkcs11properties.cfg > /dev/null
echo "library=/usr/local/lib/smpkcs11.so" | sudo tee -a /usr/local/lib/pkcs11properties.cfg > /dev/null
echo "slotListIndex=0" | sudo tee -a /usr/local/lib/pkcs11properties.cfg > /dev/null
echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> $GITHUB_ENV
echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> $GITHUB_ENV
echo "SM_HOST=${{ secrets.SM_HOST }}" >> $GITHUB_ENV
echo "SM_CLIENT_CERT_FILE=ApiClientP12.p12" >> $GITHUB_ENV
echo "${{ secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 -d > ApiClientP12.p12
-
name: Install cosign
if: github.event_name != 'pull_request'
run: |
sudo apt update && sudo apt install pcscd
curl -L https://github.com/sigstore/cosign/releases/download/v2.2.0/cosign-linux-pivkey-pkcs11key-amd64 -o cosign
chmod 755 cosign
-
name: Sign Docker image
if: github.event_name != 'pull_request'
env:
TAGS: ${{ steps.meta.outputs.tags }}
DIGEST: ${{ steps.build-and-push.outputs.digest }}
run: |
echo "${TAGS}" | xargs -I {} ./cosign sign --recursive --key "${{ secrets.SM_KEY_URI }}" --yes {}@${DIGEST}
upload_pypi:
needs: [build_wheel, test_wheel]
runs-on: ubuntu-latest
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Changelog

ALl notable changes to this project from version 0.9.3 onwards are documented in this file.
All notable changes to this project from version 0.9.3 onwards are documented in this file.

## 0.9.4 - 2023-09-28

### New features/enhancements

- Publish Docker images (#43)

## 0.9.3 - 2023-09-20

Expand Down
89 changes: 86 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ There are several ready-to-use command-line tools bundled with pkilint, or the P

## Installation

### Installing locally

1. Python 3.9 or newer must be installed. Python can be downloaded and installed from https://www.python.org/downloads/, or
use your operating system's package manager.

Expand All @@ -27,17 +29,17 @@ the instructions on the [pipx homepage](https://pypa.github.io/pipx/) to install
pipx install pkilint
```

Once installed, the bundled command line applications (listed below) and the API will be available on your machine.
Once installed, the bundled command line applications (listed below) and the Python API will be available on your machine.

### Upgrading
#### Upgrading

When a new version of pkilint is released, run the following command to upgrade your installation:

```shell
pipx upgrade pkilint
```

### REST API Installation
#### REST API Installation

pkilint provides a REST API component that can be installed as a package extra. The REST API is implemented as an ASGI
web application, so you will need to install an ASGI server in addition to the package extra. There are several ASGI
Expand All @@ -50,6 +52,87 @@ pipx install pkilint[rest]
pipx inject pkilint --include-apps uvicorn
```

### Docker

Starting with v0.9.4, Docker images are provided with each release. In addition to the pkilint Python package, the image includes
[Uvicorn](https://www.uvicorn.org/) and [Gunicorn](https://gunicorn.org/). These additional packages allow the Docker image to be
readily used to run a server that provides the pkilint REST API.

To pull the latest version of the Docker image, execute the following command:

```shell
docker pull ghcr.io/digicert/pkilint
```

After the Docker image has been pulled, all command-line linters are available, as well as the Uvicorn and Gunicorn commands to start the
REST API server.

A few examples demonstrating use of the Docker image are provided below.

#### Linting an S/MIME certificate

```shell
$ echo '-----BEGIN CERTIFICATE-----
MIIF1DCCA7ygAwIBAgIUI+v/jTtadau/a5lLVGP50z0FoW8wDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCVVMxHzAdBgNVBAoMFkZvbyBJbmR1c3RyaWVzIExpbWl0
ZWQxGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBDQTAeFw0yMzA0MTkwMDAwMDBaFw0y
MzA3MTgyMzU5NTlaMEIxFjAUBgNVBAMMDVlBTUFEQSBIYW5ha28xKDAmBgkqhkiG
9w0BCQEWGWhhbmFrby55YW1hZGFAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCw+egZQ6eumJKq3hfKfED4dE/tL4FI5sjqont9ABVI
+1GSqyi1bFBgsRjM0THllIdMbKmJtWwnKW8J+5OgNN8y6Xxv8JmM/Y5vQt2lis0f
qXmG8UTz0VTWdlAXXmhUs6lSADvAaIe4RVrCsZ97L3ZQTryY7JRVcbB4khUN3Gp0
yg+801SXzoFTTa+UGIRLE66jH51aa5VXu99hnv1OiH8tQrjdi8mH6uG/icq4XuIe
NWMF32wHqIOOPvQcWV3M5D2vxJEj702Ku6k9OQXkAo17qRSEonWW4HtLbtmS8He1
JNPc/n3dVUm+fM6NoDXPoLP7j55G9zKyqGtGAWXAj1MTAgMBAAGjggG6MIIBtjAM
BgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDAfBgNVHSMEGDAWgBTWRAAyfKgN
/6xPa2buta6bLMU4VDAdBgNVHQ4EFgQUiRlZXg7xafXLvUfhNPzimMxpMJEwFAYD
VR0gBA0wCzAJBgdngQwBBQQBMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jcmwu
Y2EuZXhhbXBsZS5jb20vaXNzdWluZ19jYV9jcmwuY3JsMEsGCCsGAQUFBwEBBD8w
PTA7BggrBgEFBQcwAoYvaHR0cDovL3JlcG9zaXRvcnkuY2EuZXhhbXBsZS5jb20v
aXNzdWluZ19jYS5kZXIwHQYDVR0lBBYwFAYIKwYBBQUHAwQGCCsGAQUFBwMCMIGU
BgNVHREEgYwwgYmBGWhhbmFrby55YW1hZGFAZXhhbXBsZS5jb22gKQYKKwYBBAGC
NxQCA6AbDBloYW5ha28ueWFtYWRhQGV4YW1wbGUuY29toCYGCCsGAQUFBwgJoBoM
GOWxseeUsOiKseWtkEBleGFtcGxlLmNvbaQZMBcxFTATBgNVBAMMDOWxseeUsOiK
seWtkDANBgkqhkiG9w0BAQsFAAOCAgEAB3UHyqEUNiG3h2cDl9O0jfsIUwOSxSOo
TI9X81QsoCb1JZpcDNJWyvBDalUSChHLAxBxImGa+WZw7dCFxhKLds8NKGtScefk
7FNVxHT7iR77DcaqqyCz3UGYT5nwoPFMJ1Iu3Vb7h1zn9zHn9BlVCFEHr19ORXHp
vjyi4cEU5/1zhfbm09tJE+2F4mrDK10AGG6BD6QTw0vV+vA+pSfxzcEmmfH0lcPL
ORgN4/A/bP4c57A7ZXG1YAbmEDJK07b6wF53EoUumalV7WvynrD9Jx1QrUera3yQ
LhOqfyWz7Ib2+dQnLlaLPw7n7gnSlo8EqfiyuY2XmOlr6i/KBGdWLnxE+t1yC/YC
FKVVykJEItSqyngEKAHZyu6Qh+v68uorMO7nMhWQ/toLEeYxjig38qMi+oJ5oMey
SlNKUQpLRTr7IRdvQ9gM2hHKTv/KrbmCa8vJv+pH0jbvE2WuHRkIQxmK/qYqkXKH
cCHQU8NkafPEeQaE2hidSZV7AUzD4t2VoySASeh5qRC3QhNTIueFEjgBkJVGbynR
nYIS9bOMsNASk8p5PYFcmDhHxOBHInjT5k+ai82xWruI5FV8ITf+qOiVgavPssSa
YFtmhJZx0eimy04HG4O2CobSjQrt7Ue+Yzzi/DWxhPfKPHOKTSqcxvS4ym37F2ly
bO2MTo+BW7w=
-----END CERTIFICATE-----' | docker run -i ghcr.io/digicert/pkilint lint_cabf_smime_cert lint -d -
SubjectKeyIdentifierValidator @ certificate.tbsCertificate.extensions.3.extnValue.subjectKeyIdentifier
pkix.subject_key_identifier_method_1_identified (INFO)
$
```

#### Starting a REST API server with 8 worker processes, listening for requests on TCP/IP port 8000 on all interfaces

```shell
$ docker run -d -p 8000:8000 ghcr.io/digicert/pkilint gunicorn -w 8 -k uvicorn.workers.UvicornWorker -b 0.0.0.0:8000 pkilint.rest:app
7a87146998609343490808cb6a37a8d72c3d5a2bf796af837f37e71e2bc9b144
$ curl -X POST -H "Content-Type: application/json" -d '{"b64":"MIIF1DCCA7ygAwIBAgIUI+v/jTtadau/a5lLVGP50z0FoW8wDQYJKoZIhvcNAQELBQAwSDELMAkGA1UEBhMCVVMxHzAdBgNVBAoMFkZvbyBJbmR1c3RyaWVzIExpbWl0ZWQxGDAWBgNVBAMMD0ludGVybWVkaWF0ZSBDQTAeFw0yMzA0MTkwMDAwMDBaFw0yMzA3MTgyMzU5NTlaMEIxFjAUBgNVBAMMDVlBTUFEQSBIYW5ha28xKDAmBgkqhkiG9w0BCQEWGWhhbmFrby55YW1hZGFAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw+egZQ6eumJKq3hfKfED4dE/tL4FI5sjqont9ABVI+1GSqyi1bFBgsRjM0THllIdMbKmJtWwnKW8J+5OgNN8y6Xxv8JmM/Y5vQt2lis0fqXmG8UTz0VTWdlAXXmhUs6lSADvAaIe4RVrCsZ97L3ZQTryY7JRVcbB4khUN3Gp0yg+801SXzoFTTa+UGIRLE66jH51aa5VXu99hnv1OiH8tQrjdi8mH6uG/icq4XuIeNWMF32wHqIOOPvQcWV3M5D2vxJEj702Ku6k9OQXkAo17qRSEonWW4HtLbtmS8He1JNPc/n3dVUm+fM6NoDXPoLP7j55G9zKyqGtGAWXAj1MTAgMBAAGjggG6MIIBtjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDAfBgNVHSMEGDAWgBTWRAAyfKgN/6xPa2buta6bLMU4VDAdBgNVHQ4EFgQUiRlZXg7xafXLvUfhNPzimMxpMJEwFAYDVR0gBA0wCzAJBgdngQwBBQQBMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jcmwuY2EuZXhhbXBsZS5jb20vaXNzdWluZ19jYV9jcmwuY3JsMEsGCCsGAQUFBwEBBD8wPTA7BggrBgEFBQcwAoYvaHR0cDovL3JlcG9zaXRvcnkuY2EuZXhhbXBsZS5jb20vaXNzdWluZ19jYS5kZXIwHQYDVR0lBBYwFAYIKwYBBQUHAwQGCCsGAQUFBwMCMIGUBgNVHREEgYwwgYmBGWhhbmFrby55YW1hZGFAZXhhbXBsZS5jb22gKQYKKwYBBAGCNxQCA6AbDBloYW5ha28ueWFtYWRhQGV4YW1wbGUuY29toCYGCCsGAQUFBwgJoBoMGOWxseeUsOiKseWtkEBleGFtcGxlLmNvbaQZMBcxFTATBgNVBAMMDOWxseeUsOiKseWtkDANBgkqhkiG9w0BAQsFAAOCAgEAB3UHyqEUNiG3h2cDl9O0jfsIUwOSxSOoTI9X81QsoCb1JZpcDNJWyvBDalUSChHLAxBxImGa+WZw7dCFxhKLds8NKGtScefk7FNVxHT7iR77DcaqqyCz3UGYT5nwoPFMJ1Iu3Vb7h1zn9zHn9BlVCFEHr19ORXHpvjyi4cEU5/1zhfbm09tJE+2F4mrDK10AGG6BD6QTw0vV+vA+pSfxzcEmmfH0lcPLORgN4/A/bP4c57A7ZXG1YAbmEDJK07b6wF53EoUumalV7WvynrD9Jx1QrUera3yQLhOqfyWz7Ib2+dQnLlaLPw7n7gnSlo8EqfiyuY2XmOlr6i/KBGdWLnxE+t1yC/YCFKVVykJEItSqyngEKAHZyu6Qh+v68uorMO7nMhWQ/toLEeYxjig38qMi+oJ5oMeySlNKUQpLRTr7IRdvQ9gM2hHKTv/KrbmCa8vJv+pH0jbvE2WuHRkIQxmK/qYqkXKHcCHQU8NkafPEeQaE2hidSZV7AUzD4t2VoySASeh5qRC3QhNTIueFEjgBkJVGbynRnYIS9bOMsNASk8p5PYFcmDhHxOBHInjT5k+ai82xWruI5FV8ITf+qOiVgavPssSaYFtmhJZx0eimy04HG4O2CobSjQrt7Ue+Yzzi/DWxhPfKPHOKTSqcxvS4ym37F2lybO2MTo+BW7w="}' http://localhost:8000/certificate/cabf-smime
{"results":[{"validator":"SubjectKeyIdentifierValidator","node_path":"certificate.tbsCertificate.extensions.3.extnValue.subjectKeyIdentifier","finding_descriptions":[{"severity":"INFO","code":"pkix.subject_key_identifier_method_1_identified","message":null}]}],"linter":{"name":"INDIVIDUAL-LEGACY"}}
$
```

#### Sigstore signature verification

Docker images are signed using [Sigstore](https://www.sigstore.dev/)'s [cosign](https://docs.sigstore.dev/signing/quickstart/) tool.
To verify the signature on a Docker image, [install the cosign utility](https://docs.sigstore.dev/system_config/installation/) and execute the following
command:
```shell
cosign verify --key https://raw.githubusercontent.com/digicert/pkilint/main/docker/cosign_public_key.pem ghcr.io/digicert/pkilint
```
## Usage
Several command line linters are bundled with pkilint, each of which will return the number of reported findings as the
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.9.3
0.9.4
17 changes: 17 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM python:3.11-slim

LABEL authors="DigiCert, Inc."

ARG WHEELFILE

WORKDIR /usr/src/app

COPY dist/$WHEELFILE ./

RUN pip install --no-cache-dir "$WHEELFILE[rest]" uvicorn gunicorn

RUN rm $WHEELFILE

COPY entrypoint.py ./

ENTRYPOINT ["python", "entrypoint.py"]
4 changes: 4 additions & 0 deletions docker/cosign_public_key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhOqVwQczkXH/AVUzwdG6XUKdKgn0
c5I1YiBb6FKurgQoj7cfCASQea5fgLoKoGKDOSieK4ucECR9GVMlE3FxHA==
-----END PUBLIC KEY-----
47 changes: 47 additions & 0 deletions docker/entrypoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env python3

import functools
import sys
import subprocess

from pkilint.bin import (
lint_cabf_serverauth_cert,
lint_cabf_smime_cert,
lint_crl,
lint_ocsp_response,
lint_pkix_cert,
lint_pkix_signer_signee_cert_chain,
)

_ENTRYPOINTS = {
'lint_cabf_serverauth_cert': lint_cabf_serverauth_cert.main,
'lint_cabf_smime_cert': lint_cabf_smime_cert.main,
'lint_crl': lint_crl.main,
'lint_ocsp_response': lint_ocsp_response.main,
'lint_pkix_cert': lint_pkix_cert.main,
'lint_pkix_signer_signee_cert_chain': lint_pkix_signer_signee_cert_chain.main,
}


def _run(cmd, args) -> int:
completed_proc = subprocess.run([cmd] + args)

return completed_proc.returncode


def main():
if len(sys.argv) < 2:
print('Executable not specified', file=sys.stderr)

return 1

cmd = sys.argv[1]
args = sys.argv[2:]

entrypoint_func = _ENTRYPOINTS.get(cmd, functools.partial(_run, cmd))

return entrypoint_func(args)


if __name__ == '__main__':
sys.exit(main())

0 comments on commit a6f7fb7

Please sign in to comment.