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).
Basic flow steps:
- The flow accepts an HTTP POST request named create-csr.
- 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.
- 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.
- 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.
- 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.
- 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.
- Through function nodes, we take the outputs of the 2 MinIO nodes and create an output containing the 2 URLs generated.
- 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}]