Skip to content

Commit

Permalink
Kubernetes configuration, fetch proxy settings.
Browse files Browse the repository at this point in the history
This commit moves proxy kubernetes configuration
to a separate nested block to provide more fine
grained settings:

```yaml
auth:
  kubernetes_ca_cert_path: /tmp/custom-ca
proxy:
  enabled: yes
  kubernetes:
    enabled: yes
    public_addr: [custom.example.com:port]
    api_addr: kuberentes.example.com:443
    listen_addr: localhost:3026
```

1. Kubernetes config section is explicitly enabled
and disabled. It is disabled by default.

2. Public address in kubernetes section
is propagated to tsh profile

The other part of the commit updates Ping
endpoint to send proxy configuration back to
the client, including kubernetes public address
and ssh listen address.

Clients updates profile accordingly to configuration
received from the proxy.
  • Loading branch information
klizhentas committed Aug 6, 2018
1 parent 6b6d734 commit 1f3b4e2
Show file tree
Hide file tree
Showing 32 changed files with 398 additions and 180 deletions.
9 changes: 8 additions & 1 deletion examples/chart/teleport/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,16 @@ config:
public_addr: teleport.example.com
web_listen_addr: 0.0.0.0:3080
listen_addr: 0.0.0.0:3023
kube_listen_addr: 0.0.0.0:3026
https_key_file: /var/lib/certs/tls.key
https_cert_file: /var/lib/certs/tls.crt
# kubernetes section configures
# kubernetes proxy protocol support
kubernetes:
enabled: yes
listen_addr: 0.0.0.0:3026
# public_addr is used to set values
# setup in kubeconfig after tsh login
# public_addr: [kubeproxy.example.com:443]

## Additional container arguments
extraArgs: []
Expand Down
2 changes: 1 addition & 1 deletion integration/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ func (i *TeleInstance) NewUnauthenticatedClient(cfg ClientConfig) (tc *client.Te
SiteName: cfg.Cluster,
ForwardAgent: cfg.ForwardAgent,
}
cconf.SetProxy(proxyHost, proxyWebPort, proxySSHPort, 0)
cconf.SetProxy(proxyHost, proxyWebPort, proxySSHPort)

return client.NewClient(cconf)
}
Expand Down
12 changes: 7 additions & 5 deletions integration/kube_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,9 @@ func (s *KubeSuite) TestKubeTrustedClusters(c *check.C) {
lib.SetInsecureDevMode(true)
defer lib.SetInsecureDevMode(false)

// route allt he traffic to the aux cluster
mainConf.Proxy.KubeClusterOverride = clusterAux
// route all the traffic to the aux cluster
mainConf.Proxy.Kube.Enabled = true
mainConf.Proxy.Kube.ClusterOverride = clusterAux
err = main.CreateEx(nil, mainConf)
c.Assert(err, check.IsNil)

Expand Down Expand Up @@ -563,8 +564,9 @@ func (s *KubeSuite) teleKubeConfig(hostname string) *service.Config {
tconf.ShutdownTimeout = 2 * tconf.ClientTimeout

// set kubernetes specific parameters
tconf.Proxy.KubeListenAddr.Addr = net.JoinHostPort(hostname, s.ports.Pop())
tconf.Proxy.KubeAPIAddr.Addr = s.kubeAPIHost
tconf.Proxy.Kube.Enabled = true
tconf.Proxy.Kube.ListenAddr.Addr = net.JoinHostPort(hostname, s.ports.Pop())
tconf.Proxy.Kube.APIAddr.Addr = s.kubeAPIHost
tconf.Auth.KubeCACertPath = s.kubeCACertPath

return tconf
Expand Down Expand Up @@ -620,7 +622,7 @@ func kubeProxyClient(t *TeleInstance, username string) (*kubernetes.Clientset, *
KeyData: key,
}
config := &rest.Config{
Host: "https://" + t.Config.Proxy.KubeListenAddr.Addr,
Host: "https://" + t.Config.Proxy.Kube.ListenAddr.Addr,
TLSClientConfig: tlsClientConfig,
}
client, err := kubernetes.NewForConfig(config)
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2032,7 +2032,7 @@ func (s *APIServer) setClusterConfig(auth ClientI, w http.ResponseWriter, r *htt
return nil, trace.Wrap(err)
}

return message(fmt.Sprintf("cluster config set: %+v", cc)), nil
return message("cluster config set"), nil
}

func (s *APIServer) getClusterName(auth ClientI, w http.ResponseWriter, r *http.Request, p httprouter.Params, version string) (interface{}, error) {
Expand Down
7 changes: 4 additions & 3 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func NewAuthServer(cfg *InitConfig, opts ...AuthServerOption) (*AuthServer, erro
if cfg.KubeCACertPath == "" {
cfg.KubeCACertPath = teleport.KubeCAPath
}

closeCtx, cancelFunc := context.WithCancel(context.TODO())
as := AuthServer{
clusterName: cfg.ClusterName,
Expand Down Expand Up @@ -157,11 +158,11 @@ type AuthServer struct {
// privateKey is used in tests to use pre-generated private keys
privateKey []byte

// kubeCACertPath is a path to kubernetes certificate authority
kubeCACertPath string

// cipherSuites is a list of ciphersuites that the auth server supports.
cipherSuites []uint16

// kubeCACertPath is a path to PEM encoded kubernetes CA certificate
kubeCACertPath string
}

// runPeriodicOperations runs some periodic bookkeeping operations
Expand Down
6 changes: 3 additions & 3 deletions lib/auth/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,11 @@ type InitConfig struct {
// used in tests that don't need periodc operations.
SkipPeriodicOperations bool

// KubeCACertPath is an optional path to kubernetes CA certificate authority
KubeCACertPath string

// CipherSuites is a list of ciphersuites that the auth server supports.
CipherSuites []uint16

// KubeCACertPath is an optional path to kubernetes CA certificate authority
KubeCACertPath string
}

// Init instantiates and configures an instance of AuthServer
Expand Down
8 changes: 7 additions & 1 deletion lib/auth/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package auth

import (
"io/ioutil"

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/kube/authority"
Expand Down Expand Up @@ -61,8 +63,12 @@ func (s *AuthServer) ProcessKubeCSR(req KubeCSR) (*KubeCSRResponse, error) {

// generate cluster with local kubernetes cluster
if req.ClusterName == s.clusterName.GetClusterName() {
caPEM, err := ioutil.ReadFile(s.kubeCACertPath)
if err != nil {
return nil, trace.Wrap(err)
}
log.Debugf("Generating certificate with local Kubernetes cluster.")
cert, err := authority.ProcessCSR(req.CSR, s.kubeCACertPath)
cert, err := authority.ProcessCSR(req.CSR, caPEM)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/permissions.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2015 Gravitational, Inc.
Copyright 2015-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.
Expand Down
10 changes: 10 additions & 0 deletions lib/auth/tls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,16 @@ func (s *TLSSuite) TestReadOwnRole(c *check.C) {
fixtures.ExpectAccessDenied(c, err)
}

func (s *TLSSuite) TestAuthPreference(c *check.C) {
clt, err := s.server.NewClient(TestAdmin())
c.Assert(err, check.IsNil)

suite := &suite.ServicesTestSuite{
ConfigS: clt,
}
suite.AuthPreference(c)
}

func (s *TLSSuite) TestTunnelConnectionsCRUD(c *check.C) {
clt, err := s.server.NewClient(TestAdmin())
c.Assert(err, check.IsNil)
Expand Down
90 changes: 60 additions & 30 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,13 @@ type Config struct {
// port setting via -p flag, otherwise '0' is passed which means "use server default"
HostPort int

// ProxyHostPort is a host or IP of the proxy (with optional ":ssh_port,https_port").
// ProxyHostPort is a host or IP of the proxy (with optional ":https_port,ssh_port,kube_port").
// The value is taken from the --proxy flag and can look like --proxy=host:5025,5080
ProxyHostPort string

// KubeProxyAddr is a kubernetes proxy address in host:port format
KubeProxyAddr string

// KeyTTL is a time to live for the temporary SSH keypair to remain valid:
KeyTTL time.Duration

Expand Down Expand Up @@ -437,7 +440,8 @@ func (c *Config) LoadProfile(profileDir string, proxyName string) error {
return trace.Wrap(err)
}
// apply the profile to the current configuration:
c.SetProxy(cp.ProxyHost, cp.ProxyWebPort, cp.ProxySSHPort, cp.ProxyKubePort)
c.SetProxy(cp.ProxyHost, cp.ProxyWebPort, cp.ProxySSHPort)
c.KubeProxyAddr = cp.KubeProxyAddr
c.Username = cp.Username
c.SiteName = cp.SiteName
c.LocalForwardPorts, err = ParsePortForwardSpec(cp.ForwardedPorts)
Expand All @@ -461,7 +465,7 @@ func (c *Config) SaveProfile(profileDir string, profileOptions ...ProfileOptions
cp.Username = c.Username
cp.ProxySSHPort = c.ProxySSHPort()
cp.ProxyWebPort = c.ProxyWebPort()
cp.ProxyKubePort = c.ProxyKubePort()
cp.KubeProxyAddr = c.KubeProxyAddr
cp.ForwardedPorts = c.LocalForwardPorts.ToStringSpec()
cp.SiteName = c.SiteName

Expand All @@ -481,12 +485,19 @@ func (c *Config) SaveProfile(profileDir string, profileOptions ...ProfileOptions
return nil
}

func (c *Config) SetProxy(host string, webPort, sshPort, kubePort int) {
if kubePort != 0 {
c.ProxyHostPort = fmt.Sprintf("%s:%d,%d,%d", host, webPort, sshPort, kubePort)
} else {
c.ProxyHostPort = fmt.Sprintf("%s:%d,%d", host, webPort, sshPort)
func (c *Config) SetProxy(host string, webPort, sshPort int) {
c.ProxyHostPort = fmt.Sprintf("%s:%d,%d", host, webPort, sshPort)
}

// KubeProxyHostPort returns kubernetes proxy host and port
func (c *Config) KubeProxyHostPort() (string, int) {
if c.KubeProxyAddr != "" {
addr, err := utils.ParseAddr(c.KubeProxyAddr)
if err == nil {
return addr.Host(), addr.Port(defaults.KubeProxyListenPort)
}
}
return c.ProxyHost(), defaults.KubeProxyListenPort
}

// ProxyHost returns the hostname of the proxy server (without any port numbers)
Expand All @@ -511,10 +522,6 @@ func (c *Config) ProxyWebHostPort() string {
return net.JoinHostPort(c.ProxyHost(), strconv.Itoa(c.ProxyWebPort()))
}

func (c *Config) ProxyKubeHostPort() string {
return net.JoinHostPort(c.ProxyHost(), strconv.Itoa(c.ProxyKubePort()))
}

// ProxyWebPort returns the port number of teleport HTTP proxy stored in the config
// usually 3080 by default.
func (c *Config) ProxyWebPort() (retval int) {
Expand All @@ -532,9 +539,9 @@ func (c *Config) ProxyWebPort() (retval int) {
return retval
}

// ProxySSHPort returns the port number of teleport SSH proxy stored in the config
// proxySSHPort returns the port number of teleport SSH proxy stored in the config
// usually 3023 by default.
func (c *Config) ProxySSHPort() (retval int) {
func (c *Config) proxySSHPort() (retval int, exists bool) {
retval = defaults.SSHProxyListenPort
_, port, err := net.SplitHostPort(c.ProxyHostPort)
if err == nil && len(port) > 0 {
Expand All @@ -545,25 +552,16 @@ func (c *Config) ProxySSHPort() (retval int) {
log.Warnf("invalid proxy SSH port: '%v': %v", ports, err)
}
}
return retval, true
}
return retval
return retval, false
}

// ProxyKubePort returns the port number of teleport Kubernetes proxy stored in the config
// usually 3026 by default.
func (c *Config) ProxyKubePort() (retval int) {
retval = defaults.KubeProxyListenPort
_, port, err := net.SplitHostPort(c.ProxyHostPort)
if err == nil && len(port) > 0 {
ports := strings.Split(port, ",")
if len(ports) > 2 {
retval, err = strconv.Atoi(ports[2])
if err != nil {
log.Warnf("invalid proxy Kubernetes port: '%v': %v", ports, err)
}
}
}
return retval
// ProxySSHPort returns the port number of teleport SSH proxy stored in the config
// usually 3023 by default.
func (c *Config) ProxySSHPort() int {
port, _ := c.proxySSHPort()
return port
}

// ProxySpecified returns true if proxy has been specified
Expand Down Expand Up @@ -1427,6 +1425,10 @@ func (tc *TeleportClient) Login(ctx context.Context, activateKey bool) (*Key, er
}
}

if err := tc.applyProxySettings(pr.Proxy); err != nil {
return nil, trace.Wrap(err)
}

// generate a new keypair. the public key will be signed via proxy if client's
// password+OTP are valid
key, err := NewKey()
Expand Down Expand Up @@ -1503,6 +1505,34 @@ func (tc *TeleportClient) Login(ctx context.Context, activateKey bool) (*Key, er
return key, nil
}

// applyProxySettings updates configuration changes based on the advertised
// proxy settings, user supplied values take precendence - will be preserved
// if set
func (tc *TeleportClient) applyProxySettings(proxySettings ProxySettings) error {
if proxySettings.Kube.Enabled && proxySettings.Kube.PublicAddr != "" && tc.KubeProxyAddr == "" {
_, err := utils.ParseAddr(proxySettings.Kube.PublicAddr)
if err != nil {
return trace.BadParameter(
"failed to parse value received from the server: %q, contact your administrator for help",
proxySettings.Kube.PublicAddr)
}
tc.KubeProxyAddr = proxySettings.Kube.PublicAddr
}
if proxySettings.SSH.ListenAddr != "" {
addr, err := utils.ParseAddr(proxySettings.SSH.ListenAddr)
if err != nil {
return trace.BadParameter(
"failed to parse value received from the server: %q, contact your administrator for help",
proxySettings.SSH.ListenAddr)
}
_, exists := tc.proxySSHPort()
if !exists {
tc.ProxyHostPort = fmt.Sprintf("%s:%d,%d", tc.ProxyHost(), tc.ProxyWebPort(), addr.Port(defaults.SSHProxyListenPort))
}
}
return nil
}

func (tc *TeleportClient) localLogin(secondFactor string, pub []byte) (*auth.SSHLoginResponse, error) {
var err error
var response *auth.SSHLoginResponse
Expand Down
5 changes: 2 additions & 3 deletions lib/client/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (s *APITestSuite) TestConfig(c *check.C) {
c.Assert(conf.ProxySSHHostPort(), check.Equals, "example.org:3023")
c.Assert(conf.ProxyWebHostPort(), check.Equals, "example.org:3080")

conf.SetProxy("example.org", 100, 200, 0)
conf.SetProxy("example.org", 100, 200)
c.Assert(conf.ProxyWebHostPort(), check.Equals, "example.org:100")
c.Assert(conf.ProxySSHHostPort(), check.Equals, "example.org:200")

Expand All @@ -57,10 +57,9 @@ func (s *APITestSuite) TestConfig(c *check.C) {
c.Assert(conf.ProxySSHHostPort(), check.Equals, "example.org:200")
c.Assert(conf.ProxyWebHostPort(), check.Equals, "example.org:3080")

conf.SetProxy("example.org", 100, 200, 300)
conf.SetProxy("example.org", 100, 200)
c.Assert(conf.ProxyWebHostPort(), check.Equals, "example.org:100")
c.Assert(conf.ProxySSHHostPort(), check.Equals, "example.org:200")
c.Assert(conf.ProxyKubeHostPort(), check.Equals, "example.org:300")

}

Expand Down
10 changes: 6 additions & 4 deletions lib/client/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ type ClientProfile struct {
//
// proxy configuration
//
ProxyHost string `yaml:"proxy_host,omitempty"`
ProxySSHPort int `yaml:"proxy_port,omitempty"`
ProxyWebPort int `yaml:"proxy_web_port,omitempty"`
ProxyKubePort int `yaml:"proxy_kube_port,omitempty"`
ProxyHost string `yaml:"proxy_host,omitempty"`
ProxySSHPort int `yaml:"proxy_port,omitempty"`
ProxyWebPort int `yaml:"proxy_web_port,omitempty"`

// KubeProxyAddr is a kubernetes address in host:port format
KubeProxyAddr string `yaml:"kube_proxy_addr,omitempty"`

//
// auth/identity
Expand Down
24 changes: 24 additions & 0 deletions lib/client/weblogin.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,34 @@ func SSHAgentSSOLogin(ctx context.Context, proxyAddr, connectorID string, pubKey
type PingResponse struct {
// Auth contains the forms of authentication the auth server supports.
Auth AuthenticationSettings `json:"auth"`
// Proxy contains the proxy settings.
Proxy ProxySettings `json:"proxy"`
// ServerVersion is the version of Teleport that is running.
ServerVersion string `json:"server_version"`
}

// ProxySettings contains basic information about proxy settings
type ProxySettings struct {
// Kube is a kubernetes specific proxy section
Kube KubeProxySettings `json:"kube"`
// SSH is SSH specific proxy settings
SSH SSHProxySettings `json:"ssh"`
}

// KubeProxySettings is kubernetes proxy settings
type KubeProxySettings struct {
// Enabled is true when kubernetes proxy is enabled
Enabled bool `json:"enabled,omitempty"`
// PublicAddr is a kubernetes proxy public address if set
PublicAddr string `json:"public_addr,omitempty"`
}

// SSHProxySettings is SSH specific proxy settings
type SSHProxySettings struct {
// ListenAddr is SSH listen address
ListenAddr string `json:"listen_addr,omitempty"`
}

// PingResponse contains the form of authentication the auth server supports.
type AuthenticationSettings struct {
// Type is the type of authentication, can be either local or oidc.
Expand Down
Loading

0 comments on commit 1f3b4e2

Please sign in to comment.