Add server code to kops-controller

This commit is contained in:
John Gardiner Myers 2020-07-15 21:36:43 -07:00
parent 96ab8423b1
commit 00c60ddff6
19 changed files with 281 additions and 10 deletions

View File

@ -8,6 +8,7 @@ go_library(
deps = [ deps = [
"//cmd/kops-controller/controllers:go_default_library", "//cmd/kops-controller/controllers:go_default_library",
"//cmd/kops-controller/pkg/config:go_default_library", "//cmd/kops-controller/pkg/config:go_default_library",
"//cmd/kops-controller/pkg/server:go_default_library",
"//pkg/nodeidentity:go_default_library", "//pkg/nodeidentity:go_default_library",
"//pkg/nodeidentity/aws:go_default_library", "//pkg/nodeidentity/aws:go_default_library",
"//pkg/nodeidentity/do:go_default_library", "//pkg/nodeidentity/do:go_default_library",
@ -47,7 +48,7 @@ ARCH = [
architecture = arch, architecture = arch,
base = "@distroless_base//image", base = "@distroless_base//image",
cmd = ["/kops-controller"], cmd = ["/kops-controller"],
user = "10001", user = "10011",
files = [ files = [
"//cmd/kops-controller", "//cmd/kops-controller",
], ],

View File

@ -29,6 +29,7 @@ import (
"k8s.io/klog/klogr" "k8s.io/klog/klogr"
"k8s.io/kops/cmd/kops-controller/controllers" "k8s.io/kops/cmd/kops-controller/controllers"
"k8s.io/kops/cmd/kops-controller/pkg/config" "k8s.io/kops/cmd/kops-controller/pkg/config"
"k8s.io/kops/cmd/kops-controller/pkg/server"
"k8s.io/kops/pkg/nodeidentity" "k8s.io/kops/pkg/nodeidentity"
nodeidentityaws "k8s.io/kops/pkg/nodeidentity/aws" nodeidentityaws "k8s.io/kops/pkg/nodeidentity/aws"
nodeidentitydo "k8s.io/kops/pkg/nodeidentity/do" nodeidentitydo "k8s.io/kops/pkg/nodeidentity/do"
@ -81,6 +82,18 @@ func main() {
} }
ctrl.SetLogger(klogr.New()) ctrl.SetLogger(klogr.New())
if opt.Server != nil {
srv, err := server.NewServer(&opt)
if err != nil {
setupLog.Error(err, "unable to create server")
os.Exit(1)
}
go func() {
err := srv.Start()
setupLog.Error(err, "unable to start server")
os.Exit(1)
}()
}
if err := buildScheme(); err != nil { if err := buildScheme(); err != nil {
setupLog.Error(err, "error building scheme") setupLog.Error(err, "error building scheme")

View File

@ -17,9 +17,20 @@ limitations under the License.
package config package config
type Options struct { type Options struct {
Cloud string `json:"cloud,omitempty"` Cloud string `json:"cloud,omitempty"`
ConfigBase string `json:"configBase,omitempty"` ConfigBase string `json:"configBase,omitempty"`
Server *ServerOptions `json:"server,omitempty"`
} }
func (o *Options) PopulateDefaults() { func (o *Options) PopulateDefaults() {
} }
type ServerOptions struct {
// Listen is the network endpoint (ip and port) we should listen on.
Listen string
// ServerKeyPath is the path to our TLS serving private key.
ServerKeyPath string `json:"serverKeyPath,omitempty"`
// ServerCertificatePath is the path to our TLS serving certificate.
ServerCertificatePath string `json:"serverCertificatePath,omitempty"`
}

View File

@ -0,0 +1,9 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["server.go"],
importpath = "k8s.io/kops/cmd/kops-controller/pkg/server",
visibility = ["//visibility:public"],
deps = ["//cmd/kops-controller/pkg/config:go_default_library"],
)

View File

@ -0,0 +1,47 @@
/*
Copyright 2020 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 server
import (
"crypto/tls"
"net/http"
"k8s.io/kops/cmd/kops-controller/pkg/config"
)
type Server struct {
opt *config.Options
server *http.Server
}
func NewServer(opt *config.Options) (*Server, error) {
server := &http.Server{
Addr: opt.Server.Listen,
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
PreferServerCipherSuites: true,
},
}
return &Server{
opt: opt,
server: server,
}, nil
}
func (s *Server) Start() error {
return s.server.ListenAndServeTLS(s.opt.Server.ServerCertificatePath, s.opt.Server.ServerKeyPath)
}

View File

@ -20,6 +20,7 @@ k8s.io/kops/cmd/kops/util
k8s.io/kops/cmd/kops-controller k8s.io/kops/cmd/kops-controller
k8s.io/kops/cmd/kops-controller/controllers k8s.io/kops/cmd/kops-controller/controllers
k8s.io/kops/cmd/kops-controller/pkg/config k8s.io/kops/cmd/kops-controller/pkg/config
k8s.io/kops/cmd/kops-controller/pkg/server
k8s.io/kops/cmd/kube-apiserver-healthcheck k8s.io/kops/cmd/kube-apiserver-healthcheck
k8s.io/kops/cmd/nodeup k8s.io/kops/cmd/nodeup
k8s.io/kops/dns-controller/cmd/dns-controller k8s.io/kops/dns-controller/cmd/dns-controller

View File

@ -15,6 +15,7 @@ go_library(
"file_assets.go", "file_assets.go",
"firewall.go", "firewall.go",
"hooks.go", "hooks.go",
"kops_controller.go",
"kube_apiserver.go", "kube_apiserver.go",
"kube_apiserver_healthcheck.go", "kube_apiserver_healthcheck.go",
"kube_controller_manager.go", "kube_controller_manager.go",
@ -41,6 +42,7 @@ go_library(
"//nodeup/pkg/distros:go_default_library", "//nodeup/pkg/distros:go_default_library",
"//nodeup/pkg/model/resources:go_default_library", "//nodeup/pkg/model/resources:go_default_library",
"//pkg/apis/kops:go_default_library", "//pkg/apis/kops:go_default_library",
"//pkg/apis/kops/model:go_default_library",
"//pkg/apis/kops/util:go_default_library", "//pkg/apis/kops/util:go_default_library",
"//pkg/apis/nodeup:go_default_library", "//pkg/apis/nodeup:go_default_library",
"//pkg/assets:go_default_library", "//pkg/assets:go_default_library",
@ -86,6 +88,7 @@ go_test(
"containerd_test.go", "containerd_test.go",
"docker_test.go", "docker_test.go",
"fakes_test.go", "fakes_test.go",
"kops_controller_test.go",
"kube_apiserver_test.go", "kube_apiserver_test.go",
"kube_controller_manager_test.go", "kube_controller_manager_test.go",
"kube_proxy_test.go", "kube_proxy_test.go",

View File

@ -25,6 +25,7 @@ import (
"k8s.io/klog" "k8s.io/klog"
"k8s.io/kops/nodeup/pkg/distros" "k8s.io/kops/nodeup/pkg/distros"
"k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/apis/kops/model"
"k8s.io/kops/pkg/apis/kops/util" "k8s.io/kops/pkg/apis/kops/util"
"k8s.io/kops/pkg/apis/nodeup" "k8s.io/kops/pkg/apis/nodeup"
"k8s.io/kops/pkg/systemd" "k8s.io/kops/pkg/systemd"
@ -326,6 +327,11 @@ func (c *NodeupModelContext) UseEtcdTLSAuth() bool {
return false return false
} }
// UseKopsControllerForNodeBootstrap checks if nodeup should use kops-controller to bootstrap.
func (c *NodeupModelContext) UseKopsControllerForNodeBootstrap() bool {
return model.UseKopsControllerForNodeBootstrap(c.Cluster)
}
// UseNodeAuthorization checks if have a node authorization policy // UseNodeAuthorization checks if have a node authorization policy
func (c *NodeupModelContext) UseNodeAuthorization() bool { func (c *NodeupModelContext) UseNodeAuthorization() bool {
return c.Cluster.Spec.NodeAuthorization != nil return c.Cluster.Spec.NodeAuthorization != nil

View File

@ -0,0 +1,85 @@
/*
Copyright 2020 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 model
import (
"path/filepath"
"k8s.io/kops/pkg/wellknownusers"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/nodeup/nodetasks"
)
// KopsControllerBuilder installs the keys for a kops-controller.
type KopsControllerBuilder struct {
*NodeupModelContext
}
var _ fi.ModelBuilder = &KopsControllerBuilder{}
// Build is responsible for configuring keys that will be used by kops-controller (via hostPath)
func (b *KopsControllerBuilder) Build(c *fi.ModelBuilderContext) error {
if !b.IsMaster {
return nil
}
// Create the directory, even if we aren't going to populate it
pkiDir := "/etc/kubernetes/kops-controller"
c.AddTask(&nodetasks.File{
Path: pkiDir,
Type: nodetasks.FileType_Directory,
Mode: s("0755"),
})
if !b.UseKopsControllerForNodeBootstrap() {
return nil
}
// We run kops-controller under an unprivileged user (wellknownusers.KopsControllerID), and then grant specific permissions
c.AddTask(&nodetasks.UserTask{
Name: wellknownusers.KopsControllerName,
UID: wellknownusers.KopsControllerID,
Shell: "/sbin/nologin",
})
issueCert := &nodetasks.IssueCert{
Name: "kops-controller",
Signer: fi.CertificateIDCA,
Type: "server",
Subject: nodetasks.PKIXName{CommonName: "kops-controller"},
AlternateNames: []string{b.Cluster.Spec.MasterInternalName},
}
c.AddTask(issueCert)
certResource, keyResource, _ := issueCert.GetResources()
c.AddTask(&nodetasks.File{
Path: filepath.Join(pkiDir, "kops-controller.crt"),
Contents: certResource,
Type: nodetasks.FileType_File,
Mode: s("0644"),
Owner: s(wellknownusers.KopsControllerName),
})
c.AddTask(&nodetasks.File{
Path: filepath.Join(pkiDir, "kops-controller.key"),
Contents: keyResource,
Type: nodetasks.FileType_File,
Mode: s("0600"),
Owner: s(wellknownusers.KopsControllerName),
})
return nil
}

View File

@ -0,0 +1,30 @@
/*
Copyright 2020 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 model
import (
"testing"
"k8s.io/kops/upup/pkg/fi"
)
func TestKopsControllerBuilder(t *testing.T) {
RunGoldenTest(t, "tests/golden/minimal", "kops-controller", func(nodeupModelContext *NodeupModelContext, target *fi.ModelBuilderContext) error {
builder := KopsControllerBuilder{NodeupModelContext: nodeupModelContext}
return builder.Build(target)
})
}

View File

@ -2,7 +2,10 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = ["utils.go"], srcs = [
"features.go",
"utils.go",
],
importpath = "k8s.io/kops/pkg/apis/kops/model", importpath = "k8s.io/kops/pkg/apis/kops/model",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [

View File

@ -0,0 +1,26 @@
/*
Copyright 2020 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 model
import (
"k8s.io/kops/pkg/apis/kops"
)
// UseKopsControllerForNodeBootstrap is true if nodeup should use kops-controller for bootstrapping.
func UseKopsControllerForNodeBootstrap(cluster *kops.Cluster) bool {
return kops.CloudProviderID(cluster.Spec.CloudProvider) == kops.CloudProviderAWS && cluster.IsKubernetesGTE("1.19")
}

View File

@ -267,6 +267,11 @@ func (m *KopsModelContext) CloudTags(name string, shared bool) map[string]string
return tags return tags
} }
// UseKopsControllerForNodeBootstrap checks if nodeup should use kops-controller to bootstrap.
func (m *KopsModelContext) UseKopsControllerForNodeBootstrap() bool {
return model.UseKopsControllerForNodeBootstrap(m.Cluster)
}
// UseBootstrapTokens checks if bootstrap tokens are enabled // UseBootstrapTokens checks if bootstrap tokens are enabled
func (m *KopsModelContext) UseBootstrapTokens() bool { func (m *KopsModelContext) UseBootstrapTokens() bool {
if m.Cluster.Spec.KubeAPIServer == nil { if m.Cluster.Spec.KubeAPIServer == nil {

View File

@ -44,6 +44,9 @@ const (
// DNSControllerGossipMemberlist is the port where dns-controller listens for the memberlist-backed gossip // DNSControllerGossipMemberlist is the port where dns-controller listens for the memberlist-backed gossip
DNSControllerGossipMemberlist = 3993 DNSControllerGossipMemberlist = 3993
// KopsControllerPort is the port where kops-controller listens.
KopsControllerPort = 3992
// 4001 is etcd main, 4002 is etcd events, 4003 is etcd cilium // 4001 is etcd main, 4002 is etcd events, 4003 is etcd cilium
// KubeAPIServerHealthCheck is the port where kube-apiserver-healthcheck listens. // KubeAPIServerHealthCheck is the port where kube-apiserver-healthcheck listens.

View File

@ -21,7 +21,7 @@ package wellknownusers
const ( const (
// Generic is the user id we use for non-privileged containers, where we don't need extra permissions // Generic is the user id we use for non-privileged containers, where we don't need extra permissions
// Used by e.g. dns-controller, kops-controller // Used by e.g. dns-controller
Generic = 10001 Generic = 10001
// LegacyEtcd is the user id for the etcd user under the legacy provider // LegacyEtcd is the user id for the etcd user under the legacy provider
@ -30,6 +30,13 @@ const (
// AWSAuthenticator is the user-id for the aws-iam-authenticator (built externally) // AWSAuthenticator is the user-id for the aws-iam-authenticator (built externally)
AWSAuthenticator = 10000 AWSAuthenticator = 10000
// KopsControllerID is the user id for kops-controller, which needs some extra permissions e.g. to write local logs
// This should match the user in cmd/kops-controller/BUILD.bazel
KopsControllerID = 10011
// KopsControllerName is the username for the kops-controller user
KopsControllerName = "kops-controller"
// KubeApiserverHealthcheckID is the user id for kube-apiserver-healthcheck sidecar // KubeApiserverHealthcheckID is the user id for kube-apiserver-healthcheck sidecar
// The user needs some extra permissions e.g. to read local secrets // The user needs some extra permissions e.g. to read local secrets
// This should match the user in cmd/kube-apiserver-healthcheck/BUILD.bazel // This should match the user in cmd/kube-apiserver-healthcheck/BUILD.bazel

View File

@ -2536,8 +2536,10 @@ spec:
name: etc-ssl-certs name: etc-ssl-certs
readOnly: true readOnly: true
{{ end }} {{ end }}
- mountPath: /etc/kubernetes/kops-controller/ - mountPath: /etc/kubernetes/kops-controller/config/
name: kops-controller-config name: kops-controller-config
- mountPath: /etc/kubernetes/kops-controller/pki/
name: kops-controller-pki
command: command:
{{ range $arg := KopsControllerArgv }} {{ range $arg := KopsControllerArgv }}
- "{{ $arg }}" - "{{ $arg }}"
@ -2565,7 +2567,10 @@ spec:
- name: kops-controller-config - name: kops-controller-config
configMap: configMap:
name: kops-controller name: kops-controller
- name: kops-controller-pki
hostPath:
path: /etc/kubernetes/kops-controller/
type: Directory
--- ---
apiVersion: v1 apiVersion: v1

View File

@ -53,8 +53,10 @@ spec:
name: etc-ssl-certs name: etc-ssl-certs
readOnly: true readOnly: true
{{ end }} {{ end }}
- mountPath: /etc/kubernetes/kops-controller/ - mountPath: /etc/kubernetes/kops-controller/config/
name: kops-controller-config name: kops-controller-config
- mountPath: /etc/kubernetes/kops-controller/pki/
name: kops-controller-pki
command: command:
{{ range $arg := KopsControllerArgv }} {{ range $arg := KopsControllerArgv }}
- "{{ $arg }}" - "{{ $arg }}"
@ -82,7 +84,10 @@ spec:
- name: kops-controller-config - name: kops-controller-config
configMap: configMap:
name: kops-controller name: kops-controller
- name: kops-controller-pki
hostPath:
path: /etc/kubernetes/kops-controller/
type: Directory
--- ---
apiVersion: v1 apiVersion: v1

View File

@ -31,6 +31,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"path"
"strconv" "strconv"
"strings" "strings"
"text/template" "text/template"
@ -379,6 +380,15 @@ func (tf *TemplateFunctions) KopsControllerConfig() (string, error) {
ConfigBase: cluster.Spec.ConfigBase, ConfigBase: cluster.Spec.ConfigBase,
} }
if tf.UseKopsControllerForNodeBootstrap() {
pkiDir := "/etc/kubernetes/kops-controller/pki"
config.Server = &kopscontrollerconfig.ServerOptions{
Listen: fmt.Sprintf(":%d", wellknownports.KopsControllerPort),
ServerCertificatePath: path.Join(pkiDir, "kops-controller.crt"),
ServerKeyPath: path.Join(pkiDir, "kops-controller.key"),
}
}
// To avoid indentation problems, we marshal as json. json is a subset of yaml // To avoid indentation problems, we marshal as json. json is a subset of yaml
b, err := json.Marshal(config) b, err := json.Marshal(config)
if err != nil { if err != nil {
@ -397,7 +407,7 @@ func (tf *TemplateFunctions) KopsControllerArgv() ([]string, error) {
// Verbose, but not excessive logging // Verbose, but not excessive logging
argv = append(argv, "--v=2") argv = append(argv, "--v=2")
argv = append(argv, "--conf=/etc/kubernetes/kops-controller/config.yaml") argv = append(argv, "--conf=/etc/kubernetes/kops-controller/config/config.yaml")
return argv, nil return argv, nil
} }

View File

@ -244,6 +244,7 @@ func (c *NodeUpCommand) Run(out io.Writer) error {
loader.Builders = append(loader.Builders, &model.KubeSchedulerBuilder{NodeupModelContext: modelContext}) loader.Builders = append(loader.Builders, &model.KubeSchedulerBuilder{NodeupModelContext: modelContext})
loader.Builders = append(loader.Builders, &model.EtcdManagerTLSBuilder{NodeupModelContext: modelContext}) loader.Builders = append(loader.Builders, &model.EtcdManagerTLSBuilder{NodeupModelContext: modelContext})
loader.Builders = append(loader.Builders, &model.KubeProxyBuilder{NodeupModelContext: modelContext}) loader.Builders = append(loader.Builders, &model.KubeProxyBuilder{NodeupModelContext: modelContext})
loader.Builders = append(loader.Builders, &model.KopsControllerBuilder{NodeupModelContext: modelContext})
loader.Builders = append(loader.Builders, &networking.CommonBuilder{NodeupModelContext: modelContext}) loader.Builders = append(loader.Builders, &networking.CommonBuilder{NodeupModelContext: modelContext})
loader.Builders = append(loader.Builders, &networking.CalicoBuilder{NodeupModelContext: modelContext}) loader.Builders = append(loader.Builders, &networking.CalicoBuilder{NodeupModelContext: modelContext})