A custom node CertificateSigningRequest approver for kubernetes clusters running on AWS.
kubelet TLS bootstrapping allows kubelet to create CertificateSigningRequest for its client certs. These CertificateSigningRequest need to be approved so as to be signed by kubernetes internal certificate signer controller.
Currently you can either manually approve each node that tries to join the cluster. Or, instruct kubernetes internal certificate approver to approve all certificates from system:bootstrappers
group. The human intervention required for manual approval is not ideal in auto scaling environments. And the default auto approver performs very basic checks that might not be very secure.
kube-aws-approver
goes a step further from default auto approver and uses the CertificateSigningRequest generated by node to extract information about the node requesting the certificate, and tries to validate it against AWS API before approving the CertificateSigningRequest.
kube-aws-approver matches incoming CertificateSigningRequest against two critera:
- The CertificateRequest
Organization
is equal tosystem:nodes
- The CertificateRequest
DNSNames
list is empty - The CertificateRequest
EmailAddresses
list is empty - The CertificateRequest
IPAddresses
list is empty - The CertificateRequest
CommonName
hassystem:node:
prefix - The CertificateSigningRequest
Username
matches the CertificateRequestCommonName
, i.e. the node provided its client certificate to create this CertificateSigningRequest. - The CertificateSigningRequest
Groups
hassystem:nodes
- There exists an
instance
inrunning state
corresponding to thenodename
from the CertificateRequestCommonName
- A
node
with thenodename
from CertificateRequest CommonName ispart
of the cluster and inReady
state. - The
instance
should belong to an AutoScalingGroup. - The AutoSaclingGroup belongs to the list of whitelisted AutoScalingGroups provided to
kube-aws-approver
at startup.
Only when a CertificateSigningRequest from a node looking to renew its client certificate meets all
the above requirements, the CertificateSigningRequest is Approved
more
- The CertificateRequest
Organization
is equal tosystem:nodes
- The CertificateRequest
DNSNames
list is empty - The CertificateRequest
EmailAddresses
list is empty - The CertificateRequest
IPAddresses
list is empty - The CertificateRequest
CommonName
hassystem:node:
prefix - The CertificateSigningRequest
Groups
hassystem:bootstrappers
- There exists an instance in
running state
corresponding to thenodename
from the CertificateRequestCommonName
- The CertificateSigningRequest
Username
shoudd be of the formsystem:bootstrappers:<aws-instance-id>
How you might achieve this? - The
instance-id
extracted from CertificateSigningRequestUsername
matches theinstance-id
extracted from AWS API for thenodename
in CertificateRequestCommonName
- The node with
nodename
from CertificateRequest CommonName isnot part
of the cluster. - The
instance
should belong to an AutoScalingGroup. - The AutoSaclingGroup belongs to the list of whitelisted AutoScalingGroups provided to
kube-aws-approver
at startup.
Only when a CertificateSigningRequest from a new node looking for its client certificate meets all
the above requirements, the CertificateSigningRequest is Approved
more
kube-aws-approver
only approves a CertificateSigningRequest that meets one of these two criteria. It never explicitly declines any CertificateSigningRequest.
The approved CertificateSigningRequest is then signed by the kube-controller-manager and can be used by the node as client certificate.
The following example go code shows how the kube-aws-approver
marks a CertificateSigningRequest approved.
...
...
// csr is the CertificateSigningRequest(https://godoc.org/k8s.io/api/certificates/v1beta1#CertificateSigningRequest) object received by the approver.
csr.Status.Conditions = append(csr.Status.Conditions, CertificateSigningRequestCondition{
Type: CertificateApproved,
Reason: "AutoApproved",
Message: successMessage,
})
_, err = client.CertificatesV1beta1().CertificateSigningRequests().UpdateApproval(csr)
...
...
--kubeconfig
should point to a valid kubeconfig file that has access CertificateSigningRequest.
--region-name
should be set to the AWS region where the instances and autoscalinggroups are running.
The kube-aws-approver
uses the following priority for the source of credentials to contact AWS API. ENV
variables > ~/.aws/
config file > EC2Role
provided credentials.
--allowed-asgs
should contain a comma separated list of the names of AutoScalingGroups that instances requesting to join the cluster can belong to.
TODO(abhinav): run as a container.
Either --kubeconfig
should point to a valid kubeconfig file that has access CertificateSigningRequest. Or
If the --kubeconfig
is empty, kube-aws-approver
will use InCluster Config to contact the apiserver.
Either --region-name
should be set to the AWS region where the instances and autoscalinggroups are running. Or
If the --region-name
is empty, kube-aws-approver
will fetch the AWS region from instance metadata service.
The kube-aws-approver
uses the following priority for the source of credentials to contact AWS API. ENV
variables > ~/.aws/
config file > EC2Role
provided credentials. Recommended approach is to use EC2Role as credentials.
--allowed-asgs
should contain a comma separated list of the names of AutoScalingGroups that instances requesting to join the cluster can belong to.
TODO(abhinav): run as a deployment, with service account, cluster role bindings and config map for allowed ASGs.