mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 15:05:27 +00:00
Merge pull request #10264 from mikedanese/ca-token
add ca cert to token controller and all service accounts
This commit is contained in:
commit
655179dcfb
@ -28,7 +28,13 @@
|
|||||||
{% endif -%}
|
{% endif -%}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
{% set params = "--master=127.0.0.1:8080" + " " + cluster_name + " " + cluster_cidr + " " + allocate_node_cidrs + " " + cloud_provider + " " + cloud_config + service_account_key + pillar['log_level'] -%}
|
{% set root_ca_file = "" -%}
|
||||||
|
|
||||||
|
{% if grains['cloud'] is defined and grains.cloud in [ 'aws', 'gce' ] %}
|
||||||
|
{% set root_ca_file = "--root_ca_file=/srv/kubernetes/ca.crt" -%}
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
{% set params = "--master=127.0.0.1:8080" + " " + cluster_name + " " + cluster_cidr + " " + allocate_node_cidrs + " " + cloud_provider + " " + cloud_config + service_account_key + pillar['log_level'] + " " + root_ca_file -%}
|
||||||
|
|
||||||
{
|
{
|
||||||
"apiVersion": "v1beta3",
|
"apiVersion": "v1beta3",
|
||||||
|
@ -23,6 +23,8 @@ limitations under the License.
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/pprof"
|
"net/http/pprof"
|
||||||
@ -73,6 +75,7 @@ type CMServer struct {
|
|||||||
DeletingPodsQps float32
|
DeletingPodsQps float32
|
||||||
DeletingPodsBurst int
|
DeletingPodsBurst int
|
||||||
ServiceAccountKeyFile string
|
ServiceAccountKeyFile string
|
||||||
|
RootCAFile string
|
||||||
|
|
||||||
ClusterName string
|
ClusterName string
|
||||||
ClusterCIDR util.IPNet
|
ClusterCIDR util.IPNet
|
||||||
@ -156,6 +159,7 @@ func (s *CMServer) AddFlags(fs *pflag.FlagSet) {
|
|||||||
fs.BoolVar(&s.AllocateNodeCIDRs, "allocate-node-cidrs", false, "Should CIDRs for Pods be allocated and set on the cloud provider.")
|
fs.BoolVar(&s.AllocateNodeCIDRs, "allocate-node-cidrs", false, "Should CIDRs for Pods be allocated and set on the cloud provider.")
|
||||||
fs.StringVar(&s.Master, "master", s.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig)")
|
fs.StringVar(&s.Master, "master", s.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig)")
|
||||||
fs.StringVar(&s.Kubeconfig, "kubeconfig", s.Kubeconfig, "Path to kubeconfig file with authorization and master location information.")
|
fs.StringVar(&s.Kubeconfig, "kubeconfig", s.Kubeconfig, "Path to kubeconfig file with authorization and master location information.")
|
||||||
|
fs.StringVar(&s.RootCAFile, "root-ca-file", s.RootCAFile, "If set, this root certificate authority will be included in service account's token secret. This must be a valid PEM-encoded CA bundle.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs the CMServer. This should never exit.
|
// Run runs the CMServer. This should never exit.
|
||||||
@ -243,6 +247,20 @@ func (s *CMServer) Run(_ []string) error {
|
|||||||
}
|
}
|
||||||
pvRecycler.Run()
|
pvRecycler.Run()
|
||||||
|
|
||||||
|
var rootCA []byte
|
||||||
|
|
||||||
|
if s.RootCAFile != "" {
|
||||||
|
rootCA, err := ioutil.ReadFile(s.RootCAFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error reading root-ca-file at %s: %v", s.RootCAFile, err)
|
||||||
|
}
|
||||||
|
if _, err := util.CertsFromPEM(rootCA); err != nil {
|
||||||
|
return fmt.Errorf("error parsing root-ca-file at %s: %v", s.RootCAFile, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rootCA = kubeconfig.CAData
|
||||||
|
}
|
||||||
|
|
||||||
if len(s.ServiceAccountKeyFile) > 0 {
|
if len(s.ServiceAccountKeyFile) > 0 {
|
||||||
privateKey, err := serviceaccount.ReadPrivateKey(s.ServiceAccountKeyFile)
|
privateKey, err := serviceaccount.ReadPrivateKey(s.ServiceAccountKeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -250,9 +268,10 @@ func (s *CMServer) Run(_ []string) error {
|
|||||||
} else {
|
} else {
|
||||||
serviceaccount.NewTokensController(
|
serviceaccount.NewTokensController(
|
||||||
kubeClient,
|
kubeClient,
|
||||||
serviceaccount.DefaultTokenControllerOptions(
|
serviceaccount.TokensControllerOptions{
|
||||||
serviceaccount.JWTTokenGenerator(privateKey),
|
TokenGenerator: serviceaccount.JWTTokenGenerator(privateKey),
|
||||||
),
|
RootCA: rootCA,
|
||||||
|
},
|
||||||
).Run()
|
).Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,9 +157,10 @@ func (s *CMServer) Run(_ []string) error {
|
|||||||
} else {
|
} else {
|
||||||
serviceaccount.NewTokensController(
|
serviceaccount.NewTokensController(
|
||||||
kubeClient,
|
kubeClient,
|
||||||
serviceaccount.DefaultTokenControllerOptions(
|
serviceaccount.TokensControllerOptions{
|
||||||
serviceaccount.JWTTokenGenerator(privateKey),
|
TokenGenerator: serviceaccount.JWTTokenGenerator(privateKey),
|
||||||
),
|
RootCA: kubeconfig.CAData,
|
||||||
|
},
|
||||||
).Run()
|
).Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1961,6 +1961,8 @@ const (
|
|||||||
ServiceAccountTokenKey = "token"
|
ServiceAccountTokenKey = "token"
|
||||||
// ServiceAccountKubeconfigKey is the key of the optional kubeconfig data for SecretTypeServiceAccountToken secrets
|
// ServiceAccountKubeconfigKey is the key of the optional kubeconfig data for SecretTypeServiceAccountToken secrets
|
||||||
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
||||||
|
// ServiceAccountRootCAKey is the key of the optional root certificate authority for SecretTypeServiceAccountToken secrets
|
||||||
|
ServiceAccountRootCAKey = "ca.crt"
|
||||||
|
|
||||||
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
||||||
//
|
//
|
||||||
|
@ -1862,6 +1862,8 @@ const (
|
|||||||
ServiceAccountTokenKey = "token"
|
ServiceAccountTokenKey = "token"
|
||||||
// ServiceAccountKubeconfigKey is the key of the optional kubeconfig data for SecretTypeServiceAccountToken secrets
|
// ServiceAccountKubeconfigKey is the key of the optional kubeconfig data for SecretTypeServiceAccountToken secrets
|
||||||
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
||||||
|
// ServiceAccountRootCAKey is the key of the optional root certificate authority for SecretTypeServiceAccountToken secrets
|
||||||
|
ServiceAccountRootCAKey = "ca.crt"
|
||||||
|
|
||||||
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
||||||
//
|
//
|
||||||
|
@ -1868,6 +1868,8 @@ const (
|
|||||||
ServiceAccountTokenKey = "token"
|
ServiceAccountTokenKey = "token"
|
||||||
// ServiceAccountKubeconfigKey is the key of the optional kubeconfig data for SecretTypeServiceAccountToken secrets
|
// ServiceAccountKubeconfigKey is the key of the optional kubeconfig data for SecretTypeServiceAccountToken secrets
|
||||||
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
||||||
|
// ServiceAccountRootCAKey is the key of the optional root certificate authority for SecretTypeServiceAccountToken secrets
|
||||||
|
ServiceAccountRootCAKey = "ca.crt"
|
||||||
|
|
||||||
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
||||||
//
|
//
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/registered"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/registered"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
@ -236,17 +237,24 @@ func NewOrDie(c *Config) *Client {
|
|||||||
// running inside a pod running on kuberenetes. It will return an error if
|
// running inside a pod running on kuberenetes. It will return an error if
|
||||||
// called from a process not running in a kubernetes environment.
|
// called from a process not running in a kubernetes environment.
|
||||||
func InClusterConfig() (*Config, error) {
|
func InClusterConfig() (*Config, error) {
|
||||||
token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
|
token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountTokenKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
tlsClientConfig := TLSClientConfig{}
|
||||||
|
rootCAFile := "/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountRootCAKey
|
||||||
|
if _, err := util.CertPoolFromFile(rootCAFile); err != nil {
|
||||||
|
glog.Errorf("expected to load root ca config from %s, but got err: %v", rootCAFile, err)
|
||||||
|
} else {
|
||||||
|
tlsClientConfig.CAFile = rootCAFile
|
||||||
|
}
|
||||||
|
|
||||||
return &Config{
|
return &Config{
|
||||||
// TODO: switch to using cluster DNS.
|
// TODO: switch to using cluster DNS.
|
||||||
Host: "https://" + net.JoinHostPort(os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")),
|
Host: "https://" + net.JoinHostPort(os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")),
|
||||||
Version: "v1beta3",
|
Version: "v1beta3",
|
||||||
BearerToken: string(token),
|
BearerToken: string(token),
|
||||||
// TODO: package certs along with the token
|
TLSClientConfig: tlsClientConfig,
|
||||||
Insecure: true,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,11 +44,8 @@ type TokensControllerOptions struct {
|
|||||||
// SecretResync is the time.Duration at which to fully re-list secrets.
|
// SecretResync is the time.Duration at which to fully re-list secrets.
|
||||||
// If zero, re-list will be delayed as long as possible
|
// If zero, re-list will be delayed as long as possible
|
||||||
SecretResync time.Duration
|
SecretResync time.Duration
|
||||||
}
|
// This CA will be added in the secretes of service accounts
|
||||||
|
RootCA []byte
|
||||||
// DefaultTokenControllerOptions returns
|
|
||||||
func DefaultTokenControllerOptions(tokenGenerator TokenGenerator) TokensControllerOptions {
|
|
||||||
return TokensControllerOptions{TokenGenerator: tokenGenerator}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTokensController returns a new *TokensController.
|
// NewTokensController returns a new *TokensController.
|
||||||
@ -56,6 +53,7 @@ func NewTokensController(cl client.Interface, options TokensControllerOptions) *
|
|||||||
e := &TokensController{
|
e := &TokensController{
|
||||||
client: cl,
|
client: cl,
|
||||||
token: options.TokenGenerator,
|
token: options.TokenGenerator,
|
||||||
|
rootCA: options.RootCA,
|
||||||
}
|
}
|
||||||
|
|
||||||
e.serviceAccounts, e.serviceAccountController = framework.NewIndexerInformer(
|
e.serviceAccounts, e.serviceAccountController = framework.NewIndexerInformer(
|
||||||
@ -110,6 +108,8 @@ type TokensController struct {
|
|||||||
client client.Interface
|
client client.Interface
|
||||||
token TokenGenerator
|
token TokenGenerator
|
||||||
|
|
||||||
|
rootCA []byte
|
||||||
|
|
||||||
serviceAccounts cache.Indexer
|
serviceAccounts cache.Indexer
|
||||||
secrets cache.Indexer
|
secrets cache.Indexer
|
||||||
|
|
||||||
@ -293,6 +293,9 @@ func (e *TokensController) createSecret(serviceAccount *api.ServiceAccount) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
secret.Data[api.ServiceAccountTokenKey] = []byte(token)
|
secret.Data[api.ServiceAccountTokenKey] = []byte(token)
|
||||||
|
if e.rootCA != nil && len(e.rootCA) > 0 {
|
||||||
|
secret.Data[api.ServiceAccountRootCAKey] = e.rootCA
|
||||||
|
}
|
||||||
|
|
||||||
// Save the secret
|
// Save the secret
|
||||||
if _, err := e.client.Secrets(serviceAccount.Namespace).Create(secret); err != nil {
|
if _, err := e.client.Secrets(serviceAccount.Namespace).Create(secret); err != nil {
|
||||||
@ -337,6 +340,9 @@ func (e *TokensController) generateTokenIfNeeded(serviceAccount *api.ServiceAcco
|
|||||||
if ok && len(tokenData) > 0 {
|
if ok && len(tokenData) > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if e.rootCA != nil && len(e.rootCA) > 0 {
|
||||||
|
secret.Data[api.ServiceAccountRootCAKey] = e.rootCA
|
||||||
|
}
|
||||||
|
|
||||||
// Generate the token
|
// Generate the token
|
||||||
token, err := e.token.GenerateToken(*serviceAccount, *secret)
|
token, err := e.token.GenerateToken(*serviceAccount, *secret)
|
||||||
|
@ -378,7 +378,7 @@ func TestTokenCreation(t *testing.T) {
|
|||||||
|
|
||||||
client := testclient.NewSimpleFake(tc.ClientObjects...)
|
client := testclient.NewSimpleFake(tc.ClientObjects...)
|
||||||
|
|
||||||
controller := NewTokensController(client, DefaultTokenControllerOptions(generator))
|
controller := NewTokensController(client, TokensControllerOptions{TokenGenerator: generator})
|
||||||
|
|
||||||
// Tell the token controller whether its stores have been synced
|
// Tell the token controller whether its stores have been synced
|
||||||
controller.serviceAccountsSynced = func() bool { return !tc.ServiceAccountsSyncPending }
|
controller.serviceAccountsSynced = func() bool { return !tc.ServiceAccountsSyncPending }
|
||||||
|
@ -123,16 +123,16 @@ func certificatesFromFile(file string) ([]*x509.Certificate, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
certs, err := certsFromPEM(pemBlock)
|
certs, err := CertsFromPEM(pemBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error reading %s: %s", file, err)
|
return nil, fmt.Errorf("error reading %s: %s", file, err)
|
||||||
}
|
}
|
||||||
return certs, nil
|
return certs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// certsFromPEM returns the x509.Certificates contained in the given PEM-encoded byte array
|
// CertsFromPEM returns the x509.Certificates contained in the given PEM-encoded byte array
|
||||||
// Returns an error if a certificate could not be parsed, or if the data does not contain any certificates
|
// Returns an error if a certificate could not be parsed, or if the data does not contain any certificates
|
||||||
func certsFromPEM(pemCerts []byte) ([]*x509.Certificate, error) {
|
func CertsFromPEM(pemCerts []byte) ([]*x509.Certificate, error) {
|
||||||
ok := false
|
ok := false
|
||||||
certs := []*x509.Certificate{}
|
certs := []*x509.Certificate{}
|
||||||
for len(pemCerts) > 0 {
|
for len(pemCerts) > 0 {
|
||||||
|
@ -422,7 +422,7 @@ func startServiceAccountTestServer(t *testing.T) (*client.Client, client.Config,
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Start the service account and service account token controllers
|
// Start the service account and service account token controllers
|
||||||
tokenController := serviceaccount.NewTokensController(rootClient, serviceaccount.DefaultTokenControllerOptions(serviceaccount.JWTTokenGenerator(serviceAccountKey)))
|
tokenController := serviceaccount.NewTokensController(rootClient, serviceaccount.TokensControllerOptions{TokenGenerator: serviceaccount.JWTTokenGenerator(serviceAccountKey)})
|
||||||
tokenController.Run()
|
tokenController.Run()
|
||||||
serviceAccountController := serviceaccount.NewServiceAccountsController(rootClient, serviceaccount.DefaultServiceAccountsControllerOptions())
|
serviceAccountController := serviceaccount.NewServiceAccountsController(rootClient, serviceaccount.DefaultServiceAccountsControllerOptions())
|
||||||
serviceAccountController.Run()
|
serviceAccountController.Run()
|
||||||
|
Loading…
Reference in New Issue
Block a user