Generate X.509 CSR

This flow accepts an input with the data needed to produce a certificate request (Common Name, Country, State, Locality, Organization, Organizational Unit, key size) and produces an output with URLs where we can download the generated files (CSR, private key).

image

Basic flow steps:

  1. The flow accepts an HTTP POST request named create-csr.
  2. We have set the flow to store the data in a MinIO bucket named with the CSR's Common Name (e.g. example.com), so a check is made to see if there is a bucket with this name in MinIO 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.
  3. An HTTP POST request is called which calls the action we have created in OpenWhisk (csr-create). The action processes the data we entered and generates a private key and a certificate request.
  4. A JSON node receives the output of the HTTP response (payload.response.result.result) containing the processed data and converts it from string to JSON format so that we can process it.
  5. After the output of the JSON node we have added nodes that extract the private key and the certificate request and store them in the previously created bucket.
  6. For the private key and the certificate request that are now stored in the bucket, we want to create presigned URLs so that we can download them without having to access the bucket through MinIO, so we have added MinIO nodes that create presigned URLs.
  7. Through function nodes, we take the outputs of the 2 MinIO nodes and create an output containing the 2 URLs generated.
  8. The final HTTP response contains the URLs of the private key and CSR.

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

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

We can test the service with curl:

curl --location 'http://192.168.56.116:1880/create-csr' \
--header 'Content-Type: application/json' \
--data '{
"csr_data" : {
"common_name" : "example.fr",
"country" : "US",
"state" : "Nevada",
"locality" : "City",
"organization" : "Example Organization",
"organizational_unit" : "IT"
},
"key_size" : 2048
}'

Sample output:

{
"keyURL":"http://192.168.56.116:9000/example.com/private_key.key?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=alex%2F20230606%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230606T222445Z&X-Amz-Expires=100&X-Amz-SignedHeaders=host&X-Amz-Signature=7b5402f5ec0c522ad948e3851926478f7e814230510c6fb93415963d692a301e",
"csrURL":"http://192.168.56.116:9000/example.com/csr.csr?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=alex%2F20230606%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230606T222445Z&X-Amz-Expires=100&X-Amz-SignedHeaders=host&X-Amz-Signature=93a8d7c0088ed71f986e0236be522e9aa403f4351964e7ef98f7d2734d2c68d2"
}

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_generate.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":"57470595e10a19d7","type":"http in","z":"62b69813b10e3632","name":"","url":"/create-csr","method":"post","upload":false,"swaggerDoc":"","x":140,"y":40,"wires":[["df76d4e00846d91b"]]},{"id":"8853f598c085b063","type":"http response","z":"62b69813b10e3632","name":"","statusCode":"","headers":{},"x":870,"y":340,"wires":[]},{"id":"a5e010d94bded55d","type":"json","z":"62b69813b10e3632","name":"","property":"payload.response.result.result","action":"obj","pretty":false,"x":190,"y":340,"wires":[["5c69d0c1171144b0","2a6d29187dc1f805"]]},{"id":"77dad73a9c5239af","type":"file","z":"62b69813b10e3632","name":"Save Private Key","filename":"/data/private_key.key","filenameType":"str","appendNewline":false,"createDir":false,"overwriteFile":"true","encoding":"utf8","x":370,"y":260,"wires":[["8874a9b6dd64ca60"]]},{"id":"b00c4bdcd4bc8830","type":"file","z":"62b69813b10e3632","name":"Save CSR","filename":"/data/csr.csr","filenameType":"str","appendNewline":false,"createDir":false,"overwriteFile":"true","encoding":"utf8","x":370,"y":420,"wires":[["cffff7e3a816a0f5"]]},{"id":"5c69d0c1171144b0","type":"change","z":"62b69813b10e3632","name":"Extract Private Key","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.response.result.result.private_key","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":300,"wires":[["77dad73a9c5239af"]]},{"id":"2a6d29187dc1f805","type":"change","z":"62b69813b10e3632","name":"Extract CSR","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.response.result.result.csr","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":380,"wires":[["b00c4bdcd4bc8830"]]},{"id":"1fd50371508ec8a3","type":"http request","z":"62b69813b10e3632","name":"","method":"POST","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"basic","senderr":false,"headers":[],"x":130,"y":180,"wires":[["a5e010d94bded55d"]]},{"id":"a96b04420df71f1c","type":"file","z":"62b69813b10e3632","name":"Delete Private Key","filename":"/data/private_key.key","filenameType":"str","appendNewline":true,"createDir":false,"overwriteFile":"delete","encoding":"none","x":570,"y":180,"wires":[[]]},{"id":"a4174a2a88ed94d0","type":"file","z":"62b69813b10e3632","name":"Delete CSR","filename":"/data/csr.csr","filenameType":"str","appendNewline":true,"createDir":false,"overwriteFile":"delete","encoding":"none","x":550,"y":500,"wires":[[]]},{"id":"8874a9b6dd64ca60","type":"minio-files","z":"62b69813b10e3632","files_name":"Store Private Key","host":"fdd33728.66ed4","files_operation":"fPutObject","files_bucket":"{{bucketName}}","files_object":"private_key.key","files_filepath":"/data/private_key.key","files_metadata":"","x":370,"y":220,"wires":[["a96b04420df71f1c","4691074631b7a877"],[]]},{"id":"cffff7e3a816a0f5","type":"minio-files","z":"62b69813b10e3632","files_name":"Store CSR","host":"fdd33728.66ed4","files_operation":"fPutObject","files_bucket":"{{bucketName}}","files_object":"csr.csr","files_filepath":"/data/csr.csr","files_metadata":"","x":370,"y":460,"wires":[["ccdd762841086592","a4174a2a88ed94d0"],[]]},{"id":"4691074631b7a877","type":"minio-presigned","z":"62b69813b10e3632","presigned_name":"Private Key URL","host":"fdd33728.66ed4","presigned_operation":"presignedGetObject","presigned_bucket":"{{bucketName}}","presigned_object":"private_key.key","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":560,"y":220,"wires":[["8c40c8d872876bcc"],[]]},{"id":"ccdd762841086592","type":"minio-presigned","z":"62b69813b10e3632","presigned_name":"CSR URL","host":"fdd33728.66ed4","presigned_operation":"presignedGetObject","presigned_bucket":"{{bucketName}}","presigned_object":"csr.csr","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":540,"y":460,"wires":[["621964c3bdc85f94"],[]]},{"id":"621964c3bdc85f94","type":"function","z":"62b69813b10e3632","name":"CSR URL Message","func":"msg.csrURL= msg.payload.presignedGetObject\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":710,"y":460,"wires":[["4b01336c8a8377ee"]]},{"id":"636c57367412785f","type":"function","z":"62b69813b10e3632","name":"URLs JSON","func":"msg.payload = {\n  pkeyURL: msg.pkeyURL,\n  csrURL: msg.csrURL\n};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":730,"y":340,"wires":[["8853f598c085b063"]]},{"id":"8c40c8d872876bcc","type":"function","z":"62b69813b10e3632","name":"Private Key URL Message","func":"msg.pkeyURL = msg.payload.presignedGetObject;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":220,"wires":[["4b01336c8a8377ee"]]},{"id":"4b01336c8a8377ee","type":"join","z":"62b69813b10e3632","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":590,"y":340,"wires":[["636c57367412785f"]]},{"id":"df76d4e00846d91b","type":"function","z":"62b69813b10e3632","name":"Add Credentials","func":"msg.url = 'http://172.17.0.1:3233/api/v1/namespaces/guest/actions/csr-create?blocking=true';\nmsg.creds = '23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP';\n\nmsg.bucketName = msg.payload.csr_data.common_name;\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":340,"y":40,"wires":[["da356e548ae48475","8f99c793ec4401ca"]]},{"id":"da356e548ae48475","type":"join","z":"62b69813b10e3632","name":"","mode":"custom","build":"merged","property":"payload","propertyType":"msg","key":"payload","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":130,"y":120,"wires":[["1fd50371508ec8a3"]]},{"id":"8f99c793ec4401ca","type":"subflow:cc9d32ea61bdb0bf","z":"62b69813b10e3632","name":"","x":580,"y":40,"wires":[["da356e548ae48475"]]},{"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 (x2)
  • file (x4)
  • function (x4)
  • http in (x1)
  • http request (x1)
  • http response (x1)
  • join (x2)
  • json (x1)
  • switch (x1)
Other
  • minio-buckets (x2)
  • minio-config (x1)
  • minio-files (x2)
  • minio-presigned (x2)
  • subflow (x1)
  • subflow:cc9d32ea61bdb0bf (x1)

Tags

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