mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-25 10:40:02 +08:00
This commit fixes #3369, refs #3374 It adds support for kuberenetes_users section in roles, allowing Teleport proxy to impersonate user identities. It also extends variable interpolation syntax by adding suffix and prefix to variables and function `email.local`: Example: ```yaml kind: role version: v3 metadata: name: admin spec: allow: # extract email local part from the email claim logins: ['{{email.local(external.email)}}'] # impersonate a kubernetes user with IAM prefix kubernetes_users: ['IAM#{{external.email}}'] # the deny section uses the identical format as the 'allow' section. # the deny rules always override allow rules. deny: {} ``` Some notes on email.local behavior: * This is the only function supported in the template variables for now * In case if the email.local will encounter invalid email address, it will interpolate to empty value, will be removed from resulting output. Changes in impersonation behavior: * By default, if no kubernetes_users is set, which is a majority of cases, user will impersonate themselves, which is the backwards-compatible behavior. * As long as at least one `kubernetes_users` is set, the forwarder will start limiting the list of users allowed by the client to impersonate. * If the users' role set does not include actual user name, it will be rejected, otherwise there will be no way to exclude the user from the list). * If the `kuberentes_users` role set includes only one user (quite frequently that's the real intent), teleport will default to it, otherwise it will refuse to select. This will enable the use case when `kubernetes_users` has just one field to link the user identity with the IAM role, for example `IAM#{{external.email}}` * Previous versions of the forwarding proxy were denying all external impersonation headers, this commit allows 'Impesrsonate-User' and 'Impersonate-Group' header values that are allowed by role set. * Previous versions of the forwarding proxy ignored 'Deny' section of the roles when applied to impersonation, this commit fixes that - roles with deny kubernetes_users and kubernetes_groups section will not allow impersonation of those users and groups.
154 lines
4.4 KiB
Go
154 lines
4.4 KiB
Go
/*
|
|
Copyright 2018-2019 Gravitational, Inc.
|
|
|
|
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 auth
|
|
|
|
import (
|
|
"github.com/gravitational/teleport"
|
|
"github.com/gravitational/teleport/lib/defaults"
|
|
"github.com/gravitational/teleport/lib/modules"
|
|
"github.com/gravitational/teleport/lib/services"
|
|
"github.com/gravitational/teleport/lib/tlsca"
|
|
|
|
"github.com/gravitational/trace"
|
|
)
|
|
|
|
// KubeCSR is a kubernetes CSR request
|
|
type KubeCSR struct {
|
|
// Username of user's certificate
|
|
Username string `json:"username"`
|
|
// ClusterName is a name of the target cluster to generate certificate for
|
|
ClusterName string `json:"cluster_name"`
|
|
// CSR is a kubernetes CSR
|
|
CSR []byte `json:"csr"`
|
|
}
|
|
|
|
// CheckAndSetDefaults checks and sets defaults
|
|
func (a *KubeCSR) CheckAndSetDefaults() error {
|
|
if len(a.CSR) == 0 {
|
|
return trace.BadParameter("missing parameter 'csr'")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// KubeCSRResponse is a response to kubernetes CSR request
|
|
type KubeCSRResponse struct {
|
|
// Cert is a signed certificate PEM block
|
|
Cert []byte `json:"cert"`
|
|
// CertAuthorities is a list of PEM block with trusted cert authorities
|
|
CertAuthorities [][]byte `json:"cert_authorities"`
|
|
// TargetAddr is an optional target address
|
|
// of the kubernetes API server that can be set
|
|
// in the kubeconfig
|
|
TargetAddr string `json:"target_addr"`
|
|
}
|
|
|
|
// ProcessKubeCSR processes CSR request against Kubernetes CA, returns
|
|
// signed certificate if sucessful.
|
|
func (s *AuthServer) ProcessKubeCSR(req KubeCSR) (*KubeCSRResponse, error) {
|
|
if !modules.GetModules().SupportsKubernetes() {
|
|
return nil, trace.AccessDenied(
|
|
"this teleport cluster does not support kubernetes, please contact system administrator for support")
|
|
}
|
|
if err := req.CheckAndSetDefaults(); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
clusterName, err := s.GetClusterName()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
// Certificate for remote cluster is a user certificate
|
|
// with special provisions.
|
|
log.Debugf("Generating certificate to access remote Kubernetes clusters.")
|
|
|
|
hostCA, err := s.GetCertAuthority(services.CertAuthID{
|
|
Type: services.HostCA,
|
|
DomainName: req.ClusterName,
|
|
}, false)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
user, err := s.GetUser(req.Username, false)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
roles, err := services.FetchRoles(user.GetRoles(), s, user.GetTraits())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
ttl := roles.AdjustSessionTTL(defaults.CertDuration)
|
|
|
|
// extract and encode the kubernetes groups of the authenticated
|
|
// user in the newly issued certificate
|
|
kubernetesGroups, kubernetesUsers, err := roles.CheckKubeGroupsAndUsers(0)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
csr, err := tlsca.ParseCertificateRequestPEM(req.CSR)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
userCA, err := s.Trust.GetCertAuthority(services.CertAuthID{
|
|
Type: services.UserCA,
|
|
DomainName: clusterName.GetClusterName(),
|
|
}, true)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
// generate TLS certificate
|
|
tlsAuthority, err := userCA.TLSCA()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
identity := tlsca.Identity{
|
|
Username: user.GetName(),
|
|
Groups: roles.RoleNames(),
|
|
// Generate a certificate restricted for
|
|
// use against a kubernetes endpoint, and not the API server endpoint
|
|
// otherwise proxies can generate certs for any user.
|
|
Usage: []string{teleport.UsageKubeOnly},
|
|
KubernetesGroups: kubernetesGroups,
|
|
KubernetesUsers: kubernetesUsers,
|
|
}
|
|
subject, err := identity.Subject()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
certRequest := tlsca.CertificateRequest{
|
|
Clock: s.clock,
|
|
PublicKey: csr.PublicKey,
|
|
Subject: subject,
|
|
NotAfter: s.clock.Now().UTC().Add(ttl),
|
|
}
|
|
tlsCert, err := tlsAuthority.GenerateCertificate(certRequest)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
re := &KubeCSRResponse{Cert: tlsCert}
|
|
for _, keyPair := range hostCA.GetTLSKeyPairs() {
|
|
re.CertAuthorities = append(re.CertAuthorities, keyPair.Cert)
|
|
}
|
|
return re, nil
|
|
}
|