Sign X.509 CSR

This flow accepts an input with the data needed to produce a signed certificate (CA certificate, CA private key, certificate request) and produces an output with a URL where we can download the generated file (certificate).

image

Basic flow steps:

  1. The flow accepts an HTTP POST request named sign-csr.
  2. The CSR is signed by a Certificate Authority (CA). In order to sign the CSR, in addition to the CSR itself, it is necessary to have the certificate and the private key of the CA, so we will retrieve them from a MinIO bucket that we have named "certificate-authority".
  3. After we have all the necessary components, we define the payload in the format we want (Format Output funtion node) in order to pass the elements we need to the OpenWhisk action.
  4. An HTTP POST request is called which calls the action we have created in OpenWhisk (csr-sign). The action processes the data we entered and produces the CA-signed certificate.
  5. A check is made in MinIO if there is a bucket with the name of the Common Name of the certificate through the "Check if bucket exists" subflow. If the bucket exists, the flow continues without creating a new bucket, while if it does not exist, it creates it in place and the flow continues.
  6. Through MinIO nodes we store the certificate in the bucket and then create a presigned URL so that we can download the certificate without having to access the bucket through MinIO.
  7. Through a function node, we take the output of the MinIO node that generated the presigned URL and create an output containing the generated URL.
  8. The final HTTP response contains the URL of the certificate.

To create the OpenWhisk action the below command must be executed:

wsk action create csr-sign <csr-sign_python_file_path> --docker alexvog2/openwhisk-python-ssl

We can test the service with curl:

curl --location 'http://192.168.56.116:1880/sign-csr' \
--header 'Content-Type: multipart/form-data' \
-F "csr=< /home/alex/python_scripts/demo/csr.csr" \
--form 'not_valid_before=2023-06-20' \
--form 'not_valid_after=2024-06-20'

Sample output:

{
"crt":"http://192.168.56.116:9000/example.fr/crt.crt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=alex%2F20230618%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230618T215631Z&X-Amz-Expires=100&X-Amz-SignedHeaders=host&X-Amz-Signature=26fb780a7febc6160cd8ad385b7bdbd9404344edf59504d4ac3808d2e9504bce"
}

The code that runs in the OpenWhisk action csr-create is a Python script that can be found at the GitHub link below: https://github.com/alexvog/python_x509_certificates/blob/main/csr_sign.py

The OpenWhisk runtime that runs the Python script is a Docker image that can be found at the Dockerhub link below: https://hub.docker.com/r/alexvog2/openwhisk-python-ssl

[{"id":"cc9d32ea61bdb0bf","type":"subflow","name":"Check if bucket exists","info":"","category":"","in":[{"x":160,"y":120,"wires":[{"id":"bf7b1fb1353b9cb2"}]}],"out":[{"x":600,"y":200,"wires":[{"id":"b2cfbb924c2a1c92","port":1},{"id":"6fa3f0490b80d9fb","port":0}]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"bf7b1fb1353b9cb2","type":"minio-buckets","z":"cc9d32ea61bdb0bf","buckets_name":"Check if exists","host":"fdd33728.66ed4","buckets_operation":"bucketExists","buckets_bucket":"{{bucketName}}","buckets_region":"","buckets_prefix":"","buckets_recursive":false,"buckets_start_after":"","x":280,"y":120,"wires":[["b2cfbb924c2a1c92"],[]]},{"id":"b2cfbb924c2a1c92","type":"switch","z":"cc9d32ea61bdb0bf","name":"","property":"payload.bucketExists","propertyType":"msg","rules":[{"t":"false"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":430,"y":120,"wires":[["6fa3f0490b80d9fb"],[]]},{"id":"6fa3f0490b80d9fb","type":"minio-buckets","z":"cc9d32ea61bdb0bf","buckets_name":"Create Bucket","host":"fdd33728.66ed4","buckets_operation":"makeBucket","buckets_bucket":"{{bucketName}}","buckets_region":"","buckets_prefix":"","buckets_recursive":false,"buckets_start_after":"","x":580,"y":100,"wires":[[],[]]},{"id":"231c43a2496a70b9","type":"minio-files","z":"f0392a87c6aad9b5","files_name":"Retrieve CA","host":"fdd33728.66ed4","files_operation":"fGetObject","files_bucket":"certificate-autority","files_object":"myCA.pem","files_filepath":"/data/myCA.pem","files_metadata":"","x":290,"y":60,"wires":[["81c366e90f90ed72"],[]]},{"id":"81c366e90f90ed72","type":"file in","z":"f0392a87c6aad9b5","name":"Save CA","filename":"/data/myCA.pem","filenameType":"str","format":"utf8","chunk":false,"sendError":false,"encoding":"ascii","allProps":false,"x":480,"y":60,"wires":[["f9a1a985d67c8340"]]},{"id":"8c4c44970a7c09e5","type":"http in","z":"f0392a87c6aad9b5","name":"","url":"/sign-csr","method":"post","upload":true,"swaggerDoc":"","x":120,"y":100,"wires":[["231c43a2496a70b9","8a18eddb6a95e003","d469ee0374a11acc"]]},{"id":"5d25628feb3235b3","type":"http response","z":"f0392a87c6aad9b5","name":"","statusCode":"","headers":{},"x":790,"y":520,"wires":[]},{"id":"8a18eddb6a95e003","type":"minio-files","z":"f0392a87c6aad9b5","files_name":"Retrieve CA pkey","host":"fdd33728.66ed4","files_operation":"fGetObject","files_bucket":"certificate-autority","files_object":"myCA.key","files_filepath":"/data/myCA.key","files_metadata":"","x":310,"y":100,"wires":[["59a4eb1470bcdbf6"],[]]},{"id":"59a4eb1470bcdbf6","type":"file in","z":"f0392a87c6aad9b5","name":"Save CA pkey","filename":"/data/myCA.key","filenameType":"str","format":"utf8","chunk":false,"sendError":false,"encoding":"ascii","allProps":false,"x":500,"y":100,"wires":[["266d5e430fb6c81a"]]},{"id":"4c561e0fc81a0875","type":"function","z":"f0392a87c6aad9b5","name":"Format Output","func":"var cacrt = msg.cacrt;\nvar cakey = msg.cakey;\nvar csr_data = msg.csr_data;\n\nmsg.payload = {\n    \"cacrt\": cacrt,\n    \"cakey\": cakey,\n    \"csr_data\": csr_data\n};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":440,"y":220,"wires":[["27a77f4de8653b33"]]},{"id":"3f300cdf234b5832","type":"join","z":"f0392a87c6aad9b5","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"3","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":850,"y":140,"wires":[["4c561e0fc81a0875"]]},{"id":"f9a1a985d67c8340","type":"function","z":"f0392a87c6aad9b5","name":"CA","func":"msg.cacrt = msg.payload;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":670,"y":60,"wires":[["3f300cdf234b5832","5d0ab4f76da84e66"]]},{"id":"266d5e430fb6c81a","type":"function","z":"f0392a87c6aad9b5","name":"CA pkey","func":"msg.cakey = msg.payload;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":100,"wires":[["3f300cdf234b5832","fca473028e2371f5"]]},{"id":"d469ee0374a11acc","type":"function","z":"f0392a87c6aad9b5","name":"CSR data","func":"msg.csr_data = msg.payload;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":280,"y":140,"wires":[["3f300cdf234b5832"]]},{"id":"27a77f4de8653b33","type":"function","z":"f0392a87c6aad9b5","name":"Add Credentials","func":"msg.url = 'http://172.17.0.1:3233/api/v1/namespaces/guest/actions/csr-sign?blocking=true';\nmsg.creds = '23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP';\n\nvar auth = 'Basic ' + new Buffer(msg.creds).toString('base64');\nmsg.headers = {\n    \"Authorization\": auth\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":620,"y":220,"wires":[["6f8d1850781797f7"]]},{"id":"6f8d1850781797f7","type":"http request","z":"f0392a87c6aad9b5","name":"","method":"POST","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"basic","senderr":false,"headers":[],"x":430,"y":280,"wires":[["cf42081c047b26da"]]},{"id":"5d0ab4f76da84e66","type":"file","z":"f0392a87c6aad9b5","name":"Delete CA","filename":"/data/myCA.pem","filenameType":"str","appendNewline":true,"createDir":false,"overwriteFile":"delete","encoding":"none","x":850,"y":60,"wires":[[]]},{"id":"fca473028e2371f5","type":"file","z":"f0392a87c6aad9b5","name":"Delete CA pkey","filename":"/data/myCA.key","filenameType":"str","appendNewline":true,"createDir":false,"overwriteFile":"delete","encoding":"none","x":860,"y":100,"wires":[[]]},{"id":"cf42081c047b26da","type":"function","z":"f0392a87c6aad9b5","name":"Retrieve bucketName / certificate","func":"msg.bucketName = msg.payload.response.result.common_name;\nmsg.certificate = msg.payload.response.result.certificate;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":280,"wires":[["d12b34d7f0d375ee"]]},{"id":"958a532f1429f0df","type":"minio-files","z":"f0392a87c6aad9b5","files_name":"Store CRT","host":"fdd33728.66ed4","files_operation":"fPutObject","files_bucket":"{{bucketName}}","files_object":"crt.crt","files_filepath":"/data/crt.crt","files_metadata":"","x":310,"y":500,"wires":[["69f0ca144e14a5a0","bcb6645f6ef58f92"],[]]},{"id":"01d98ea676fb6c35","type":"file","z":"f0392a87c6aad9b5","name":"Save CRT","filename":"/data/crt.crt","filenameType":"str","appendNewline":false,"createDir":false,"overwriteFile":"true","encoding":"utf8","x":590,"y":340,"wires":[["c826bddaa079d1ed"]]},{"id":"d12b34d7f0d375ee","type":"change","z":"f0392a87c6aad9b5","name":"Extract CRT","rules":[{"t":"set","p":"payload","pt":"msg","to":"certificate","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":340,"wires":[["01d98ea676fb6c35"]]},{"id":"69f0ca144e14a5a0","type":"file","z":"f0392a87c6aad9b5","name":"Delete CRT","filename":"/data/crt.crt","filenameType":"str","appendNewline":true,"createDir":false,"overwriteFile":"delete","encoding":"none","x":490,"y":480,"wires":[[]]},{"id":"bcb6645f6ef58f92","type":"minio-presigned","z":"f0392a87c6aad9b5","presigned_name":"CRT URL","host":"fdd33728.66ed4","presigned_operation":"presignedGetObject","presigned_bucket":"{{bucketName}}","presigned_object":"crt.crt","presigned_expiry":"100","presigned_req_params":"","presigned_resp_headers":"","presigned_issue":"","presigned_policy_bucket":"","presigned_policy_key":"","presigned_policy_key_prefix":"","presigned_policy_expires":"","presigned_policy_content_type":"","presigned_policy_content_length_range_from":"","presigned_policy_content_length_range_to":"","x":480,"y":520,"wires":[["c041797bd2ac9383"],[]]},{"id":"c041797bd2ac9383","type":"function","z":"f0392a87c6aad9b5","name":"Set Response","func":"msg.payload = {\n    crt: msg.payload.presignedGetObject\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":640,"y":520,"wires":[["5d25628feb3235b3"]]},{"id":"c826bddaa079d1ed","type":"subflow:cc9d32ea61bdb0bf","z":"f0392a87c6aad9b5","name":"","x":500,"y":400,"wires":[["958a532f1429f0df"]]},{"id":"fdd33728.66ed4","type":"minio-config","name":"MinIO Instance","host":"192.168.56.110","port":"9000","useSsl":false}]

Flow Info

Created 1 year, 7 months ago
Updated 1 year, 6 months ago
Rating: not yet rated

Owner

Actions

Rate:

Node Types

Core
  • change (x1)
  • file (x4)
  • file in (x2)
  • function (x7)
  • http in (x1)
  • http request (x1)
  • http response (x1)
  • join (x1)
  • switch (x1)
Other
  • minio-buckets (x2)
  • minio-config (x1)
  • minio-files (x3)
  • minio-presigned (x1)
  • subflow (x1)
  • subflow:cc9d32ea61bdb0bf (x1)

Tags

  • node-red
  • openwhisk
  • x.509
  • certificates
  • csr
  • sign
  • CA
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option