Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request]: Unable to Issue a CA certificate with the desired pathlen constraint value #220

Open
2 tasks done
find-arka opened this issue Nov 1, 2022 · 11 comments
Labels
enhancement New feature or request

Comments

@find-arka
Copy link

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 of cert-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

  1. Create an EKS Cluster.

  2. 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
  1. 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
  1. 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}"
  1. 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;
  1. 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
  1. 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
  1. 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
  1. 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?

Category

Supported Workflow Broken

Severity

Severity 3

@find-arka find-arka added the bug Something isn't working label Nov 1, 2022
@divyansh-gupta
Copy link
Contributor

divyansh-gupta commented Nov 1, 2022

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.

@divyansh-gupta
Copy link
Contributor

Related: #98

@find-arka
Copy link
Author

Hello @divyansh-gupta, Thank you for the quick response! Looking forward to hearing more about the team-discussion outcomes on this.

@divyansh-gupta
Copy link
Contributor

Hi @find-arka, discussed with the team, there are several things we can do here:

  1. 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.

  2. 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?

@divyansh-gupta divyansh-gupta added enhancement New feature or request and removed bug Something isn't working labels Nov 3, 2022
@divyansh-gupta divyansh-gupta changed the title [Bug]: Unable to Issue a CA certificate with the desired pathlen constraint value [Feature Request]: Unable to Issue a CA certificate with the desired pathlen constraint value Nov 10, 2022
@find-arka
Copy link
Author

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:

  1. 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.

@find-arka
Copy link
Author

Hello @divyansh-gupta ,
Hope you are doing well! Any thoughts on how this could be taken forward?

@bmsiegel
Copy link
Contributor

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!

@dcamzn
Copy link

dcamzn commented Mar 30, 2023

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?

@divyansh-gupta
Copy link
Contributor

Just to add to that, the annotation would be something like:

kind: Certificate
  metadata:
    annotations:
      acm-pca.template-arn: templateArn

@find-arka
Copy link
Author

@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 an Issuer to use the above created Subordinate CA, end user decides to put annotation acm-pca.template-arn: SubordinateCACertificate_PathLen3/V1
  • From pathlen: 2 Issuing cert, can we create a CA cert with pathlen: 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 the Certificate.spec.isCA is set to true, 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?

@dcamzn
Copy link

dcamzn commented Mar 31, 2023

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants