Add IPs to kubelet server cert

Since AWS does not resolve instance hostnames to ipv6, ipv6-only pods that talk to kubelet API has to use node IP, not hostname. Thus we need to add IPs to kubelet server cert.
This commit is contained in:
Ole Markus With 2021-08-21 13:28:10 +02:00
parent 20e472eded
commit ad16042a1f
5 changed files with 78 additions and 5 deletions

View File

@ -203,7 +203,7 @@ func (s *Server) issueCert(name string, pubKey string, id *fi.VerifyResult, vali
issueReq.Subject = pkix.Name{
CommonName: id.NodeName,
}
issueReq.AlternateNames = []string{id.NodeName}
issueReq.AlternateNames = id.CertificateNames
issueReq.Type = "server"
case "kube-proxy":
issueReq.Subject = pkix.Name{

View File

@ -18,6 +18,8 @@ package model
import (
"fmt"
"net"
"os"
"path"
"path/filepath"
@ -25,6 +27,7 @@ import (
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
v1 "k8s.io/api/core/v1"
"k8s.io/klog/v2"
@ -560,7 +563,7 @@ func (b *KubeletBuilder) buildKubeletServingCertificate(c *fi.ModelBuilderContex
name := "kubelet-server"
dir := b.PathSrvKubernetes()
nodeName, err := b.NodeName()
names, err := b.kubeletNames()
if err != nil {
return err
}
@ -594,9 +597,9 @@ func (b *KubeletBuilder) buildKubeletServingCertificate(c *fi.ModelBuilderContex
KeypairID: b.NodeupConfig.KeypairIDs[fi.CertificateIDCA],
Type: "server",
Subject: nodetasks.PKIXName{
CommonName: nodeName,
CommonName: names[0],
},
AlternateNames: []string{nodeName},
AlternateNames: names,
}
c.AddTask(issueCert)
return issueCert.AddFileTasks(c, dir, name, "", nil)
@ -605,3 +608,27 @@ func (b *KubeletBuilder) buildKubeletServingCertificate(c *fi.ModelBuilderContex
return nil
}
func (b *KubeletBuilder) kubeletNames() ([]string, error) {
if kops.CloudProviderID(b.Cluster.Spec.CloudProvider) != kops.CloudProviderAWS {
name, err := os.Hostname()
if err != nil {
return nil, err
}
addrs, _ := net.LookupHost(name)
return append(addrs, name), nil
}
cloud := b.Cloud.(awsup.AWSCloud)
result, err := cloud.EC2().DescribeInstances(&ec2.DescribeInstancesInput{
InstanceIds: []*string{&b.InstanceID},
})
if err != nil {
return nil, fmt.Errorf("error describing instances: %v", err)
}
return awsup.GetInstanceCertificateNames(result)
}

View File

@ -28,6 +28,9 @@ type VerifyResult struct {
// InstanceGroupName is the name of the kops InstanceGroup this node is a member of.
InstanceGroupName string
// CertificateNames is the names the node is authorized to use for certificates.
CertificateNames []string
}
// Verifier verifies authentication credentials for requests.

View File

@ -2069,3 +2069,40 @@ func GetRolesInInstanceProfile(c AWSCloud, profileName string) ([]string, error)
}
return roleNames, nil
}
// GetInstanceCertificateNames returns the instance hostname and addresses that should go into certificates.
// The first value is the node name and any additional values are IP addresses.
func GetInstanceCertificateNames(instances *ec2.DescribeInstancesOutput) (addrs []string, err error) {
if len(instances.Reservations) != 1 {
return nil, fmt.Errorf("too many reservations returned for the single instance-id")
}
if len(instances.Reservations[0].Instances) != 1 {
return nil, fmt.Errorf("too many instances returned for the single instance-id")
}
instance := instances.Reservations[0].Instances[0]
name := *instance.PrivateDnsName
addrs = append(addrs, name)
// We only use data for the first interface, and only the first IP
for _, iface := range instance.NetworkInterfaces {
if iface.Attachment == nil {
continue
}
if *iface.Attachment.DeviceIndex != 0 {
continue
}
addrs = append(addrs, *iface.PrivateIpAddress)
if iface.Ipv6Addresses != nil && len(iface.Ipv6Addresses) > 0 {
addrs = append(addrs, *iface.Ipv6Addresses[0].Ipv6Address)
}
if iface.Association.PublicIp != nil {
addrs = append(addrs, *iface.Association.PublicIp)
}
}
return addrs, nil
}

View File

@ -232,8 +232,14 @@ func (a awsVerifier) VerifyToken(token string, body []byte) (*fi.VerifyResult, e
instance := instances.Reservations[0].Instances[0]
addrs, err := GetInstanceCertificateNames(instances)
if err != nil {
return nil, err
}
result := &fi.VerifyResult{
NodeName: aws.StringValue(instance.PrivateDnsName),
NodeName: addrs[0],
CertificateNames: addrs,
}
for _, tag := range instance.Tags {