Skip to content

Commit

Permalink
Remove Teleport related entries from kubeconfig upon "tsh logout".
Browse files Browse the repository at this point in the history
  • Loading branch information
russjones committed Nov 29, 2018
1 parent 6c37b4a commit b64555a
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 15 deletions.
3 changes: 3 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ const (
// and vice versa.
ComponentKeepAlive = "keepalive"

// ComponentTSH is the "tsh" binary.
ComponentTSH = "tsh"

// DebugEnvVar tells tests to use verbose debug output
DebugEnvVar = "DEBUG"

Expand Down
2 changes: 1 addition & 1 deletion lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ func readProfile(profileDir string, profileName string) (*ProfileStatus, error)
ValidUntil: validUntil,
Extensions: extensions,
Roles: roles,
Cluster: profile.SiteName,
Cluster: profile.Name(),
}, nil
}

Expand Down
31 changes: 29 additions & 2 deletions lib/kube/client/kubeclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

// UpdateKubeconfig updates kubernetes kube config
// UpdateKubeconfig adds Teleport configuration to kubeconfig.
func UpdateKubeconfig(tc *client.TeleportClient) error {
config, err := LoadKubeConfig()
if err != nil {
Expand All @@ -41,7 +41,7 @@ func UpdateKubeconfig(tc *client.TeleportClient) error {
ClientKeyData: creds.Priv,
}
config.Clusters[clusterName] = &clientcmdapi.Cluster{
Server: clusterAddr,
Server: clusterAddr,
CertificateAuthorityData: certAuthorities,
}

Expand All @@ -60,6 +60,33 @@ func UpdateKubeconfig(tc *client.TeleportClient) error {
return SaveKubeConfig(*config)
}

// RemoveKubeconifg removes Teleport configuration from kubeconfig.
func RemoveKubeconifg(tc *client.TeleportClient, clusterName string) error {
// Load existing kubeconfig from disk.
config, err := LoadKubeConfig()
if err != nil {
return trace.Wrap(err)
}

// Remove Teleport related AuthInfos, Clusters, and Contexts from kubeconfig.
delete(config.AuthInfos, clusterName)
delete(config.Clusters, clusterName)
delete(config.Contexts, clusterName)

// Take an element from the list of contexts and make it the current context.
if len(config.Contexts) > 0 {
var currentContext *clientcmdapi.Context
for _, cc := range config.Contexts {
currentContext = cc
break
}
config.CurrentContext = currentContext.Cluster
}

// Update kubeconfig on disk.
return SaveKubeConfig(*config)
}

// LoadKubeconfig tries to read a kubeconfig file and if it can't, returns an error.
// One exception, missing files result in empty configs, not an error.
func LoadKubeConfig() (*clientcmdapi.Config, error) {
Expand Down
20 changes: 18 additions & 2 deletions tool/tsh/help.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
Copyright 2018 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 main

const (
Expand All @@ -7,8 +23,8 @@ const (
EXAMPLES:
Use ports 8080 and 8023 for https and SSH proxy:
$ tsh --proxy=host.example.com:8080,8023 login
$ tsh --proxy=host.example.com:8080,8023 login
Use port 8080 and 3023 (default) for SSH proxy:
$ tsh --proxy=host.example.com:8080 login
Expand Down
59 changes: 49 additions & 10 deletions tool/tsh/tsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ import (
"github.com/sirupsen/logrus"
)

var log = logrus.WithFields(logrus.Fields{
trace.Component: teleport.ComponentTSH,
})

// CLIConf stores command line arguments and flags:
type CLIConf struct {
// UserHost contains "[login]@hostname" argument to SSH command
Expand Down Expand Up @@ -288,17 +292,17 @@ func Run(args []string, underTest bool) {

select {
case sig := <-exitSignals:
logrus.Debugf("signal: %v", sig)
log.Debugf("signal: %v", sig)
cancel()
}
}()
cf.Context = ctx

if cf.Gops {
logrus.Debugf("starting gops agent")
log.Debugf("Starting gops agent.")
err = gops.Listen(&gops.Options{Addr: cf.GopsAddr})
if err != nil {
logrus.Warningf("failed to start gops agent %v", err)
log.Warningf("Failed to start gops agent %v.", err)
}
}

Expand Down Expand Up @@ -485,16 +489,25 @@ func setupNoninteractiveClient(tc *client.TeleportClient, key *client.Key) error

// onLogout deletes a "session certificate" from ~/.tsh for a given proxy
func onLogout(cf *CLIConf) {
// Extract all clusters the user is currently logged into.
active, available, err := client.Status("", "")
if err != nil {
utils.FatalError(err)
return
}
profiles := append(available, active)

// Unlink the current profile.
client.UnlinkCurrentProfile()

// extract the proxy name
// Extract the proxy name.
proxyHost, _, err := net.SplitHostPort(cf.Proxy)
if err != nil {
proxyHost = cf.Proxy
}

switch {
// proxy and username for key to remove
// Proxy and username for key to remove.
case proxyHost != "" && cf.Username != "":
tc, err := makeClient(cf, true)
if err != nil {
Expand All @@ -512,8 +525,24 @@ func onLogout(cf *CLIConf) {
utils.FatalError(err)
return
}

// Get the address of the active Kubernetes proxy to find AuthInfos,
// Clusters, and Contexts in kubeconfig.
clusterName, _ := tc.KubeProxyHostPort()
if tc.SiteName != "" {
clusterName = fmt.Sprintf("%v.%v", tc.SiteName, clusterName)
}

// Remove Teleport related entries from kubeconfig.
log.Debugf("Removing Teleport related entries for '%v' from kubeconfig.", clusterName)
err = kubeclient.RemoveKubeconifg(tc, clusterName)
if err != nil {
utils.FatalError(err)
return
}

fmt.Printf("Logged out %v from %v.\n", cf.Username, proxyHost)
// remove all keys
// Remove all keys.
case proxyHost == "" && cf.Username == "":
// The makeClient function requires a proxy. However this value is not used
// because the user will be logged out from all proxies. Pass a dummy value
Expand All @@ -525,6 +554,16 @@ func onLogout(cf *CLIConf) {
return
}

// Remove Teleport related entries from kubeconfig for all clusters.
for _, profile := range profiles {
log.Debugf("Removing Teleport related entries for '%v' from kubeconfig.", profile.Cluster)
err = kubeclient.RemoveKubeconifg(tc, profile.Cluster)
if err != nil {
utils.FatalError(err)
return
}
}

// Remove all keys from disk and the running agent.
err = tc.LogoutAll()
if err != nil {
Expand Down Expand Up @@ -784,7 +823,7 @@ func makeClient(cf *CLIConf, useProfileLogin bool) (tc *client.TeleportClient, e
if err != nil {
return nil, trace.Wrap(err)
}
logrus.Debugf("Extracted username %q from the identity file %v.", certUsername, cf.IdentityFileIn)
log.Debugf("Extracted username %q from the identity file %v.", certUsername, cf.IdentityFileIn)
c.Username = certUsername

identityAuth, err = authFromIdentity(key)
Expand Down Expand Up @@ -920,7 +959,7 @@ func refuseArgs(command string, args []string) {
// If the "host auth callback" is not returned, user will be prompted to
// trust the proxy server.
func loadIdentity(idFn string) (*client.Key, ssh.HostKeyCallback, error) {
logrus.Infof("Reading identity file: %v", idFn)
log.Infof("Reading identity file: %v", idFn)

f, err := os.Open(idFn)
if err != nil {
Expand Down Expand Up @@ -966,7 +1005,7 @@ func loadIdentity(idFn string) (*client.Key, ssh.HostKeyCallback, error) {
// -cert.pub prefix
if len(cert) == 0 {
certFn := idFn + "-cert.pub"
logrus.Infof("certificate not found in %s. looking in %s", idFn, certFn)
log.Infof("Certificate not found in %s. Looking in %s.", idFn, certFn)
cert, err = ioutil.ReadFile(certFn)
if err != nil {
return nil, nil, trace.Wrap(err)
Expand Down Expand Up @@ -1007,7 +1046,7 @@ func loadIdentity(idFn string) (*client.Key, ssh.HostKeyCallback, error) {
}
}
err = trace.AccessDenied("host %v is untrusted", host)
logrus.Error(err)
log.Error(err)
return err
}
}
Expand Down

0 comments on commit b64555a

Please sign in to comment.