Merge pull request #15125 from justinsb/node_challenge

Perform challenge callbacks into a node
This commit is contained in:
Kubernetes Prow Robot 2023-05-07 09:13:16 -07:00 committed by GitHub
commit 68dcc7ad48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
211 changed files with 1236 additions and 39 deletions

View File

@ -36,6 +36,8 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2"
"k8s.io/kops/cmd/kops-controller/pkg/config"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/apis/kops/model"
"k8s.io/kops/pkg/apis/nodeup"
"k8s.io/kops/pkg/bootstrap"
"k8s.io/kops/pkg/pki"
@ -61,6 +63,9 @@ type Server struct {
// uncachedClient is an uncached client for the kube apiserver
uncachedClient client.Client
// challengeClient performs our callback-challenge into the node
challengeClient *bootstrap.ChallengeClient
}
var _ manager.LeaderElectionRunnable = &Server{}
@ -94,6 +99,17 @@ func NewServer(opt *config.Options, verifier bootstrap.Verifier, uncachedClient
}
s.secretStore = secrets.NewVFSSecretStore(nil, p)
s.keystore, s.keypairIDs, err = newKeystore(opt.Server.CABasePath, opt.Server.SigningCAs)
if err != nil {
return nil, err
}
challengeClient, err := bootstrap.NewChallengeClient(s.keystore)
if err != nil {
return nil, err
}
s.challengeClient = challengeClient
r := http.NewServeMux()
r.Handle("/bootstrap", http.HandlerFunc(s.bootstrap))
server.Handler = recovery(r)
@ -106,12 +122,6 @@ func (s *Server) NeedLeaderElection() bool {
}
func (s *Server) Start(ctx context.Context) error {
var err error
s.keystore, s.keypairIDs, err = newKeystore(s.opt.Server.CABasePath, s.opt.Server.SigningCAs)
if err != nil {
return err
}
go func() {
<-ctx.Done()
@ -198,6 +208,17 @@ func (s *Server) bootstrap(w http.ResponseWriter, r *http.Request) {
return
}
if model.UseChallengeCallback(kops.CloudProviderID(s.opt.Cloud)) {
if err := s.challengeClient.DoCallbackChallenge(ctx, s.opt.ClusterName, id.ChallengeEndpoint, req); err != nil {
klog.Infof("bootstrap %s callback challenge failed: %v", r.RemoteAddr, err)
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte("callback failed"))
return
}
klog.Infof("performed successful callback challenge with %s; identified as %s", id.ChallengeEndpoint, id.NodeName)
}
resp := &nodeup.BootstrapResponse{
Certs: map[string]string{},
}

4
go.mod
View File

@ -54,6 +54,8 @@ require (
golang.org/x/sync v0.1.0
golang.org/x/sys v0.6.0
google.golang.org/api v0.112.0
google.golang.org/grpc v1.53.0
google.golang.org/protobuf v1.28.1
gopkg.in/gcfg.v1 v1.2.3
gopkg.in/inf.v0 v0.9.1
gopkg.in/square/go-jose.v2 v2.6.0
@ -213,8 +215,6 @@ require (
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488 // indirect
google.golang.org/grpc v1.53.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

View File

@ -49,7 +49,7 @@ func (b BootstrapClientBuilder) Build(c *fi.NodeupModelBuilderContext) error {
var authenticator bootstrap.Authenticator
var resolver resolver.Resolver
switch b.BootConfig.CloudProvider {
switch b.CloudProvider() {
case kops.CloudProviderAWS:
a, err := awsup.NewAWSAuthenticator(b.Cloud.Region())
if err != nil {
@ -81,7 +81,7 @@ func (b BootstrapClientBuilder) Build(c *fi.NodeupModelBuilderContext) error {
authenticator = a
default:
return fmt.Errorf("unsupported cloud provider for authenticator %q", b.BootConfig.CloudProvider)
return fmt.Errorf("unsupported cloud provider for authenticator %q", b.CloudProvider())
}
baseURL := url.URL{
@ -102,6 +102,8 @@ func (b BootstrapClientBuilder) Build(c *fi.NodeupModelBuilderContext) error {
Certs: b.bootstrapCerts,
KeypairIDs: b.bootstrapKeypairIDs,
}
bootstrapClientTask.UseChallengeCallback = b.UseChallengeCallback(b.CloudProvider())
bootstrapClientTask.ClusterName = b.NodeupConfig.ClusterName
for _, cert := range b.bootstrapCerts {
cert.Cert.Task = bootstrapClientTask

View File

@ -90,7 +90,7 @@ func (b *CloudConfigBuilder) build(c *fi.NodeupModelBuilderContext, inTree bool)
// Add cloud config file if needed
var lines []string
cloudProvider := b.BootConfig.CloudProvider
cloudProvider := b.CloudProvider()
var config string
requireGlobal := true

View File

@ -396,6 +396,11 @@ func (c *NodeupModelContext) UseKopsControllerForNodeBootstrap() bool {
return model.UseKopsControllerForNodeBootstrap(c.Cluster)
}
// UseChallengeCallback is true if we should use a callback challenge during node provisioning with kops-controller.
func (c *NodeupModelContext) UseChallengeCallback(cloudProvider kops.CloudProviderID) bool {
return model.UseChallengeCallback(cloudProvider)
}
// UsesSecondaryIP checks if the CNI in use attaches secondary interfaces to the host.
func (c *NodeupModelContext) UsesSecondaryIP() bool {
return (c.NodeupConfig.Networking.CNI != nil && c.NodeupConfig.Networking.CNI.UsesSecondaryIP) ||
@ -630,14 +635,19 @@ func (c *NodeupModelContext) InstallNvidiaRuntime() bool {
c.GPUVendor == architectures.GPUVendorNvidia
}
// CloudProvider returns the cloud provider we are running on
func (c *NodeupModelContext) CloudProvider() kops.CloudProviderID {
return c.BootConfig.CloudProvider
}
// RunningOnGCE returns true if we are running on GCE
func (c *NodeupModelContext) RunningOnGCE() bool {
return c.BootConfig.CloudProvider == kops.CloudProviderGCE
return c.CloudProvider() == kops.CloudProviderGCE
}
// RunningOnAzure returns true if we are running on Azure
func (c *NodeupModelContext) RunningOnAzure() bool {
return c.BootConfig.CloudProvider == kops.CloudProviderAzure
return c.CloudProvider() == kops.CloudProviderAzure
}
// GetMetadataLocalIP returns the local IP address read from metadata

View File

@ -67,7 +67,7 @@ func (b *KubeAPIServerBuilder) Build(c *fi.NodeupModelBuilderContext) error {
kubeAPIServer = *b.NodeupConfig.APIServerConfig.KubeAPIServer
}
if b.BootConfig.CloudProvider == kops.CloudProviderHetzner {
if b.CloudProvider() == kops.CloudProviderHetzner {
localIP, err := b.GetMetadataLocalIP()
if err != nil {
return err
@ -419,7 +419,7 @@ func (b *KubeAPIServerBuilder) writeServerCertificate(c *fi.NodeupModelBuilderCo
// We also want to be able to reference it locally via https://127.0.0.1
alternateNames = append(alternateNames, "127.0.0.1")
if b.BootConfig.CloudProvider == kops.CloudProviderHetzner {
if b.CloudProvider() == kops.CloudProviderHetzner {
localIP, err := b.GetMetadataLocalIP()
if err != nil {
return err
@ -428,7 +428,7 @@ func (b *KubeAPIServerBuilder) writeServerCertificate(c *fi.NodeupModelBuilderCo
alternateNames = append(alternateNames, localIP)
}
}
if b.BootConfig.CloudProvider == kops.CloudProviderOpenstack {
if b.CloudProvider() == kops.CloudProviderOpenstack {
instanceAddress, err := getInstanceAddress()
if err != nil {
return err

View File

@ -737,7 +737,7 @@ func (b *KubeletBuilder) buildKubeletServingCertificate(c *fi.NodeupModelBuilder
}
func (b *KubeletBuilder) kubeletNames() ([]string, error) {
if b.BootConfig.CloudProvider != kops.CloudProviderAWS {
if b.CloudProvider() != kops.CloudProviderAWS {
name, err := os.Hostname()
if err != nil {
return nil, err

View File

@ -50,7 +50,7 @@ func (b *NTPBuilder) Build(c *fi.NodeupModelBuilderContext) error {
}
var ntpHost string
switch b.BootConfig.CloudProvider {
switch b.CloudProvider() {
case kops.CloudProviderAWS:
ntpHost = "169.254.169.123"
case kops.CloudProviderGCE:

View File

@ -34,7 +34,7 @@ func (b *PrefixBuilder) Build(c *fi.NodeupModelBuilderContext) error {
if !b.IsKopsControllerIPAM() {
return nil
}
switch b.BootConfig.CloudProvider {
switch b.CloudProvider() {
case kops.CloudProviderAWS:
c.AddTask(&nodetasks.Prefix{
Name: "prefix",
@ -42,7 +42,7 @@ func (b *PrefixBuilder) Build(c *fi.NodeupModelBuilderContext) error {
case kops.CloudProviderGCE:
// Prefix is assigned by GCE
default:
return fmt.Errorf("kOps IPAM controller not supported on cloud %q", b.BootConfig.CloudProvider)
return fmt.Errorf("kOps IPAM controller not supported on cloud %q", b.CloudProvider())
}
return nil
}

View File

@ -176,7 +176,7 @@ type ProtokubeFlags struct {
func (t *ProtokubeBuilder) ProtokubeFlags() (*ProtokubeFlags, error) {
f := &ProtokubeFlags{
Channels: t.NodeupConfig.Channels,
Cloud: fi.PtrTo(string(t.BootConfig.CloudProvider)),
Cloud: fi.PtrTo(string(t.CloudProvider())),
Containerized: fi.PtrTo(false),
LogLevel: fi.PtrTo(int32(4)),
Master: b(t.IsMaster),
@ -273,7 +273,7 @@ func (t *ProtokubeBuilder) buildEnvFile() (*nodetasks.File, error) {
}
}
if t.BootConfig.CloudProvider == kops.CloudProviderDO && os.Getenv("DIGITALOCEAN_ACCESS_TOKEN") != "" {
if t.CloudProvider() == kops.CloudProviderDO && os.Getenv("DIGITALOCEAN_ACCESS_TOKEN") != "" {
envVars["DIGITALOCEAN_ACCESS_TOKEN"] = os.Getenv("DIGITALOCEAN_ACCESS_TOKEN")
}
@ -294,7 +294,7 @@ func (t *ProtokubeBuilder) buildEnvFile() (*nodetasks.File, error) {
envVars["AZURE_STORAGE_ACCOUNT"] = os.Getenv("AZURE_STORAGE_ACCOUNT")
}
if t.BootConfig.CloudProvider == kops.CloudProviderScaleway {
if t.CloudProvider() == kops.CloudProviderScaleway {
if os.Getenv("SCW_PROFILE") != "" || os.Getenv("SCW_SECRET_KEY") != "" {
profile, err := scaleway.CreateValidScalewayProfile()
if err != nil {

View File

@ -125,7 +125,7 @@ func (b *SysctlBuilder) Build(c *fi.NodeupModelBuilderContext) error {
)
}
if b.BootConfig.CloudProvider == kops.CloudProviderAWS {
if b.CloudProvider() == kops.CloudProviderAWS {
sysctls = append(sysctls,
"# AWS settings",
"",

View File

@ -30,7 +30,7 @@ var _ fi.NodeupModelBuilder = &WarmPoolBuilder{}
func (b *WarmPoolBuilder) Build(c *fi.NodeupModelBuilderContext) error {
// Check if the cloud provider is AWS
if b.BootConfig.CloudProvider != kops.CloudProviderAWS {
if b.CloudProvider() != kops.CloudProviderAWS {
return nil
}

View File

@ -36,6 +36,16 @@ func UseKopsControllerForNodeBootstrap(cluster *kops.Cluster) bool {
}
}
// UseChallengeCallback is true if we should use a callback challenge during node provisioning with kops-controller.
func UseChallengeCallback(cloudProvider kops.CloudProviderID) bool {
switch cloudProvider {
case kops.CloudProviderHetzner:
return true
default:
return false
}
}
// UseKopsControllerForNodeConfig checks if nodeup should use kops-controller to get nodeup.Config.
func UseKopsControllerForNodeConfig(cluster *kops.Cluster) bool {
switch cluster.Spec.GetCloudProvider() {

View File

@ -30,6 +30,17 @@ type BootstrapRequest struct {
// IncludeNodeConfig controls whether the cluster & instance group configuration should be returned.
// This allows for nodes without access to the kops state store.
IncludeNodeConfig bool `json:"includeNodeConfig"`
// Challenge is for a callback challenge.
Challenge *ChallengeRequest `json:"challenge,omitempty"`
}
// ChallengeRequest describes the callback challenge.
type ChallengeRequest struct {
Endpoint string `json:"endpoint,omitempty"`
ServerCA []byte `json:"ca,omitempty"`
ChallengeID string `json:"challengeID,omitempty"`
ChallengeSecret []byte `json:"challengeSecret,omitempty"`
}
// BootstrapResponse is a response to a BootstrapRequest.

View File

@ -127,6 +127,8 @@ type BootConfig struct {
// APIServerIPs is the API server IP addresses.
// This field is used for adding an alias for api.internal. in /etc/hosts, when Topology.DNS.Type == DNSTypeNone.
APIServerIPs []string `json:",omitempty"`
// ClusterName is the name of the cluster.
ClusterName string `json:",omitempty"`
// InstanceGroupName is the name of the instance group.
InstanceGroupName string `json:",omitempty"`
// InstanceGroupRole is the instance group role.
@ -200,6 +202,7 @@ func NewConfig(cluster *kops.Cluster, instanceGroup *kops.InstanceGroup) (*Confi
bootConfig := BootConfig{
CloudProvider: cluster.Spec.GetCloudProvider(),
ClusterName: cluster.ObjectMeta.Name,
InstanceGroupName: instanceGroup.ObjectMeta.Name,
InstanceGroupRole: role,
}

View File

@ -39,6 +39,12 @@ type VerifyResult struct {
// CertificateNames is the alternate names the node is authorized to use for certificates.
CertificateNames []string
// ChallengeEndpoint is a valid endpoints to which we should issue a challenge request,
// corresponding to the node the request identified as.
// This should be sourced from e.g. the cloud, and acts as a cross-check
// that this is the correct instance.
ChallengeEndpoint string
}
// Verifier verifies authentication credentials for requests.

101
pkg/bootstrap/challenge.go Normal file
View File

@ -0,0 +1,101 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package bootstrap
import (
cryptorand "crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"time"
"k8s.io/klog/v2"
"k8s.io/kops/pkg/pki"
)
func randomBytes(length int) []byte {
b := make([]byte, length)
if _, err := cryptorand.Read(b); err != nil {
klog.Fatalf("failed to read from crypto/rand: %v", err)
}
return b
}
func challengeKopsControllerSubject(clusterName string) pkix.Name {
// Note: keep in sync with subjectsMatch if you add (additional) fields here
return pkix.Name{
CommonName: "kops-controller." + clusterName,
}
}
func subjectsMatch(l, r pkix.Name) bool {
// We need to check all the fields in challengeKopsControllerSubject
return l.CommonName == r.CommonName
}
func challengeServerHostName(clusterName string) string {
return "challenge-server." + clusterName
}
func BuildChallengeServerCertificate(clusterName string) (*tls.Certificate, error) {
serverName := challengeServerHostName(clusterName)
privateKey, err := pki.GeneratePrivateKey()
if err != nil {
return nil, fmt.Errorf("generating ecdsa key: %w", err)
}
keyUsage := x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment
now := time.Now()
notBefore := now.Add(-15 * time.Minute)
notAfter := notBefore.Add(time.Hour)
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: serverName,
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: keyUsage,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
template.DNSNames = append(template.DNSNames, serverName)
der, err := x509.CreateCertificate(cryptorand.Reader, &template, &template, privateKey.Key.Public(), privateKey.Key)
if err != nil {
return nil, fmt.Errorf("failed to create certificate: %w", err)
}
parsed, err := x509.ParseCertificate(der)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate: %w", err)
}
tlsCertificate := &tls.Certificate{
PrivateKey: privateKey.Key,
Certificate: [][]byte{parsed.Raw},
Leaf: parsed,
}
return tlsCertificate, nil
}

View File

@ -0,0 +1,128 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package bootstrap
import (
"context"
"crypto/subtle"
"crypto/tls"
"crypto/x509"
"fmt"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"k8s.io/kops/pkg/apis/nodeup"
"k8s.io/kops/pkg/pki"
pb "k8s.io/kops/proto/kops/bootstrap/v1"
"k8s.io/kops/upup/pkg/fi"
)
type ChallengeClient struct {
keystore pki.Keystore
}
func NewChallengeClient(keystore pki.Keystore) (*ChallengeClient, error) {
return &ChallengeClient{
keystore: keystore,
}, nil
}
func (c *ChallengeClient) getClientCertificate(ctx context.Context, clusterName string) (*tls.Certificate, error) {
subject := challengeKopsControllerSubject(clusterName)
certificate, privateKey, _, err := pki.IssueCert(ctx, &pki.IssueCertRequest{
Validity: 1 * time.Hour,
Signer: fi.CertificateIDCA,
Type: "client",
Subject: subject,
}, c.keystore)
if err != nil {
return nil, fmt.Errorf("error creating certificate: %w", err)
}
// TODO: Caching and rotation
clientCertificate := &tls.Certificate{
PrivateKey: privateKey.Key,
Certificate: [][]byte{certificate.Certificate.Raw},
Leaf: certificate.Certificate,
}
return clientCertificate, nil
}
func (c *ChallengeClient) DoCallbackChallenge(ctx context.Context, clusterName string, targetEndpoint string, bootstrapRequest *nodeup.BootstrapRequest) error {
challenge := bootstrapRequest.Challenge
if challenge == nil {
return fmt.Errorf("challenge not set")
}
if challenge.ChallengeID == "" {
return fmt.Errorf("challenge.id not set")
}
if len(challenge.ChallengeSecret) == 0 {
return fmt.Errorf("challenge.secret not set")
}
if challenge.Endpoint == "" {
return fmt.Errorf("challenge.endpoint not set")
}
if len(challenge.ServerCA) == 0 {
return fmt.Errorf("challenge.ca not set")
}
clientCertificate, err := c.getClientCertificate(ctx, clusterName)
if err != nil {
return err
}
serverCAs := x509.NewCertPool()
if !serverCAs.AppendCertsFromPEM(challenge.ServerCA) {
return fmt.Errorf("error loading certificate pool")
}
serverName := challengeServerHostName(clusterName)
tlsConfig := &tls.Config{
RootCAs: serverCAs,
Certificates: []tls.Certificate{*clientCertificate},
ServerName: serverName,
}
kospControllerNonce := randomBytes(16)
req := &pb.ChallengeRequest{
ChallengeId: challenge.ChallengeID,
ChallengeRandom: kospControllerNonce,
}
expectedChallengeResponse := buildChallengeResponse(challenge.ChallengeSecret, kospControllerNonce)
var opts []grpc.DialOption
opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
conn, err := grpc.DialContext(ctx, targetEndpoint, opts...)
if err != nil {
return fmt.Errorf("error dialing target %q: %w", targetEndpoint, err)
}
client := pb.NewCallbackServiceClient(conn)
response, err := client.Challenge(ctx, req)
if err != nil {
return fmt.Errorf("error from callback challenge: %w", err)
}
if subtle.ConstantTimeCompare(response.GetChallengeResponse(), expectedChallengeResponse) != 1 {
return fmt.Errorf("callback challenge returned wrong result")
}
return nil
}

View File

@ -0,0 +1,217 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package bootstrap
import (
"bytes"
"context"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"encoding/pem"
"fmt"
"net"
"sync"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
"k8s.io/klog/v2"
"k8s.io/kops/pkg/apis/nodeup"
pb "k8s.io/kops/proto/kops/bootstrap/v1"
)
type ChallengeServer struct {
tlsConfig *tls.Config
servingCA []byte
mutex sync.Mutex
challenges map[string]*Challenge
RequiredSubject pkix.Name
pb.UnimplementedCallbackServiceServer
}
func NewChallengeServer(clusterName string, caBundle []byte) (*ChallengeServer, error) {
serverCertificate, err := BuildChallengeServerCertificate(clusterName)
if err != nil {
return nil, err
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{*serverCertificate},
}
var servingCA bytes.Buffer
for _, cert := range serverCertificate.Certificate {
if err := pem.Encode(&servingCA, &pem.Block{Type: "CERTIFICATE", Bytes: cert}); err != nil {
return nil, err
}
}
clientCAs := x509.NewCertPool()
if !clientCAs.AppendCertsFromPEM(caBundle) {
return nil, fmt.Errorf("unable to build client-cert CA pools")
}
tlsConfig.ClientCAs = clientCAs
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
return &ChallengeServer{
RequiredSubject: challengeKopsControllerSubject(clusterName),
tlsConfig: tlsConfig,
servingCA: servingCA.Bytes(),
}, nil
}
type Challenge struct {
ChallengeID string
ChallengeSecret []byte
}
func (s *ChallengeServer) createChallenge() *Challenge {
c := &Challenge{}
c.ChallengeID = hex.EncodeToString(randomBytes(16))
c.ChallengeSecret = randomBytes(16)
s.mutex.Lock()
defer s.mutex.Unlock()
if s.challenges == nil {
s.challenges = make(map[string]*Challenge)
}
s.challenges[c.ChallengeID] = c
return c
}
type ChallengeListener struct {
endpoint string
server *ChallengeServer
grpcServer *grpc.Server
}
func (s *ChallengeListener) CreateChallenge() *nodeup.ChallengeRequest {
challenge := s.server.createChallenge()
return &nodeup.ChallengeRequest{
Endpoint: s.Endpoint(),
ChallengeID: challenge.ChallengeID,
ChallengeSecret: challenge.ChallengeSecret,
ServerCA: s.server.servingCA,
}
}
func (s *ChallengeListener) Stop() {
s.grpcServer.Stop()
}
func (s *ChallengeListener) Endpoint() string {
return s.endpoint
}
func (s *ChallengeServer) NewListener(ctx context.Context, listen string) (*ChallengeListener, error) {
var opts []grpc.ServerOption
opts = append(opts, grpc.Creds(credentials.NewTLS(s.tlsConfig)))
grpcServer := grpc.NewServer(opts...)
pb.RegisterCallbackServiceServer(grpcServer, s)
lis, err := net.Listen("tcp", listen)
if err != nil {
return nil, fmt.Errorf("error listening on %q: %w", listen, err)
}
grpcListener := &ChallengeListener{
server: s,
grpcServer: grpcServer,
endpoint: lis.Addr().String(),
}
go func() {
klog.Infof("starting node-challenge listener on %v", lis.Addr())
if err := grpcServer.Serve(lis); err != nil {
lis.Close()
klog.Warningf("error serving GRPC: %v", err)
}
}()
return grpcListener, nil
}
// Answers challenges to cross-check bootstrap requests.
func (s *ChallengeServer) Challenge(ctx context.Context, req *pb.ChallengeRequest) (*pb.ChallengeResponse, error) {
klog.Infof("got node-challenge request")
// Explicitly authenticate the username for safety
peerInfo, ok := peer.FromContext(ctx)
if !ok {
klog.Warningf("no peer in context")
return nil, status.Error(codes.Unauthenticated, "peer was nil")
}
tlsInfo, ok := peerInfo.AuthInfo.(credentials.TLSInfo)
if !ok {
klog.Warningf("peer.AuthInfo was of unexpected type %T", peerInfo.AuthInfo)
return nil, status.Error(codes.Unauthenticated, "unexpected peer transport credentials")
}
if len(tlsInfo.State.VerifiedChains) == 0 || len(tlsInfo.State.VerifiedChains[0]) == 0 {
klog.Warningf("no VerifiedChains in TLSInfo")
return nil, status.Error(codes.Unauthenticated, "verified chains were empty")
}
if got, want := tlsInfo.State.VerifiedChains[0][0].Subject, s.RequiredSubject; !subjectsMatch(got, want) {
klog.Warningf("certificate subjects did not match expected; got %q, want %q", got, want)
return nil, status.Error(codes.Unauthenticated, "certificate subjects did not match")
}
s.mutex.Lock()
defer s.mutex.Unlock()
key := req.ChallengeId
if key == "" {
return nil, status.Errorf(codes.InvalidArgument, "challenge_id is required")
}
challenge := s.challenges[key]
if challenge == nil {
return nil, status.Errorf(codes.NotFound, "challenge was not found")
}
// Prevent replay attacks
delete(s.challenges, key)
hash := buildChallengeResponse(challenge.ChallengeSecret, req.GetChallengeRandom())
response := &pb.ChallengeResponse{
ChallengeResponse: hash,
}
return response, nil
}
func buildChallengeResponse(nodeNonce []byte, kopsControllerNonde []byte) []byte {
// Arguably this is overkill because the TLS handshake is stronger and everything is encrypted.
hasher := sha256.New()
hasher.Sum(nodeNonce)
hasher.Sum(kopsControllerNonde)
hash := hasher.Sum(nil)
return hash
}

View File

@ -20,6 +20,9 @@ const (
// KubeAPIServer is the port where kube-apiserver listens.
KubeAPIServer = 443
// NodeupChallenge is the port where nodeup listens for challenges.
NodeupChallenge = 3987
// KopsControllerPort is the port where kops-controller listens.
KopsControllerPort = 3988

8
proto/buf.gen.yaml Normal file
View File

@ -0,0 +1,8 @@
version: v1
plugins:
- plugin: go
out: .
opt: paths=source_relative
- plugin: go-grpc
out: .
opt: paths=source_relative

7
proto/buf.yaml Normal file
View File

@ -0,0 +1,7 @@
version: v1
breaking:
use:
- FILE
lint:
use:
- DEFAULT

View File

@ -0,0 +1,253 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.0
// protoc (unknown)
// source: kops/bootstrap/v1/bootstrap.proto
package v1
import (
reflect "reflect"
sync "sync"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ChallengeRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// challenge_id is a random value (nonce) that the node generated and passed in its bootstrap request.
ChallengeId string `protobuf:"bytes,1,opt,name=challenge_id,json=challengeId,proto3" json:"challenge_id,omitempty"`
// challenge_random is a random value (nonce) that kops-controller generated, it is hashed into the response.
ChallengeRandom []byte `protobuf:"bytes,2,opt,name=challenge_random,json=challengeRandom,proto3" json:"challenge_random,omitempty"`
}
func (x *ChallengeRequest) Reset() {
*x = ChallengeRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_kops_bootstrap_v1_bootstrap_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ChallengeRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ChallengeRequest) ProtoMessage() {}
func (x *ChallengeRequest) ProtoReflect() protoreflect.Message {
mi := &file_kops_bootstrap_v1_bootstrap_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ChallengeRequest.ProtoReflect.Descriptor instead.
func (*ChallengeRequest) Descriptor() ([]byte, []int) {
return file_kops_bootstrap_v1_bootstrap_proto_rawDescGZIP(), []int{0}
}
func (x *ChallengeRequest) GetChallengeId() string {
if x != nil {
return x.ChallengeId
}
return ""
}
func (x *ChallengeRequest) GetChallengeRandom() []byte {
if x != nil {
return x.ChallengeRandom
}
return nil
}
type ChallengeResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// challenge_response combines the node nonce with the kops-controller nonce.
// The node nonce is passed in the bootstrap request.
// The kops-controller nonce is challenge_random in the ChallengeRequest.
// challenge_response is expected to be sha256(node-node + kops-controller-nonce)
ChallengeResponse []byte `protobuf:"bytes,1,opt,name=challenge_response,json=challengeResponse,proto3" json:"challenge_response,omitempty"`
}
func (x *ChallengeResponse) Reset() {
*x = ChallengeResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_kops_bootstrap_v1_bootstrap_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ChallengeResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ChallengeResponse) ProtoMessage() {}
func (x *ChallengeResponse) ProtoReflect() protoreflect.Message {
mi := &file_kops_bootstrap_v1_bootstrap_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ChallengeResponse.ProtoReflect.Descriptor instead.
func (*ChallengeResponse) Descriptor() ([]byte, []int) {
return file_kops_bootstrap_v1_bootstrap_proto_rawDescGZIP(), []int{1}
}
func (x *ChallengeResponse) GetChallengeResponse() []byte {
if x != nil {
return x.ChallengeResponse
}
return nil
}
var File_kops_bootstrap_v1_bootstrap_proto protoreflect.FileDescriptor
var file_kops_bootstrap_v1_bootstrap_proto_rawDesc = []byte{
0x0a, 0x21, 0x6b, 0x6f, 0x70, 0x73, 0x2f, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70,
0x2f, 0x76, 0x31, 0x2f, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x11, 0x6b, 0x6f, 0x70, 0x73, 0x2e, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74,
0x72, 0x61, 0x70, 0x2e, 0x76, 0x31, 0x22, 0x60, 0x0a, 0x10, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65,
0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68,
0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0b, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x49, 0x64, 0x12, 0x29, 0x0a,
0x10, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x64, 0x6f,
0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e,
0x67, 0x65, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x22, 0x42, 0x0a, 0x11, 0x43, 0x68, 0x61, 0x6c,
0x6c, 0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a,
0x12, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x68, 0x61, 0x6c, 0x6c,
0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x6b, 0x0a, 0x0f,
0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
0x58, 0x0a, 0x09, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x12, 0x23, 0x2e, 0x6b,
0x6f, 0x70, 0x73, 0x2e, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2e, 0x76, 0x31,
0x2e, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x24, 0x2e, 0x6b, 0x6f, 0x70, 0x73, 0x2e, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72,
0x61, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x25, 0x5a, 0x23, 0x6b, 0x38, 0x73,
0x2e, 0x69, 0x6f, 0x2f, 0x6b, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6b,
0x6f, 0x70, 0x73, 0x2f, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2f, 0x76, 0x31,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_kops_bootstrap_v1_bootstrap_proto_rawDescOnce sync.Once
file_kops_bootstrap_v1_bootstrap_proto_rawDescData = file_kops_bootstrap_v1_bootstrap_proto_rawDesc
)
func file_kops_bootstrap_v1_bootstrap_proto_rawDescGZIP() []byte {
file_kops_bootstrap_v1_bootstrap_proto_rawDescOnce.Do(func() {
file_kops_bootstrap_v1_bootstrap_proto_rawDescData = protoimpl.X.CompressGZIP(file_kops_bootstrap_v1_bootstrap_proto_rawDescData)
})
return file_kops_bootstrap_v1_bootstrap_proto_rawDescData
}
var file_kops_bootstrap_v1_bootstrap_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_kops_bootstrap_v1_bootstrap_proto_goTypes = []interface{}{
(*ChallengeRequest)(nil), // 0: kops.bootstrap.v1.ChallengeRequest
(*ChallengeResponse)(nil), // 1: kops.bootstrap.v1.ChallengeResponse
}
var file_kops_bootstrap_v1_bootstrap_proto_depIdxs = []int32{
0, // 0: kops.bootstrap.v1.CallbackService.Challenge:input_type -> kops.bootstrap.v1.ChallengeRequest
1, // 1: kops.bootstrap.v1.CallbackService.Challenge:output_type -> kops.bootstrap.v1.ChallengeResponse
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_kops_bootstrap_v1_bootstrap_proto_init() }
func file_kops_bootstrap_v1_bootstrap_proto_init() {
if File_kops_bootstrap_v1_bootstrap_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_kops_bootstrap_v1_bootstrap_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ChallengeRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_kops_bootstrap_v1_bootstrap_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ChallengeResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_kops_bootstrap_v1_bootstrap_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_kops_bootstrap_v1_bootstrap_proto_goTypes,
DependencyIndexes: file_kops_bootstrap_v1_bootstrap_proto_depIdxs,
MessageInfos: file_kops_bootstrap_v1_bootstrap_proto_msgTypes,
}.Build()
File_kops_bootstrap_v1_bootstrap_proto = out.File
file_kops_bootstrap_v1_bootstrap_proto_rawDesc = nil
file_kops_bootstrap_v1_bootstrap_proto_goTypes = nil
file_kops_bootstrap_v1_bootstrap_proto_depIdxs = nil
}

View File

@ -0,0 +1,28 @@
syntax = "proto3";
package kops.bootstrap.v1;
option go_package = "k8s.io/kops/proto/kops/bootstrap/v1";
// CallbackService is the service that the node must run during bootstrapping,
// we perform a simple callback from the controller.
service CallbackService {
// Answers challenges to cross-check bootstrap requests.
rpc Challenge(ChallengeRequest) returns (ChallengeResponse) {}
}
message ChallengeRequest {
// challenge_id is a random value (nonce) that the node generated and passed in its bootstrap request.
string challenge_id = 1;
// challenge_random is a random value (nonce) that kops-controller generated, it is hashed into the response.
bytes challenge_random = 2;
}
message ChallengeResponse {
// challenge_response combines the node nonce with the kops-controller nonce.
// The node nonce is passed in the bootstrap request.
// The kops-controller nonce is challenge_random in the ChallengeRequest.
// challenge_response is expected to be sha256(node-node + kops-controller-nonce)
bytes challenge_response = 1;
}

View File

@ -0,0 +1,120 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package v1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// CallbackServiceClient is the client API for CallbackService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type CallbackServiceClient interface {
// Answers challenges to cross-check bootstrap requests.
Challenge(ctx context.Context, in *ChallengeRequest, opts ...grpc.CallOption) (*ChallengeResponse, error)
}
type callbackServiceClient struct {
cc grpc.ClientConnInterface
}
func NewCallbackServiceClient(cc grpc.ClientConnInterface) CallbackServiceClient {
return &callbackServiceClient{cc}
}
func (c *callbackServiceClient) Challenge(ctx context.Context, in *ChallengeRequest, opts ...grpc.CallOption) (*ChallengeResponse, error) {
out := new(ChallengeResponse)
err := c.cc.Invoke(ctx, "/kops.bootstrap.v1.CallbackService/Challenge", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// CallbackServiceServer is the server API for CallbackService service.
// All implementations must embed UnimplementedCallbackServiceServer
// for forward compatibility
type CallbackServiceServer interface {
// Answers challenges to cross-check bootstrap requests.
Challenge(context.Context, *ChallengeRequest) (*ChallengeResponse, error)
mustEmbedUnimplementedCallbackServiceServer()
}
// UnimplementedCallbackServiceServer must be embedded to have forward compatible implementations.
type UnimplementedCallbackServiceServer struct {
}
func (UnimplementedCallbackServiceServer) Challenge(context.Context, *ChallengeRequest) (*ChallengeResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Challenge not implemented")
}
func (UnimplementedCallbackServiceServer) mustEmbedUnimplementedCallbackServiceServer() {}
// UnsafeCallbackServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to CallbackServiceServer will
// result in compilation errors.
type UnsafeCallbackServiceServer interface {
mustEmbedUnimplementedCallbackServiceServer()
}
func RegisterCallbackServiceServer(s grpc.ServiceRegistrar, srv CallbackServiceServer) {
s.RegisterService(&CallbackService_ServiceDesc, srv)
}
func _CallbackService_Challenge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ChallengeRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CallbackServiceServer).Challenge(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/kops.bootstrap.v1.CallbackService/Challenge",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CallbackServiceServer).Challenge(ctx, req.(*ChallengeRequest))
}
return interceptor(ctx, in, info, handler)
}
// CallbackService_ServiceDesc is the grpc.ServiceDesc for CallbackService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var CallbackService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "kops.bootstrap.v1.CallbackService",
HandlerType: (*CallbackServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Challenge",
Handler: _CallbackService_Challenge_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "kops/bootstrap/v1/bootstrap.proto",
}

View File

@ -261,6 +261,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: additionalobjects.example.com
ConfigBase: memfs://tests/additionalobjects.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -151,6 +151,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: additionalobjects.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -151,6 +151,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: apiserver
InstanceGroupRole: APIServer

View File

@ -256,6 +256,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -151,6 +151,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -254,6 +254,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -254,6 +254,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: bastionuserdata.example.com
ConfigBase: memfs://clusters.example.com/bastionuserdata.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -159,6 +159,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: bastionuserdata.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -252,6 +252,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: cas-priority-expander-custom.example.com
ConfigBase: memfs://clusters.example.com/cas-priority-expander-custom.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -149,6 +149,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: cas-priority-expander-custom.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -149,6 +149,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: cas-priority-expander-custom.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -149,6 +149,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: cas-priority-expander-custom.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -252,6 +252,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: cas-priority-expander.example.com
ConfigBase: memfs://clusters.example.com/cas-priority-expander.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -149,6 +149,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: cas-priority-expander.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -149,6 +149,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: cas-priority-expander.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -149,6 +149,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: cas-priority-expander.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -269,6 +269,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: complex.example.com
ConfigBase: memfs://clusters.example.com/complex.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -159,6 +159,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: complex.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -133,7 +133,7 @@ ensure-install-dir
echo "H4sIAAAAAAAA/+xWXW/bNhe+968g+qJ3tWQ3afpW6I1qZ4mW2PXkZNmuApo8lrlQpHZIKvF+/UBSsp00GTYMK3pRFGjM88Xz8ZyHYlI7PtFqLapsQEhNFa1gaTXSCiaSGgMmIxYdDEAx3DZWaNWZE+WkHIBlfCKdsYDGR4AWlA2/+mgYD4SsKLtzTQkWlA8zpVuTkQ+jTguq7Q2HRNEaMnJ6NZnezvJ5fnZa3k7z4vLX20/55OJ6sbwtT69O51fF53nnQkhLpQMfjwdRC2iEVhk5St4l70NpQn1zad25FeSLYgnYxoSolPp+gaIVEirgXe8JoUqrba2dyZ3dZGRNpQniRuSOC1AMQs+HxEdEBRZMYlqWcFhTJ200jddMtFM2I2Mvc3ajUfxBfeEzzSEjubynW5P7NAaErITiOecIxmRklIR/A0ICahaoW8EBMwIPFlBR6cev6EpCzmthfJ0L6SqhutTmtAbTUAaXYg1syyQE8aWohS2pqgDD2WcpGOSM+USDaBqrOMTlofxKS8BQwxKYVjwqZ85SK1S1S+YGVhut74LyZyoFf1k91xxKMBYF82GDrASjHTL4yWlLfamW8djRrr6NtY3J0nT89n3o1Dg7Ho3Gjy0/t4AoeD+tNK7L/551fTsgZA3UOoQzaqFbqsmymIkqlpvfLDPyykPkVdAV6goBYtPzm+W1QqiEX80DK1HTCjISNbhN7v5vEqFTj5shbYSJUGzHyduTMGuvkGAXCGtAhB4OV9umL6JQcfzFIhzPtbF+UcLh9OFAJ3V1CS3IjPjSEH53YOwGKAcMgAMeIBKj0qpCqKjV+NT29MEiPQ8/fVbioXf5ZVhCrS0Mg8Xwqd8ZatdEv6cOQfXU/tr41Gt43sVrB4QYYA5hodFm5Pj4KEgO8VsY43z/+wnTRiSia1jCdN34ZibwQOtGghd8EeHHm4vldVn8gxCpbkAJnrbj9Lf7O7OP2BF1sQjrlpHxaJScHHvEpeOQelywT5TdgeJZAO5RIKmJVha1lICzPXt6smLUgt+WSTEtzZ6urKVsMwX/f+l3kgkJy61iC0CheUbG9cj8JZGwmKoPG/P8cBLzHO+V80DHLzSRhVfKIUz8HaV2foN2xPl194rtujfsH5/9gskIagmBamIyB7JdTx9vjzPwmCgnCNy/YFT2Y/BXL9kGuJNxXl+3aLO7+t/V2hFQ9vIryCq/vlMUnrmI2RoLNd/JS61tRtK/g7bpfHmwFOPRgUaH74f+mHjg79+7KaxcVQlVnVPFpeeKvhBo4wtyTpFnpIZa4zahLRXS+30cj0Yz8UZpDmvzSPy6Fwr/1/yAAB/fvX4T+v2FaS99ZPufzNvPIi7WgvoJpC3FVIpV2g0p3Rt8gdhG80KtkXoqoUIBFs+Dp6HOQHaUnESfGVViDcZ2F4Jl6f4bJ607rSeTBrUFZi+8TnafBvtJ9NV0G+Gbt9OZjbNc36szpAx6ijoKDPWMaoLCCkblQnPj4WIGNfWRL74D9TtQv22g/gkAAP//AQAA///KG9Hm7w0AAA==" | base64 -d | gzip -d > conf/cluster_spec.yaml
echo "H4sIAAAAAAAA/1SOQWrDMBBF9zqFLuCY0LoBQTcxpDVp3WASsh7kUeJW0qgayTU+fTFZefv57/FqS7k/RRqHHqOS8MeiJm+G2x4YlXToDKuy1DZzwsgbnMAFixtNrtTkQkRejaLxnMBrfIuUQwtukcDCFpmLhJyKLaxPHVlUsiafItmTBY+ipR5zeIS8A9+VnLaX7vv2vPu5fn1eq3l3buO+q5qX+X5+Ohybg/mtRoBj/jCXV/EPAAD//wEAAP//1eFPetUAAAA=" | base64 -d | gzip -d > conf/kube_env.yaml
echo "H4sIAAAAAAAA/2zO0UrDMBTG8fs8RV6gK0PrIODNAtMyraNs7PqQnmzTJCfmJLP06UV6VfD+/+P7tKMyHBLdbwMmJeGHhXaFM6YOPCppyMeEzCscwUeHK0NeaAr2dtkCo5IevWVV12ZWi7D+V7eBMwSDL4lKnFc8/NmqcJWRc7WGZdSTQyU1hZzIHRwEFB0NWOJ85BX4quS4PvWfl8fN1/nj/dxMm2OXtn3TPk3X48Nu3+7sd3MH2Jc3e3oWvwAAAP//AQAA///JQrb09wAAAA==" | base64 -d | gzip -d > conf/kube_env.yaml
download-release
echo "== nodeup node config done =="

View File

@ -133,7 +133,7 @@ ensure-install-dir
echo "H4sIAAAAAAAA/2yST2/bMAzF7/4URIHeCttZt2IQeikSrA22DMGyYWfGohWishhQkrd8+0Fu3D9YT4b4e8R70nPnJdulhJ6dqQAGDOhol0TR0dJjjBQNJM1UPeY9eUpFhUHCaZAc73I6GOjRR6oAOqeSjyvlkdRAPMVEg32e/xBJBppyLp5blZFtEdLfRBrQTyTHRLr6vjOwaNv65mPd1ov2FZEBOZj5WHvppkUKuPe0on12joN7wGA96RwdgEbuEkt4QLUGBhpETzWOyL7s3S7adsNXQSz18c34ch5y+cYvSnT76fKKB3T/S+fpG20F0BOmrHSPiWJ5PoDlbr1hp1gi3f3eGbgoOS8mtg4/lWjrs+PCfgUlx+Wyr1Sli24qbYulgWZEbTzvm3NJzYugAvDivtFI3sCHCuAodh16xaWEhBxI1yW2gScbPdWPn2PN0hwxRzLX9c3TzgYD9xTT2ZBSN5looESxGc40FrFKoi59LcyvqMfs00sT82123YFsnh7vmcVDTlb+hHvFjrakLNbAdRvfR0vlxB36rdhYfpdY/QMAAP//AQAA//+ifSCJzQIAAA==" | base64 -d | gzip -d > conf/cluster_spec.yaml
echo "H4sIAAAAAAAA/6yUS4+qSBiG9/4K9ua0iIpg0ouP4mJhgyC3Q+8QAUEuxUVLzfz4ifQkJ5M5mUwmzYLF+6aexZcnLyqb68nqmlt+SroNE9F+gpo6zTMn6W5Jt5kwDAKUdEOe5nE0JP2G+WPCMAzz4/VJioZNBikHF6sYgauM6dgbGEvHAiE45BlQLEGGDZWA2UqyYDqywIHnGMBqyGk1Bx8Xsq1IiHpgSNldLcAbIVJm+hIYrhwM87DyH8fKZz9/GkPIKXR7jk2jUKj5hIUhx3QvAw1e2fNXNkLGooDQOIRUhVD2bVtW6Lw7BXoZV6vypPnPj8A8Gyqlsh3qu+YTn2+xCbYiSTbII8ShQB3QJTBVvHh2rHsRLl0fCDdapAZ56vsysAV+zt+0RWhFXAsZXvvLZkdTn33gbD5CimG65dOlNaMXCyzXctMDEfFDaLaKolIWAVUAIhMZClA5C2X/wFpgb2cS2DJkivZ1WBA0mHsnhSrSjNqqAYYEqUC39vhgL0mhopra4ukXbvxR6lTu9eVsJ8r+etecPfgL8s/LA6uAJJzYRRpydB3sSLrnxBZz4opcObSMIxFyX9uTzImFETJUdt/yXXwfFu60O64E0fKHJ1vRWbxz7/sLn205hND0sU0fHH1//+WNYsq/t+b/WnWrnIjdQxlWP3ffa5VrcL+36lV8m1WGyqvL6PNEEvbgkcfjEkkkoJ+opsc0PYfank7Tnj+cPq5eu7aRWenVTGnXIyQI9s/cCOUci84xLok85e1cZ82F0hLie99o1QeuCA+HwpJhy9dnPO3jwB8Wtnis/9Uq3/bvK8MbroCTxLpaYrOa3wficg6v3tJc0JcY3evyS01RWnsyd+3Psa9aQ5Q0H6KWXoUoXO50KWnbbNUU5bKtDrV7Lv6LVf24ZP1ryn4w52Eg/WY2uzSk/xE39dA1ZZl0b3k9JF0dlW9xU5Eu6fu35B5VpExewWYhCsJsgut+iOo40brmSsyoSjZM3ZyS/u/FoSmTDWM2p2Ty+l3J155uo/68YYDtLp7x5BCHh2CuGR2fcmhNYrFIPU61+zYA3Xvq2xrw++RPAAAA//8BAAD//zgmNPacBQAA" | base64 -d | gzip -d > conf/kube_env.yaml
echo "H4sIAAAAAAAA/6yUS4+ySBiF9/4K9qZbREUw6cVLcbGwQZDbR+8QAUEuxUVLzfz4SdOTfJlMZzKZNAsW56SexZsnB5XN9WR1zS0/Jd2GiWg/QeW1H5LOjKpkw8RNRbqk71+Te1SRMnmNm2qCmjrNMyfpbkm3mTAMApR0Q57mcTQk/Yb5Y8IwDPPy+UmKhk0GKQcXqxiBq4zp2BsYS8cCITjkGVAsQYYNlYDZSrJgOrLAgecYwGrIaTUHHxeyrUiIemBI2V0twBshUmb6EhiuHAzzsPIfx8pnP34ZQ8gpdHuOTaNQqPmEhSHHdC8DDT6z5+9shIxFAaFxCKkKoezbtqzQeXcK9DKuVuVJ85/vgXk2VEplO9R3zQc+32ITbEWSbJBHiEOBOqBLYKp48exY9yJcuj4QbrRIDfLU92VgC/ycv2mL0Iq4FjK89pfNjqY++8DZfIQUw3TLp0trRi8WWK7lpgci4ofQbBVFpSwCqgBEJjIUoHIWyv6BtcDeziSwZcgU7euwIGgw904KVaQZtVUDDAlSgW7t8cFekkJFNbXF0y/c+L3Uqdzry9lOlP31rjl78Bfkn5cHVgFJOLGLNOToOtiRdM+JLebEFblyaBlHIuS+tieZEwsjZKjsvuW7+D4s3Gl3XAmi5Q9PtqKzeOfe9xc+23IIoeljmz44+vb22xvFlL+35v9adauciN1DGVa/dj9rlWtw31v1WfyYVYbKq8vo40QS9uCRx+MSSSSgH6imxzQ9h9qeTtOeP5zer167tpFZ6dVMadcjJAj2z9wI5RyLzjEuiTzl7VxnzYXSEuJ7P2jVO64ID4fCkmHL12c87ePAHxa2eKz/1Srf9u8rwxuugJPEulpis5rfB+JyDq/e0lzQlxjd6/JLTVFaezJ37c+xr1pDlDTvopZehShc7nQpadts1RTlsq0OtXsu/otV/bhk/eeUvTDnYSD9Zja7NKR/iZt66JqyTLrXvB6Sro7K1+8mcbMQBWE2wXU/RHWcaF1zJV8DWjenpP97cWjKZMOYzSmZfP6u5GtPt1F/3jDAdhfPeHKIw0Mw14yOTzm0JrFYpB6n2n0bgO499W0N+G3yJwAAAP//AQAA//8syA67vgUAAA==" | base64 -d | gzip -d > conf/kube_env.yaml
download-release
echo "== nodeup node config done =="

View File

@ -254,6 +254,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: containerd.example.com
ConfigBase: memfs://clusters.example.com/containerd.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: containerd.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -254,6 +254,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: containerd.example.com
ConfigBase: memfs://clusters.example.com/containerd.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: containerd.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -254,6 +254,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: 123.example.com
ConfigBase: memfs://clusters.example.com/123.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: 123.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -257,6 +257,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: docker.example.com
ConfigBase: memfs://clusters.example.com/docker.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -151,6 +151,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: docker.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -262,6 +262,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: existing-iam.example.com
ConfigBase: memfs://tests/existing-iam.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -262,6 +262,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: existing-iam.example.com
ConfigBase: memfs://tests/existing-iam.example.com
InstanceGroupName: master-us-test-1b
InstanceGroupRole: ControlPlane

View File

@ -262,6 +262,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: existing-iam.example.com
ConfigBase: memfs://tests/existing-iam.example.com
InstanceGroupName: master-us-test-1c
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: existing-iam.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -262,6 +262,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: existingsg.example.com
ConfigBase: memfs://clusters.example.com/existingsg.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -262,6 +262,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: existingsg.example.com
ConfigBase: memfs://clusters.example.com/existingsg.example.com
InstanceGroupName: master-us-test-1b
InstanceGroupRole: ControlPlane

View File

@ -262,6 +262,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: existingsg.example.com
ConfigBase: memfs://clusters.example.com/existingsg.example.com
InstanceGroupName: master-us-test-1c
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: existingsg.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -254,6 +254,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -254,6 +254,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -254,6 +254,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: externallb.example.com
ConfigBase: memfs://clusters.example.com/externallb.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: externallb.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -256,6 +256,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: externalpolicies.example.com
ConfigBase: memfs://clusters.example.com/externalpolicies.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: externalpolicies.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -262,6 +262,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: ha.example.com
ConfigBase: memfs://tests/ha.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -262,6 +262,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: ha.example.com
ConfigBase: memfs://tests/ha.example.com
InstanceGroupName: master-us-test-1b
InstanceGroupRole: ControlPlane

View File

@ -262,6 +262,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: ha.example.com
ConfigBase: memfs://tests/ha.example.com
InstanceGroupName: master-us-test-1c
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: ha.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -252,6 +252,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: gce
ClusterName: ha-gce.example.com
ConfigBase: memfs://tests/ha-gce.example.com
InstanceGroupName: master-us-test1-a
InstanceGroupRole: ControlPlane

View File

@ -252,6 +252,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: gce
ClusterName: ha-gce.example.com
ConfigBase: memfs://tests/ha-gce.example.com
InstanceGroupName: master-us-test1-b
InstanceGroupRole: ControlPlane

View File

@ -252,6 +252,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: gce
ClusterName: ha-gce.example.com
ConfigBase: memfs://tests/ha-gce.example.com
InstanceGroupName: master-us-test1-c
InstanceGroupRole: ControlPlane

View File

@ -147,6 +147,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: gce
ClusterName: ha-gce.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -254,6 +254,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -254,6 +254,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -255,6 +255,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -257,6 +257,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -151,6 +151,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -255,6 +255,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -255,6 +255,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -255,6 +255,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -255,6 +255,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -245,6 +245,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: gce
ClusterName: minimal.example.com
ConfigBase: memfs://tests/minimal.example.com
InstanceGroupName: master-us-test1-a
InstanceGroupRole: ControlPlane

View File

@ -147,6 +147,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: gce
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

View File

@ -255,6 +255,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigBase: memfs://clusters.example.com/minimal.example.com
InstanceGroupName: master-us-test-1a
InstanceGroupRole: ControlPlane

View File

@ -150,6 +150,7 @@ __EOF_CLUSTER_SPEC
cat > conf/kube_env.yaml << '__EOF_KUBE_ENV'
CloudProvider: aws
ClusterName: minimal.example.com
ConfigServer:
CACertificates: |
-----BEGIN CERTIFICATE-----

Some files were not shown because too many files have changed in this diff Show More