Skip to content

Commit

Permalink
Feature/return ca chain (#1)
Browse files Browse the repository at this point in the history
Updating to return CA chain and improve actions to be independent of repository owner
  • Loading branch information
kingcdavid authored Jan 3, 2024
1 parent 3651317 commit 26da84b
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 24 deletions.
37 changes: 35 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,23 @@ on: workflow_dispatch
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
GITHUBOWNER: ${{ github.repository_owner }}

jobs:
build-and-push-image:
docker_build_and_release:
runs-on: ubuntu-latest
permissions:
contents: read
contents: write
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Get the version
id: get_version
run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3)

- name: Log in to the Container registry
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
Expand All @@ -46,3 +51,31 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Create install.yaml
shell: bash
run: make build/install.yaml

- name: Create release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ steps.get_version.outputs.VERSION }}
draft: true
prerelease: false
body: |
To install: `kubectl apply -f https://github.com/${{ github.repository_owner }}/atlas-cert-manager/releases/download/${{ steps.get_version.outputs.VERSION }}/install.yaml`
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: build/install.yaml
asset_name: install.yaml
asset_content_type: application/x-yaml
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 nhgs64
Copyright (c) 2023 GlobalSign

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ SHELL := bash

# The version which will be reported by the --version argument of each binary
# and which will be used as the Docker image tag
VERSION ?= master
VERSION ?= latest
# The Docker repository name, overridden in CI.
DOCKER_REGISTRY ?= ghcr.io
DOCKER_IMAGE_NAME ?= nhgs64/atlas-cert-manager
GITHUBOWNER ?= globalsign
DOCKER_IMAGE_NAME ?= ${GITHUBOWNER}/atlas-cert-manager
# Image URL to use all building/pushing image targets
IMG ?= ${DOCKER_REGISTRY}/${DOCKER_IMAGE_NAME}:${VERSION}
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/do
```
Next, install the Atlas controller and CRDs:
```console
kubectl apply -f https://github.com/nhgs64/atlas-cert-manager/releases/latest/download/install.yaml
kubectl apply -f https://github.com/globalsign/atlas-cert-manager/releases/latest/download/install.yaml
```
The controller is deployed and ready to handle Atlas requests.

Expand Down
3 changes: 2 additions & 1 deletion internal/controllers/certificaterequest_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,12 @@ func (r *CertificateRequestReconciler) Reconcile(ctx context.Context, req ctrl.R
return ctrl.Result{}, fmt.Errorf("%w: %v", errSignerBuilder, err)
}

signed, err := signer.Sign(certificateRequest.Spec.Request)
signed, ca, err := signer.Sign(certificateRequest.Spec.Request)
if err != nil {
return ctrl.Result{}, fmt.Errorf("%w: %v", errSignerSign, err)
}
certificateRequest.Status.Certificate = signed
certificateRequest.Status.CA = ca

setReadyCondition(cmmeta.ConditionTrue, cmapi.CertificateRequestReasonIssued, "Signed")
return ctrl.Result{}, nil
Expand Down
5 changes: 3 additions & 2 deletions internal/controllers/certificaterequest_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ type fakeSigner struct {
errSign error
}

func (o *fakeSigner) Sign([]byte) ([]byte, error) {
return []byte("fake signed certificate"), o.errSign
func (o *fakeSigner) Sign([]byte) ([]byte, []byte, error) {

return []byte("fake signed certificate"), []byte("fake ca chain"), o.errSign
}

func TestCertificateRequestReconcile(t *testing.T) {
Expand Down
49 changes: 34 additions & 15 deletions internal/issuer/signer/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type HealthChecker interface {
type HealthCheckerBuilder func(*sampleissuerapi.IssuerSpec, map[string][]byte) (HealthChecker, error)

type Signer interface {
Sign([]byte) ([]byte, error)
Sign([]byte) ([]byte, []byte, error)
}

type SignerBuilder func(*sampleissuerapi.IssuerSpec, map[string][]byte) (Signer, error)
Expand Down Expand Up @@ -67,19 +67,20 @@ func (o *hvcaSigner) Check() error {
return nil
}

func (o *hvcaSigner) Sign(csrBytes []byte) ([]byte, error) {
func (o *hvcaSigner) Sign(csrBytes []byte) ([]byte, []byte, error) {
ctx, cancel := context.WithCancel(context.Background())
var clnt *hvclient.Client
var serial *big.Int
var info *hvclient.CertInfo
var caChainList []*x509.Certificate
defer cancel()
if clnt, err = hvclient.NewClient(ctx, o.config); err != nil {
return nil, err
return nil, nil, err
}
// Parse the csr
csr, err := parseCSR(csrBytes)
if err != nil {
return nil, err
return nil, nil, err
}

var req = hvclient.Request{
Expand All @@ -92,13 +93,13 @@ func (o *hvcaSigner) Sign(csrBytes []byte) ([]byte, error) {
// Pull the validation policy and check it for required fields
vp, err := clnt.Policy(ctx)
if err != nil {
return nil, err
return nil, nil, err
}
// Subject validation
// common name
if vp.SubjectDN.CommonName.Presence == hvclient.Required {
if csr.Subject.CommonName == "" {
return nil, errors.New("atlas validation policy requires subject common name, but CSR did not contain one")
return nil, nil, errors.New("atlas validation policy requires subject common name, but CSR did not contain one")
}
req.Subject.CommonName = csr.Subject.CommonName
}
Expand All @@ -109,7 +110,7 @@ func (o *hvcaSigner) Sign(csrBytes []byte) ([]byte, error) {
// serial number
if vp.SubjectDN.SerialNumber.Presence == hvclient.Required {
if csr.Subject.SerialNumber == "" {
return nil, errors.New("atlas validation policy requires subject serial number, but CSR did not contain one")
return nil, nil, errors.New("atlas validation policy requires subject serial number, but CSR did not contain one")
}
req.Subject.SerialNumber = csr.Subject.SerialNumber
}
Expand All @@ -135,30 +136,48 @@ func (o *hvcaSigner) Sign(csrBytes []byte) ([]byte, error) {
}
// Validate number of SANs
if vp.SAN.DNSNames.MinCount > len(req.SAN.DNSNames) || vp.SAN.IPAddresses.MinCount > len(req.SAN.IPAddresses) {
return nil, errors.New("atlas validation policy requires additional SANs not present in the provided CSR")
return nil, nil, errors.New("atlas validation policy requires additional SANs not present in the provided CSR")
}
// Check key type
if vp.PublicKey.KeyType.String() != csr.PublicKeyAlgorithm.String() {
return nil, errors.New("csr public key type doesn't match Atlas account pubic key type: CSR - " + csr.PublicKeyAlgorithm.String() + "Atlas - " + vp.PublicKey.KeyType.String())
return nil, nil, errors.New("csr public key type doesn't match Atlas account pubic key type: CSR - " + csr.PublicKeyAlgorithm.String() + "Atlas - " + vp.PublicKey.KeyType.String())
}
// Check PKCS type
if vp.PublicKey.KeyFormat != hvclient.PKCS10 {
return nil, errors.New("atlas account does not support pkcs10 key format, update atlas account")
return nil, nil, errors.New("atlas account does not support pkcs10 key format, update atlas account")
}
// Check signature hash algorithm requirement and set to the first approved one
if vp.SignaturePolicy.HashAlgorithm.Presence == 2 { //Presence is required
req.Signature.HashAlgorithm = vp.SignaturePolicy.HashAlgorithm.List[0]
}
// Request cert
if serial, err = clnt.CertificateRequest(ctx, &req); err != nil {
return nil, err
return nil, nil, err
}
// Retrieve cert
if info, err = clnt.CertificateRetrieve(ctx, serial); err != nil {
return nil, err
return nil, nil, err
}
// Retrieve ca chain
if caChainList, err = clnt.TrustChain(ctx); err != nil {
return nil, nil, err
}

// Convert CA Chain into PEM
var caChain []byte
for _, cert := range caChainList {
var certPEM = pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
})

caChain = append(caChain, certPEM...)
}

return pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: info.X509.Raw,
}), nil
Type: "CERTIFICATE",
Bytes: info.X509.Raw,
}),
caChain,
nil
}

0 comments on commit 26da84b

Please sign in to comment.