Seed the random number generator on AWS

This commit is contained in:
John Gardiner Myers 2021-06-15 23:27:55 -07:00
parent 7ec956dd00
commit 42bf3ee85b
6 changed files with 86 additions and 26 deletions

View File

@ -43,11 +43,7 @@ func (b BootstrapClientBuilder) Build(c *fi.ModelBuilderContext) error {
var err error
switch kops.CloudProviderID(b.Cluster.Spec.CloudProvider) {
case kops.CloudProviderAWS:
region, regionErr := awsup.FindRegion(b.Cluster)
if regionErr != nil {
return fmt.Errorf("querying AWS region: %v", regionErr)
}
authenticator, err = awsup.NewAWSAuthenticator(region)
authenticator, err = awsup.NewAWSAuthenticator(b.Cloud.Region())
default:
return fmt.Errorf("unsupported cloud provider %s", b.Cluster.Spec.CloudProvider)
}

View File

@ -43,6 +43,8 @@ type Config struct {
InstanceGroupRole kops.InstanceGroupRole
// ClusterName is the name of the cluster
ClusterName string `json:",omitempty"`
// CloudProvider is the cloud provider in use.
CloudProvider string
// Channels is a list of channels that we should apply
Channels []string `json:"channels,omitempty"`
// ApiserverAdditionalIPs are additional IP address to put in the apiserver server cert.
@ -91,9 +93,6 @@ type ConfigServerOptions struct {
Server string `json:"server,omitempty"`
// CA is the ca-certificate to require for the configuration server
CA string `json:"ca,omitempty"`
// CloudProvider is the cloud provider in use (needed for authentication)
CloudProvider string `json:"cloudProvider,omitempty"`
}
// Image is a docker image we should pre-load

View File

@ -247,6 +247,7 @@ func (r *NodeRoleAPIServer) BuildAWSPolicy(b *PolicyBuilder) (*Policy, error) {
addMasterEC2Policies(p, resource, b.Cluster.GetName())
addASLifecyclePolicies(p, resource, b.Cluster.GetName(), r.warmPool)
addCertIAMPolicies(p, resource)
addKMSGenerateRandomPolicies(p)
var err error
if p, err = b.AddS3Permissions(p); err != nil {
@ -293,6 +294,7 @@ func (r *NodeRoleMaster) BuildAWSPolicy(b *PolicyBuilder) (*Policy, error) {
addMasterASPolicies(p, resource, b.Cluster.GetName())
addMasterELBPolicies(p, resource)
addCertIAMPolicies(p, resource)
addKMSGenerateRandomPolicies(p)
var err error
if p, err = b.AddS3Permissions(p); err != nil {
@ -354,6 +356,7 @@ func (r *NodeRoleNode) BuildAWSPolicy(b *PolicyBuilder) (*Policy, error) {
addNodeEC2Policies(p, resource)
addASLifecyclePolicies(p, resource, b.Cluster.GetName(), r.enableLifecycleHookPermissions)
addKMSGenerateRandomPolicies(p)
var err error
if p, err = b.AddS3Permissions(p); err != nil {
@ -866,6 +869,17 @@ func addKMSIAMPolicies(p *Policy, resource stringorslice.StringOrSlice) {
})
}
func addKMSGenerateRandomPolicies(p *Policy) {
// For nodeup to seed the instance's random number generator.
p.Statement = append(p.Statement, &Statement{
Effect: StatementEffectAllow,
Action: stringorslice.Of(
"kms:GenerateRandom",
),
Resource: stringorslice.Slice([]string{"*"}),
})
}
func addNodeEC2Policies(p *Policy, resource stringorslice.StringOrSlice) {
// Protokube makes a DescribeInstances call, DescribeRegions when finding S3 State Bucket
p.Statement = append(p.Statement, &Statement{

View File

@ -1319,6 +1319,7 @@ func (n *nodeUpConfigBuilder) BuildConfig(ig *kops.InstanceGroup, apiserverAddit
}
config.ClusterName = cluster.ObjectMeta.Name
config.InstanceGroupName = ig.ObjectMeta.Name
config.CloudProvider = cluster.Spec.CloudProvider
if isMaster || useGossip {
for _, arch := range architectures.GetSupported() {
@ -1349,9 +1350,8 @@ func (n *nodeUpConfigBuilder) BuildConfig(ig *kops.InstanceGroup, apiserverAddit
}
configServer := &nodeup.ConfigServerOptions{
Server: baseURL.String(),
CloudProvider: cluster.Spec.CloudProvider,
CA: ca,
Server: baseURL.String(),
CA: ca,
}
config.ConfigServer = configServer

View File

@ -30,6 +30,7 @@ go_library(
"//vendor/github.com/aws/aws-sdk-go/aws/session:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/autoscaling:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/ec2:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/kms:go_default_library",
"//vendor/k8s.io/klog/v2:go_default_library",
],
)

View File

@ -26,11 +26,13 @@ import (
"io/ioutil"
"net"
"net/url"
"os"
"os/exec"
"strconv"
"strings"
"time"
"github.com/aws/aws-sdk-go/service/kms"
"k8s.io/kops/nodeup/pkg/model"
"k8s.io/kops/nodeup/pkg/model/networking"
api "k8s.io/kops/pkg/apis/kops"
@ -91,13 +93,21 @@ func (c *NodeUpCommand) Run(out io.Writer) error {
return fmt.Errorf("CacheDir is required")
}
region, err := getRegion(ctx, c.config)
if err != nil {
return err
}
if err = seedRNG(ctx, c.config, region); err != nil {
return err
}
var configBase vfs.Path
// If we're using a config server instead of vfs, nodeConfig will hold our configuration
var nodeConfig *nodeup.NodeConfig
if c.config.ConfigServer != nil {
response, err := getNodeConfigFromServer(ctx, c.config.ConfigServer)
response, err := getNodeConfigFromServer(ctx, c.config, region)
if err != nil {
return err
}
@ -182,7 +192,7 @@ func (c *NodeUpCommand) Run(out io.Writer) error {
return fmt.Errorf("auxiliary config hash mismatch")
}
err := evaluateSpec(c)
err = evaluateSpec(c)
if err != nil {
return err
}
@ -209,10 +219,6 @@ func (c *NodeUpCommand) Run(out io.Writer) error {
var cloud fi.Cloud
if api.CloudProviderID(c.cluster.Spec.CloudProvider) == api.CloudProviderAWS {
region, err := awsup.FindRegion(c.cluster)
if err != nil {
return err
}
awsCloud, err := awsup.NewAWSCloud(region, nil)
if err != nil {
return err
@ -674,16 +680,60 @@ func loadKernelModules(context *model.NodeupModelContext) error {
return nil
}
// getNodeConfigFromServer queries kops-controller for our node's configuration.
func getNodeConfigFromServer(ctx context.Context, config *nodeup.ConfigServerOptions) (*nodeup.BootstrapResponse, error) {
var authenticator fi.Authenticator
// getRegionAndSeedRNG queries the cloud provider for the region and adds entropy to the random number generator.
func getRegion(ctx context.Context, config *nodeup.Config) (string, error) {
switch api.CloudProviderID(config.CloudProvider) {
case api.CloudProviderAWS:
region, err := awsup.RegionFromMetadata(ctx)
if err != nil {
return nil, err
return "", err
}
return region, nil
}
return "", nil
}
// seedRNG adds entropy to the random number generator.
func seedRNG(ctx context.Context, config *nodeup.Config, region string) error {
switch api.CloudProviderID(config.CloudProvider) {
case api.CloudProviderAWS:
config := aws.NewConfig().WithCredentialsChainVerboseErrors(true).WithRegion(region)
sess, err := session.NewSession(config)
if err != nil {
return err
}
random, err := kms.New(sess, config).GenerateRandom(&kms.GenerateRandomInput{
NumberOfBytes: aws.Int64(64),
})
if err != nil {
return fmt.Errorf("generating random seed: %v", err)
}
f, err := os.OpenFile("/dev/urandom", os.O_WRONLY, 0)
if err != nil {
return fmt.Errorf("opening /dev/urandom: %v", err)
}
_, err = f.Write(random.Plaintext)
if err1 := f.Close(); err1 != nil && err == nil {
err = err1
}
if err != nil {
return fmt.Errorf("writing /dev/urandom: %v", err)
}
}
return nil
}
// getNodeConfigFromServer queries kops-controller for our node's configuration.
func getNodeConfigFromServer(ctx context.Context, config *nodeup.Config, region string) (*nodeup.BootstrapResponse, error) {
var authenticator fi.Authenticator
switch api.CloudProviderID(config.CloudProvider) {
case api.CloudProviderAWS:
a, err := awsup.NewAWSAuthenticator(region)
if err != nil {
return nil, err
@ -697,13 +747,13 @@ func getNodeConfigFromServer(ctx context.Context, config *nodeup.ConfigServerOpt
Authenticator: authenticator,
}
if config.CA != "" {
client.CA = []byte(config.CA)
if config.ConfigServer.CA != "" {
client.CA = []byte(config.ConfigServer.CA)
}
u, err := url.Parse(config.Server)
u, err := url.Parse(config.ConfigServer.Server)
if err != nil {
return nil, fmt.Errorf("unable to parse configuration server url %q: %w", config.Server, err)
return nil, fmt.Errorf("unable to parse configuration server url %q: %w", config.ConfigServer.Server, err)
}
client.BaseURL = *u