allow incluster authentication info lookup

pull/6/head
deads2k 2017-02-24 14:12:34 -05:00
parent 81d01a84e0
commit 3d039f60cf
17 changed files with 295 additions and 63 deletions

View File

@ -466,7 +466,7 @@ function start_apiserver {
# this uses the API port because if you don't have any authenticator, you can't seem to use the secure port at all.
# this matches what happened with the combination in 1.4.
# TODO change this conditionally based on whether API_PORT is on or off
kube::util::wait_for_url "http://${API_HOST_IP}:${API_PORT}/version" "apiserver: " 1 ${WAIT_FOR_URL_API_SERVER} \
kube::util::wait_for_url "http://${API_HOST_IP}:${API_SECURE_PORT}/healthz" "apiserver: " 1 ${WAIT_FOR_URL_API_SERVER} \
|| { echo "check apiserver logs: ${APISERVER_LOG}" ; exit 1 ; }
# Create kubeconfigs for all components, using client certs

View File

@ -17,14 +17,19 @@ limitations under the License.
package options
import (
"encoding/json"
"fmt"
"io/ioutil"
"time"
"github.com/golang/glog"
"github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
"k8s.io/apiserver/pkg/server"
authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)
@ -98,6 +103,8 @@ type DelegatingAuthenticationOptions struct {
ClientCert ClientCertAuthenticationOptions
RequestHeader RequestHeaderAuthenticationOptions
SkipInClusterLookup bool
}
func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions {
@ -128,15 +135,28 @@ func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
s.ClientCert.AddFlags(fs)
s.RequestHeader.AddFlags(fs)
fs.BoolVar(&s.SkipInClusterLookup, "authentication-skip-lookup", s.SkipInClusterLookup, ""+
"If false, the authentication-kubeconfig will be used to lookup missing authentication "+
"configuration from the cluster.")
}
func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.Config) error {
var err error
c, err = c.ApplyClientCert(s.ClientCert.ClientCA)
clientCA, err := s.getClientCA()
if err != nil {
return err
}
c, err = c.ApplyClientCert(clientCA.ClientCA)
if err != nil {
return fmt.Errorf("unable to load client CA file: %v", err)
}
c, err = c.ApplyClientCert(s.RequestHeader.ClientCAFile)
requestHeader, err := s.getRequestHeader()
if err != nil {
return err
}
c, err = c.ApplyClientCert(requestHeader.ClientCAFile)
if err != nil {
return fmt.Errorf("unable to load client CA file: %v", err)
}
@ -165,17 +185,162 @@ func (s *DelegatingAuthenticationOptions) ToAuthenticationConfig() (authenticato
return authenticatorfactory.DelegatingAuthenticatorConfig{}, err
}
clientCA, err := s.getClientCA()
if err != nil {
return authenticatorfactory.DelegatingAuthenticatorConfig{}, err
}
requestHeader, err := s.getRequestHeader()
if err != nil {
return authenticatorfactory.DelegatingAuthenticatorConfig{}, err
}
ret := authenticatorfactory.DelegatingAuthenticatorConfig{
Anonymous: true,
TokenAccessReviewClient: tokenClient,
CacheTTL: s.CacheTTL,
ClientCAFile: s.ClientCert.ClientCA,
RequestHeaderConfig: s.RequestHeader.ToAuthenticationRequestHeaderConfig(),
ClientCAFile: clientCA.ClientCA,
RequestHeaderConfig: requestHeader.ToAuthenticationRequestHeaderConfig(),
}
return ret, nil
}
func (s *DelegatingAuthenticationOptions) newTokenAccessReview() (authenticationclient.TokenReviewInterface, error) {
const (
authenticationConfigMapNamespace = metav1.NamespaceSystem
authenticationConfigMapName = "extension-apiserver-authentication"
authenticationRoleName = "extension-apiserver-authentication-reader"
)
func (s *DelegatingAuthenticationOptions) getClientCA() (*ClientCertAuthenticationOptions, error) {
if len(s.ClientCert.ClientCA) > 0 || s.SkipInClusterLookup {
return &s.ClientCert, nil
}
incluster, err := s.lookupInClusterClientCA()
if err != nil {
glog.Warningf("Unable to get configmap/%s in %s. Usually fixed by "+
"'kubectl create rolebinding -n %s ROLE_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'",
authenticationConfigMapName, authenticationConfigMapNamespace, authenticationConfigMapNamespace, authenticationRoleName)
return nil, err
}
if incluster == nil {
return nil, fmt.Errorf("cluster doesn't provide client-ca-file")
}
return incluster, nil
}
func (s *DelegatingAuthenticationOptions) getRequestHeader() (*RequestHeaderAuthenticationOptions, error) {
if len(s.RequestHeader.ClientCAFile) > 0 || s.SkipInClusterLookup {
return &s.RequestHeader, nil
}
incluster, err := s.lookupInClusterRequestHeader()
if err != nil {
glog.Warningf("Unable to get configmap/%s in %s. Usually fixed by "+
"'kubectl create rolebinding -n %s ROLE_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'",
authenticationConfigMapName, authenticationConfigMapNamespace, authenticationConfigMapNamespace, authenticationRoleName)
return nil, err
}
if incluster == nil {
return nil, fmt.Errorf("cluster doesn't provide requestheader-client-ca-file")
}
return incluster, nil
}
func (s *DelegatingAuthenticationOptions) lookupInClusterClientCA() (*ClientCertAuthenticationOptions, error) {
clientConfig, err := s.getClientConfig()
if err != nil {
return nil, err
}
client, err := coreclient.NewForConfig(clientConfig)
if err != nil {
return nil, err
}
authConfigMap, err := client.ConfigMaps(authenticationConfigMapNamespace).Get(authenticationConfigMapName, metav1.GetOptions{})
if err != nil {
return nil, err
}
clientCA, ok := authConfigMap.Data["client-ca-file"]
if !ok {
return nil, nil
}
f, err := ioutil.TempFile("", "client-ca-file")
if err != nil {
return nil, err
}
if err := ioutil.WriteFile(f.Name(), []byte(clientCA), 0600); err != nil {
return nil, err
}
return &ClientCertAuthenticationOptions{ClientCA: f.Name()}, nil
}
func (s *DelegatingAuthenticationOptions) lookupInClusterRequestHeader() (*RequestHeaderAuthenticationOptions, error) {
clientConfig, err := s.getClientConfig()
if err != nil {
return nil, err
}
client, err := coreclient.NewForConfig(clientConfig)
if err != nil {
return nil, err
}
authConfigMap, err := client.ConfigMaps(authenticationConfigMapNamespace).Get(authenticationConfigMapName, metav1.GetOptions{})
if err != nil {
return nil, err
}
requestHeaderCA, ok := authConfigMap.Data["requestheader-client-ca-file"]
if !ok {
return nil, nil
}
f, err := ioutil.TempFile("", "requestheader-client-ca-file")
if err != nil {
return nil, err
}
if err := ioutil.WriteFile(f.Name(), []byte(requestHeaderCA), 0600); err != nil {
return nil, err
}
usernameHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-username-headers"])
if err != nil {
return nil, err
}
groupHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-group-headers"])
if err != nil {
return nil, err
}
extraHeaderPrefixes, err := deserializeStrings(authConfigMap.Data["requestheader-extra-headers-prefix"])
if err != nil {
return nil, err
}
allowedNames, err := deserializeStrings(authConfigMap.Data["requestheader-allowed-names"])
if err != nil {
return nil, err
}
return &RequestHeaderAuthenticationOptions{
UsernameHeaders: usernameHeaders,
GroupHeaders: groupHeaders,
ExtraHeaderPrefixes: extraHeaderPrefixes,
ClientCAFile: f.Name(),
AllowedNames: allowedNames,
}, nil
}
func deserializeStrings(in string) ([]string, error) {
if len(in) == 0 {
return nil, nil
}
var ret []string
if err := json.Unmarshal([]byte(in), &ret); err != nil {
return nil, err
}
return ret, nil
}
func (s *DelegatingAuthenticationOptions) getClientConfig() (*rest.Config, error) {
var clientConfig *rest.Config
var err error
if len(s.RemoteKubeConfigFile) > 0 {
@ -197,6 +362,14 @@ func (s *DelegatingAuthenticationOptions) newTokenAccessReview() (authentication
clientConfig.QPS = 200
clientConfig.Burst = 400
return clientConfig, nil
}
func (s *DelegatingAuthenticationOptions) newTokenAccessReview() (authenticationclient.TokenReviewInterface, error) {
clientConfig, err := s.getClientConfig()
if err != nil {
return nil, err
}
client, err := authenticationclient.NewForConfig(clientConfig)
if err != nil {
return nil, err

View File

@ -41,11 +41,6 @@ spec:
- "--tls-cert-file=/var/run/serving-cert/tls.crt"
- "--tls-private-key-file=/var/run/serving-cert/tls.key"
- "--tls-ca-file=/var/run/serving-ca/ca.crt"
- "--client-ca-file=/var/run/client-ca/ca.crt"
- "--requestheader-username-headers=X-Remote-User"
- "--requestheader-group-headers=X-Remote-Group"
- "--requestheader-extra-headers-prefix=X-Remote-Extra-"
- "--requestheader-client-ca-file=/var/run/request-header-ca/ca.crt"
- "--etcd-servers=https://etcd.kube-public.svc:4001"
- "--etcd-certfile=/var/run/etcd-client-cert/tls.crt"
- "--etcd-keyfile=/var/run/etcd-client-cert/tls.key"

View File

@ -60,16 +60,18 @@ function start_kube-aggregator {
kubectl delete clusterrolebinding kube-aggregator:system:kube-aggregator > /dev/null 2>&1 || true
kubectl create clusterrolebinding kube-aggregator:system:auth-delegator --clusterrole=system:auth-delegator --serviceaccount=kube-public:kube-aggregator
kubectl create clusterrolebinding kube-aggregator:system:kube-aggregator --clusterrole=system:kube-aggregator --serviceaccount=kube-public:kube-aggregator
kubectl delete rolebinding kube-aggregator:authentication-reader > /dev/null 2>&1 || true
kubectl create rolebinding -n kube-system kube-aggregator:authentication-reader --role=extension-apiserver-authentication-reader --serviceaccount=kube-public:kube-aggregator
# make sure the resources we're about to create don't exist
kubectl -n kube-public delete secret auth-proxy-client serving-etcd serving-kube-aggregator kube-aggregator-etcd > /dev/null 2>&1 || true
kubectl -n kube-public delete configmap etcd-ca kube-aggregator-ca client-ca request-header-ca > /dev/null 2>&1 || true
kubectl -n kube-public delete -f "${AGG_ROOT}/artifacts/self-contained" > /dev/null 2>&1 || true
kubectl -n kube-public create secret tls auth-proxy-client --cert="${FRONT_PROXY_CLIENT_CERT}" --key="${FRONT_PROXY_CLIENT_KEY}"
kubectl -n kube-public create secret tls serving-etcd --cert="${AGGREGATOR_CERT_DIR}/serving-etcd.crt" --key="${AGGREGATOR_CERT_DIR}/serving-etcd.key"
kubectl -n kube-public create secret tls serving-kube-aggregator --cert="${SERVING_CERT}" --key="${SERVING_KEY}"
kubectl -n kube-public create secret tls kube-aggregator-etcd --cert="${AGGREGATOR_CERT_DIR}/client-kube-aggregator-etcd.crt" --key="${AGGREGATOR_CERT_DIR}/client-kube-aggregator-etcd.key"
${sudo} $(which kubectl) -n kube-public create secret tls auth-proxy-client --cert="${FRONT_PROXY_CLIENT_CERT}" --key="${FRONT_PROXY_CLIENT_KEY}"
${sudo} $(which kubectl) -n kube-public create secret tls serving-etcd --cert="${AGGREGATOR_CERT_DIR}/serving-etcd.crt" --key="${AGGREGATOR_CERT_DIR}/serving-etcd.key"
${sudo} $(which kubectl) -n kube-public create secret tls serving-kube-aggregator --cert="${SERVING_CERT}" --key="${SERVING_KEY}"
${sudo} $(which kubectl) -n kube-public create secret tls kube-aggregator-etcd --cert="${AGGREGATOR_CERT_DIR}/client-kube-aggregator-etcd.crt" --key="${AGGREGATOR_CERT_DIR}/client-kube-aggregator-etcd.key"
kubectl -n kube-public create configmap etcd-ca --from-file="ca.crt=${AGGREGATOR_CERT_DIR}/etcd-ca.crt" || true
kubectl -n kube-public create configmap kube-aggregator-ca --from-file="ca.crt=${SERVING_CERT_CA_CERT}" || true
kubectl -n kube-public create configmap client-ca --from-file="ca.crt=${CLIENT_CERT_CA_CERT}" || true

View File

@ -0,0 +1,12 @@
apiVersion: apiregistration.k8s.io/v1alpha1
kind: APIService
metadata:
name: v1alpha1.wardle.k8s.io
spec:
insecureSkipTLSVerify: true
group: wardle.k8s.io
priority: 200
service:
name: api
namespace: wardle
version: v1alpha1

View File

@ -0,0 +1,13 @@
apiVersion: rbac.authorization.k8s.io/v1alpha1
kind: ClusterRoleBinding
metadata:
name: wardle:system:auth-delegator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- apiVersion: v1
kind: ServiceAccount
name: apiserver
namespace: wardle

View File

@ -0,0 +1,14 @@
apiVersion: rbac.authorization.k8s.io/v1alpha1
kind: RoleBinding
metadata:
name: wardle-auth-reader
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: extension-apiserver-authentication-reader
subjects:
- apiVersion: v1
kind: ServiceAccount
name: apiserver
namespace: wardle

View File

@ -1,7 +0,0 @@
apiVersion: serviceinjection.k8s.io/v1alpha1
kind: ServiceInjection
metadata:
name: injector
namespace: kube-system
labels:
sample-label: foo

View File

@ -0,0 +1,25 @@
apiVersion: v1
kind: ReplicationController
metadata:
name: wardle-server
namespace: wardle
labels:
apiserver: "true"
spec:
replicas: 1
selector:
apiserver: "true"
template:
metadata:
labels:
apiserver: "true"
spec:
serviceAccountName: apiserver
containers:
- name: wardle-server
image: kube-sample-apiserver:latest
imagePullPolicy: Never
args:
- "--etcd-servers=http://localhost:2379"
- name: etcd
image: quay.io/coreos/etcd:v3.0.17

View File

@ -0,0 +1,5 @@
kind: ServiceAccount
apiVersion: v1
metadata:
name: apiserver
namespace: wardle

View File

@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: api
namespace: wardle
spec:
ports:
- port: 443
protocol: TCP
targetPort: 443
selector:
apiserver: "true"

View File

@ -0,0 +1,6 @@
apiVersion: wardle.k8s.io/v1alpha1
kind: Flunder
metadata:
name: my-first-flunder
labels:
sample-label: "true"

View File

@ -13,5 +13,5 @@
# limitations under the License.
FROM fedora
ADD kube-service-injection /
ENTRYPOINT ["/kube-service-injection"]
ADD kube-sample-apiserver /
ENTRYPOINT ["/kube-sample-apiserver"]

View File

@ -1,33 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: kube-service-injection
namespace: kube-system
spec:
hostNetwork: true
containers:
- name: kube-service-injection
image: kube-service-injection
imagePullPolicy: Never
args:
- "--secure-port=9443"
- "--authentication-kubeconfig=/all-certs/admin.kubeconfig"
- "--authorization-kubeconfig=/all-certs/admin.kubeconfig"
- "--tls-ca-file=/all-certs/apiserver.crt"
- "--client-ca-file=/all-certs/client-ca.crt"
- "--requestheader-username-headers=X-Remote-User"
- "--requestheader-group-headers=X-Remote-Group"
- "--requestheader-extra-headers-prefix=X-Remote-Extra-"
- "--requestheader-client-ca-file=/all-certs/request-header-ca.crt"
- "--etcd-servers=http://127.0.0.1:2379"
ports:
- containerPort: 9443
hostPort: 9443
volumeMounts:
- name: all-certs
mountPath: /all-certs
readOnly: true
volumes:
- name: all-certs
hostPath:
path: /var/run/kubernetes/

View File

@ -15,14 +15,16 @@
# limitations under the License.
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../../..
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/../../../../..
source "${KUBE_ROOT}/hack/lib/util.sh"
# Register function to be called on EXIT to remove generated binary.
function cleanup {
rm "${KUBE_ROOT}/cmd/kube-sample-apiserver/artifacts/simple-image/kube-sample-apiserver"
rm "${KUBE_ROOT}/vendor/k8s.io/sample-apiserver/artifacts/simple-image/kube-sample-apiserver"
}
trap cleanup EXIT
cp -v ${KUBE_ROOT}/_output/local/bin/linux/amd64/kube-sample-apiserver "${KUBE_ROOT}/cmd/kube-sample-apiserver/artifacts/simple-image/kube-sample-apiserver"
docker build -t kube-sample-apiserver:latest ${KUBE_ROOT}/cmd/kube-sample-apiserver/artifacts/simple-image
pushd "${KUBE_ROOT}/vendor/k8s.io/sample-apiserver"
cp -v ../../../../_output/local/bin/linux/amd64/sample-apiserver ./artifacts/simple-image/kube-sample-apiserver
docker build -t kube-sample-apiserver:latest ./artifacts/simple-image
popd

View File

@ -81,7 +81,6 @@ func TestAggregatedAPIServer(t *testing.T) {
defer os.RemoveAll(certDir)
_, defaultServiceClusterIPRange, _ := net.ParseCIDR("10.0.0.0/24")
proxySigningKey, err := cert.NewPrivateKey()
if err != nil {
t.Fatal(err)
}
@ -93,6 +92,18 @@ func TestAggregatedAPIServer(t *testing.T) {
if err := ioutil.WriteFile(proxyCACertFile.Name(), cert.EncodeCertPEM(proxySigningCert), 0644); err != nil {
t.Fatal(err)
}
clientSigningKey, err := cert.NewPrivateKey()
if err != nil {
t.Fatal(err)
}
clientSigningCert, err := cert.NewSelfSignedCACert(cert.Config{CommonName: "client-ca"}, clientSigningKey)
if err != nil {
t.Fatal(err)
}
clientCACertFile, _ := ioutil.TempFile(certDir, "client-ca.crt")
if err := ioutil.WriteFile(clientCACertFile.Name(), cert.EncodeCertPEM(clientSigningCert), 0644); err != nil {
t.Fatal(err)
}
kubeAPIServerOptions := options.NewServerRunOptions()
kubeAPIServerOptions.SecureServing.ServingOptions.BindAddress = net.ParseIP("127.0.0.1")
@ -106,6 +117,7 @@ func TestAggregatedAPIServer(t *testing.T) {
kubeAPIServerOptions.Authentication.RequestHeader.ExtraHeaderPrefixes = []string{"X-Remote-Extra-"}
kubeAPIServerOptions.Authentication.RequestHeader.AllowedNames = []string{"kube-aggregator"}
kubeAPIServerOptions.Authentication.RequestHeader.ClientCAFile = proxyCACertFile.Name()
kubeAPIServerOptions.Authentication.ClientCert.ClientCA = clientCACertFile.Name()
kubeAPIServerOptions.Authorization.Mode = "RBAC"
config, sharedInformers, err := app.BuildMasterConfig(kubeAPIServerOptions)

1
vendor/BUILD vendored
View File

@ -10573,6 +10573,7 @@ go_library(
"//vendor:k8s.io/apiserver/pkg/util/flag",
"//vendor:k8s.io/client-go/kubernetes/typed/authentication/v1beta1",
"//vendor:k8s.io/client-go/kubernetes/typed/authorization/v1beta1",
"//vendor:k8s.io/client-go/kubernetes/typed/core/v1",
"//vendor:k8s.io/client-go/rest",
"//vendor:k8s.io/client-go/tools/clientcmd",
"//vendor:k8s.io/client-go/util/cert",