aws-privateca-issuer
aws-privateca-issuer copied to clipboard
[Feature Request]: Unable to Issue a CA certificate with the desired pathlen constraint value
Describe the expected outcome
- I created a Root CA in AWS Private CA with the template:
RootCACertificate/V1
- Created a Subordinate CA with the help of the same Root CA. Template used for Subordinate:
SubordinateCACertificate_PathLen3/V1
- Verified that the certificate of Subordinate CA has the
pathlen:3
constraint i.e.
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:3
- Next, I created a CA certificate (using
Certificate
ofcert-manager.io/v1
). The generated CA certificate has the following constraints-
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:0
Expected a CA certificate with pathlen:2
Describe the actual outcome
The generated CA certificate had a pathlen:0
constraint, instead of the expected pathlen:2
constraint.
Is this happening due to this section in
pca.go
?
if spec.IsCA {
return prefix + "acm-pca:::template/SubordinateCACertificate_PathLen0/V1"
}
Steps to reproduce
-
Create an EKS Cluster.
-
Install Cert Manager v1.10.0
# https://cert-manager.io/docs/installation/helm/
CERT_MANAGER_VERSION=v1.10.0
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version ${CERT_MANAGER_VERSION} \
--set installCRDs=true \
--wait
- Create a Root CA and a Subordinate CA in AWS Private CA You could run this script to create a Root CA and a Subordinate CA- https://raw.githubusercontent.com/find-arka/k8s-misc/main/create-ca-hierarchy-aws-pca.sh
With the output from the script, save the Intermediate CA ARN in an environment variable-
export CA_ARN=arn:aws:acm-pca:REDACTED:REDACTED-AC:certificate-authority/REDACTED
- Setup an IAM Policy to access the CA
cat <<EOF > AWSPCAIssuerPolicyTest.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "awspcaissuer",
"Action": [
"acm-pca:DescribeCertificateAuthority",
"acm-pca:GetCertificate",
"acm-pca:IssueCertificate"
],
"Effect": "Allow",
"Resource": "${CA_ARN}"
}
]
}
EOF
POLICY_ARN=$(aws iam create-policy \
--policy-name AWSPCAIssuerPolicyTest \
--policy-document file://AWSPCAIssuerPolicyTest.json \
--output json | jq -r '.Policy.Arn')
echo "POLICY_ARN = ${POLICY_ARN}"
- Setup a k8s Service Account and an associated IAM role to access the subordinate CA.
CURRENT_CLUSTER="Please put your cluster name here"
echo "${POLICY_ARN}"
echo "${REGION}"
echo "${CURRENT_CLUSTER}"
# Currently, we are installing the plugin in the same namespace as cert-manager
export PCA_NAMESPACE=cert-manager
# latest version https://github.com/cert-manager/aws-privateca-issuer/releases
export AWSPCA_ISSUER_TAG=v1.2.2
# Enable the IAM OIDC Provider for the cluster
eksctl utils associate-iam-oidc-provider \
--cluster=${CURRENT_CLUSTER} \
--region=${REGION} \
--approve;
# Create IAM role bound to a service account
eksctl create iamserviceaccount --cluster=${CURRENT_CLUSTER} \
--region=${REGION} \
--namespace=${PCA_NAMESPACE} \
--attach-policy-arn=${POLICY_ARN} \
--override-existing-serviceaccounts \
--tags "created-by=${USER},team=${TEAM},purpose=customer-support" \
--name=aws-pca-issuer \
--role-name "ServiceAccountRolePrivateCA-${CURRENT_CLUSTER}" \
--approve;
# Install AWS Private CA Issuer Plugin
# https://github.com/cert-manager/aws-privateca-issuer/#setup
helm repo add awspca https://cert-manager.github.io/aws-privateca-issuer
helm repo update
helm install aws-pca-issuer awspca/aws-privateca-issuer \
--namespace ${PCA_NAMESPACE} \
--set image.tag=${AWSPCA_ISSUER_TAG} \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-pca-issuer \
--kube-context ${CURRENT_CLUSTER} \
--wait;
# Verify deployment status
kubectl --context ${CURRENT_CLUSTER} -n ${PCA_NAMESPACE} \
rollout status deploy/aws-pca-issuer-aws-privateca-issuer;
- Create the Issuer-
# edit the var if your CA is in a different region
export CA_REGION="YOUR CA REGION"
export CA_ARN="arn:aws:acm-pca:redacted:redacted:certificate-authority/redacted"
cat << EOF | kubectl apply -f -
apiVersion: awspca.cert-manager.io/v1beta1
kind: AWSPCAClusterIssuer
metadata:
name: my-cluster-issuer
spec:
arn: ${CA_ARN}
region: ${CA_REGION}
EOF
- Create a Certificate object
cat << EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: my-internal-ca
spec:
commonName: my-internal-ca
dnsNames:
- "*.internal.my-org.ca"
isCA: true
duration: 2160h #90d
secretName: my-internal-ca-cert-manager
subject:
organizations:
- cluster.local
- cert-manager
issuerRef:
group: awspca.cert-manager.io
kind: AWSPCAClusterIssuer
name: my-cluster-issuer
EOF
Verify:
kubectl get Certificate my-internal-ca
Expected output:
NAME READY SECRET AGE
my-internal-ca True my-internal-ca-cert-manager 4s
- extract the secret to read the value-
kubectl get secret my-internal-ca-cert-manager -o yaml | yq -r '.data."tls.crt"' | base64 -d > my-internal-ca-cert-manager-tls-crt.pem
kubectl get secret my-internal-ca-cert-manager -o yaml | yq -r '.data."ca.crt"' | base64 -d > my-internal-ca-cert-manager-ca-crt.pem
my-internal-ca-cert-manager-tls-crt.pem
has the CA cert chained with the Issuer. Extract the top section from the pem file and copy it to a different file. I named it generated-ca-cert.pem
openssl x509 -in generated-ca-cert.pem -noout -text | grep -A3 Constraint
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:0
X509v3 Authority Key Identifier:
5C:F9:5F:9D:CF:86:DD:56:94:64:36:C4:REDACTED
- Get the Subordinate cert and verify that the pathlen 3 constraint is present-
ROOT_CAARN="arn:aws:acm-pca:REDACTED:REDACTED:certificate-authority/REDACTED"
SUBORDINATE_CERTARN="arn:aws:acm-pca:REDACTED:REDACTED:certificate-authority/REDACTED/certificate/REDACTED"
aws acm-pca get-certificate \
--certificate-authority-arn "${ROOT_CAARN}" \
--certificate-arn "${SUBORDINATE_CERTARN}" \
--output json | jq -r '.Certificate' > "intermediate-cert-common-purpose.pem"
openssl x509 -in intermediate-cert-common-purpose.pem -noout -text | grep -A3 Constraint
X509v3 Basic Constraints: critical
CA:TRUE, pathlen:3
X509v3 Authority Key Identifier:
0B:97:66:22:D3:3A:FF:7D:51:10:2F:46:D1:F8:E8:E9:1D:4E:64:CA
Subordinate CA cert has pathlen:3
but the generated CA cert from that CA cert doesn't have pathlen:2
, instead it has pathlen:0
Relevant log output
N/A.
Have already attached the expected output along with the commands in the above section.
Version
Cert Manager -> v1.10.0
aws-privateca-issuer-> v1.2.2
Kubernetes -> 1.22
Amazon EKS platform version -> eks.6
Have you tried the following?
- [X] Check the Troubleshooting section
- [X] Search open issues
Category
Supported Workflow Broken
Severity
Severity 3
Hi @find-arka, thanks for the issue. I wouldn't say this is a bug - more of a missing feature. The root of the problem here is that this issuer does not allow users to set the TemplateArn themselves. This is a feature that we have discussed having, but have not committed resources to determine the best method to do so. I will discuss with the team.
Related: https://github.com/cert-manager/aws-privateca-issuer/issues/98
Hello @divyansh-gupta, Thank you for the quick response! Looking forward to hearing more about the team-discussion outcomes on this.
Hi @find-arka, discussed with the team, there are several things we can do here:
-
We can ask cert-manager to include an optional
pathLen
in their Certificate Resource API (currently unsupported https://cert-manager.io/docs/usage/certificate/) and consume that in this plugin to issue the certificate with the appropriate Private CA certificate template. -
We can make this a more general feature to allow users to specifically name the Private CA template they would like to issue with. We can do this in two ways: a. We update the Issuer Resource to take in a new optional
TemplateArn
parameter. We can then apply this template to all issuances that happen through this Issuer. This has the disadvantage that users will have to make multiple Issuers if they want to issue with more than 1 type of Private CA template.b. We talk to cert-manager and see if there is a way we can include TemplateArn as a custom API parameter in the Certificate Resource API (unlikely).
We aren't sure which is the right path forward yet, but would love to get your thoughts?
Hello @divyansh-gupta , Hope you are doing well! My sincere apologies for the late response on this. Thank you very much for your suggestions.
Had a discussion with @jmunozro , and we both felt that option 2.a sounds like the best one:
- We can make this a more general feature to allow users to specifically name the Private CA template they would like to issue with. We can do this in two ways: a. We update the Issuer Resource to take in a new optional TemplateArn parameter. We can then apply this template to all issuances that happen through this Issuer. This has the disadvantage that users will have to make multiple Issuers if they want to issue with more than 1 type of Private CA template.
Hello @divyansh-gupta , Hope you are doing well! Any thoughts on how this could be taken forward?
Hi @find-arka, we're having discussions as a team, and when we have a path forward we'll update you. If you have solutions we'll be happy to take a look at a pull request. Thanks for checking in!
Hi @find-arka, we will look into using Kubernetes annotations. With annotations, we think you can pass in the template ARN that you'd like to use. If we're right, then you can specify the a template with the path length that you need. Do you have any thoughts or feedback on our approach?
Just to add to that, the annotation would be something like:
kind: Certificate
metadata:
annotations:
acm-pca.template-arn: templateArn
@dcamzn , @divyansh-gupta Thank you for taking the discussion ahead. The annotation approach should work, but we might have to put additional validation to ensure that the generated cert has the right pathlen
constraint.
e.g. scenario:
- Subordinate CA gets created in AWS with template:
SubordinateCACertificate_PathLen2/V1
- While creating
Certificate
custom resource for a CA certificate, and using anIssuer
to use the above created Subordinate CA, end user decides to put annotationacm-pca.template-arn: SubordinateCACertificate_PathLen3/V1
- From
pathlen: 2
Issuing cert, can we create a CA cert withpathlen: 3
? Assuming that the answer is no- We would have to put a validation corresponding to the annotation value:acm-pca.template-arn: SubordinateCACertificate_PathLen3/V1
so that accidentally users don't pass a template value which has higher path len than the Issuing cert path len.
If we have to put in a validation like that, we would have to get the path len of the Issuing cert
and then compare with the requested path len of the new CA cert.
Can we do it this way?
- We fetch the
path len of the Issuing cert
. Let's call this value "n" - If the annotation
acm-pca.template-arn
is not present, and if theCertificate.spec.isCA
is set totrue
, we generate the CA cert with a pathlen "n-1" - If the annotation is present, we check if the requested path len is less than the Issuing path len or not. If its less, then we set the path len based on the requested path len.
- If the annotation is present, and the requested path len is greater than the issuing cert path len, we don't generate a cert and print the error that the requested len must be <= "n-1"
I'm sure some other boundary conditions for this validation need to be present. What do you all think?
@find-arka thanks for the feedback and reviewing our approach! We agree with you that we need to put additional validations with this approach. We will review validation requirements and come back for your thoughts.