Split public_addr into web_proxy_addr and ssh_proxy_addr.

This commit is contained in:
Russell Jones 2018-08-29 22:54:35 +00:00 committed by Russell Jones
parent 1439408b34
commit 97074076cb
14 changed files with 246 additions and 157 deletions

View file

@ -847,31 +847,23 @@ func (i *TeleInstance) NewUnauthenticatedClient(cfg ClientConfig) (tc *client.Te
return nil, err return nil, err
} }
// break down proxy address into host, ssh_port and web_port:
proxyConf := &i.Config.Proxy proxyConf := &i.Config.Proxy
proxyHost, sp, err := net.SplitHostPort(proxyConf.SSHAddr.Addr) proxyHost, _, err := net.SplitHostPort(proxyConf.SSHAddr.Addr)
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
// use alternative proxy if necessary var webProxyAddr string
var proxySSHPort, proxyWebPort int var sshProxyAddr string
if cfg.Proxy == nil { if cfg.Proxy == nil {
proxySSHPort, err = strconv.Atoi(sp) webProxyAddr = proxyConf.WebAddr.Addr
if err != nil { sshProxyAddr = proxyConf.SSHAddr.Addr
return nil, trace.Wrap(err)
}
_, sp, err = net.SplitHostPort(proxyConf.WebAddr.Addr)
if err != nil {
return nil, trace.Wrap(err)
}
proxyWebPort, err = strconv.Atoi(sp)
if err != nil {
return nil, trace.Wrap(err)
}
} else { } else {
proxySSHPort, proxyWebPort = cfg.Proxy.SSHPort, cfg.Proxy.WebPort webProxyAddr = net.JoinHostPort(proxyHost, strconv.Itoa(cfg.Proxy.WebPort))
sshProxyAddr = net.JoinHostPort(proxyHost, strconv.Itoa(cfg.Proxy.SSHPort))
} }
cconf := &client.Config{ cconf := &client.Config{
Username: cfg.Login, Username: cfg.Login,
Host: cfg.Host, Host: cfg.Host,
@ -881,8 +873,9 @@ func (i *TeleInstance) NewUnauthenticatedClient(cfg ClientConfig) (tc *client.Te
KeysDir: keyDir, KeysDir: keyDir,
SiteName: cfg.Cluster, SiteName: cfg.Cluster,
ForwardAgent: cfg.ForwardAgent, ForwardAgent: cfg.ForwardAgent,
WebProxyAddr: webProxyAddr,
SSHProxyAddr: sshProxyAddr,
} }
cconf.SetProxy(proxyHost, proxyWebPort, proxySSHPort)
return client.NewClient(cconf) return client.NewClient(cconf)
} }

View file

@ -116,22 +116,13 @@ type Config struct {
// port setting via -p flag, otherwise '0' is passed which means "use server default" // port setting via -p flag, otherwise '0' is passed which means "use server default"
HostPort int HostPort int
// ProxySSHHost is host or IP of the SSH proxy. It is derived from either // WebProxyAddr is the host:port the web proxy can be accessed at.
// the --proxy flag or from the value the proxy advertises in public_addr. WebProxyAddr string
ProxySSHHost string
// ProxySSHPort is the port to use to connect to the SSH proxy. It is // SSHProxyAddr is the host:port the SSH proxy can be accessed at.
// derived from either the --proxy flag or from the value the proxy SSHProxyAddr string
// advertises in listen_addr.
ProxySSHPort int
// ProxyWebHost is host or IP of the web proxy. // KubeProxyAddr is the host:port the Kubernetes proxy can be accessed at.
ProxyWebHost string
// ProxyWebPort is the port to use to connect to the web proxy.
ProxyWebPort int
// KubeProxyAddr is a kubernetes proxy address in host:port format
KubeProxyAddr string KubeProxyAddr string
// KeyTTL is a time to live for the temporary SSH keypair to remain valid: // KeyTTL is a time to live for the temporary SSH keypair to remain valid:
@ -278,7 +269,7 @@ func readProfile(profileDir string, profileName string) (*ProfileStatus, error)
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
keys, err := store.GetKey(profile.ProxyWebHost, profile.Username) keys, err := store.GetKey(profile.Name(), profile.Username)
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
@ -320,7 +311,7 @@ func readProfile(profileDir string, profileName string) (*ProfileStatus, error)
return &ProfileStatus{ return &ProfileStatus{
ProxyURL: url.URL{ ProxyURL: url.URL{
Scheme: "https", Scheme: "https",
Host: net.JoinHostPort(profile.ProxyWebHost, strconv.Itoa(profile.ProxyWebPort)), Host: profile.WebProxyAddr,
}, },
Username: profile.Username, Username: profile.Username,
Logins: cert.ValidPrincipals, Logins: cert.ValidPrincipals,
@ -451,26 +442,33 @@ func (c *Config) LoadProfile(profileDir string, proxyName string) error {
return trace.Wrap(err) return trace.Wrap(err)
} }
// DELETE IN: 3.1.0
// The "proxy_host" field (and associated ports) have been deprecated and
// replaced with "proxy_web_addr" and "proxy_ssh_addr".
if cp.ProxyHost != "" {
if cp.ProxyWebPort == 0 {
cp.ProxyWebPort = defaults.HTTPListenPort
}
if cp.ProxySSHPort == 0 {
cp.ProxySSHPort = defaults.SSHProxyListenPort
}
c.WebProxyAddr = net.JoinHostPort(cp.ProxyHost, strconv.Itoa(cp.ProxyWebPort))
c.SSHProxyAddr = net.JoinHostPort(cp.ProxyHost, strconv.Itoa(cp.ProxySSHPort))
}
c.Username = cp.Username c.Username = cp.Username
c.SiteName = cp.SiteName c.SiteName = cp.SiteName
c.KubeProxyAddr = cp.KubeProxyAddr c.KubeProxyAddr = cp.KubeProxyAddr
// Read in the value for the web proxy (obtained by parsing --proxy). If // UPDATE IN: 3.1.0
// the value of the SSH proxy is not available, fallback to the to the // Remove the above DELETE IN block and below if statements and always set
// value of the web proxy. // WebProxyAddr and SSHProxyAddr. This needs to be done right now to support
c.ProxyWebHost = cp.ProxyWebHost // backward compatibility with Teleport 2.0.
if cp.ProxySSHHost != "" { if cp.WebProxyAddr != "" {
c.ProxySSHHost = cp.ProxySSHHost c.WebProxyAddr = cp.WebProxyAddr
} else {
c.ProxySSHHost = cp.ProxyWebHost
} }
if cp.SSHProxyAddr != "" {
// Same logic as above but apply it to the web and SSH port values as well. c.SSHProxyAddr = cp.SSHProxyAddr
c.ProxyWebPort = cp.ProxyWebPort
if cp.ProxySSHPort != 0 {
c.ProxySSHPort = cp.ProxySSHPort
} else {
c.ProxySSHPort = cp.ProxyWebPort
} }
c.LocalForwardPorts, err = ParsePortForwardSpec(cp.ForwardedPorts) c.LocalForwardPorts, err = ParsePortForwardSpec(cp.ForwardedPorts)
@ -484,18 +482,19 @@ func (c *Config) LoadProfile(profileDir string, proxyName string) error {
// SaveProfile updates the given profiles directory with the current configuration // SaveProfile updates the given profiles directory with the current configuration
// If profileDir is an empty string, the default ~/.tsh is used // If profileDir is an empty string, the default ~/.tsh is used
func (c *Config) SaveProfile(profileDir string, profileOptions ...ProfileOptions) error { func (c *Config) SaveProfile(profileDir string, profileOptions ...ProfileOptions) error {
if c.ProxyWebHost == "" { if c.WebProxyAddr == "" {
return nil return nil
} }
// The profile is saved to a directory with the name of the proxy web endpoint.
webProxyHost, _ := c.WebProxyHostPort()
profileDir = FullProfilePath(profileDir) profileDir = FullProfilePath(profileDir)
profilePath := path.Join(profileDir, c.ProxyWebHost) + ".yaml" profilePath := path.Join(profileDir, webProxyHost) + ".yaml"
var cp ClientProfile var cp ClientProfile
cp.Username = c.Username cp.Username = c.Username
cp.ProxyWebHost = c.ProxyWebHost cp.WebProxyAddr = c.WebProxyAddr
cp.ProxySSHHost = c.ProxySSHHost cp.SSHProxyAddr = c.SSHProxyAddr
cp.ProxyWebPort = c.ProxyWebPort
cp.ProxySSHPort = c.ProxySSHPort
cp.KubeProxyAddr = c.KubeProxyAddr cp.KubeProxyAddr = c.KubeProxyAddr
cp.ForwardedPorts = c.LocalForwardPorts.ToStringSpec() cp.ForwardedPorts = c.LocalForwardPorts.ToStringSpec()
cp.SiteName = c.SiteName cp.SiteName = c.SiteName
@ -519,7 +518,7 @@ func (c *Config) SaveProfile(profileDir string, profileOptions ...ProfileOptions
// ParseProxyHost parses the proxyHost string and updates the config. // ParseProxyHost parses the proxyHost string and updates the config.
// //
// Format of proxyHost string: // Format of proxyHost string:
// proxy_host:<https_proxy_port>,<ssh_proxy_port> // proxy_web_addr:<proxy_web_port>,<proxy_ssh_portt>
func (c *Config) ParseProxyHost(proxyHost string) error { func (c *Config) ParseProxyHost(proxyHost string) error {
host, port, err := net.SplitHostPort(proxyHost) host, port, err := net.SplitHostPort(proxyHost)
if err != nil { if err != nil {
@ -527,36 +526,34 @@ func (c *Config) ParseProxyHost(proxyHost string) error {
port = "" port = ""
} }
// The command line flag specifies both the Web and SSH proxy host. // Split on comma.
c.ProxyWebHost = host
c.ProxySSHHost = host
parts := strings.Split(port, ",") parts := strings.Split(port, ",")
switch { switch {
// Default ports for both the SSH and Web proxy. // Default ports for both the SSH and Web proxy.
case len(parts) == 0: case len(parts) == 0:
c.ProxyWebPort = defaults.HTTPListenPort c.WebProxyAddr = net.JoinHostPort(host, strconv.Itoa(defaults.HTTPListenPort))
c.ProxySSHPort = defaults.SSHProxyListenPort c.SSHProxyAddr = net.JoinHostPort(host, strconv.Itoa(defaults.SSHProxyListenPort))
// User defined HTTP proxy port, default SSH proxy port. // User defined HTTP proxy port, default SSH proxy port.
case len(parts) == 1: case len(parts) == 1:
webPort, err := strconv.Atoi(parts[0]) webPort := parts[0]
if err != nil { if webPort == "" {
webPort = defaults.HTTPListenPort webPort = strconv.Itoa(defaults.HTTPListenPort)
} }
c.ProxyWebPort = webPort c.WebProxyAddr = net.JoinHostPort(host, webPort)
c.ProxySSHPort = defaults.SSHProxyListenPort c.SSHProxyAddr = net.JoinHostPort(host, strconv.Itoa(defaults.SSHProxyListenPort))
// User defined HTTP and SSH proxy ports. // User defined HTTP and SSH proxy ports.
case len(parts) == 2: case len(parts) == 2:
webPort, err := strconv.Atoi(parts[0]) webPort := parts[0]
if err != nil { if webPort == "" {
webPort = defaults.HTTPListenPort webPort = strconv.Itoa(defaults.HTTPListenPort)
} }
sshPort, err := strconv.Atoi(parts[1]) sshPort := parts[1]
if err != nil { if sshPort == "" {
sshPort = defaults.SSHProxyListenPort sshPort = strconv.Itoa(defaults.SSHProxyListenPort)
} }
c.ProxyWebPort = webPort c.WebProxyAddr = net.JoinHostPort(host, webPort)
c.ProxySSHPort = sshPort c.SSHProxyAddr = net.JoinHostPort(host, sshPort)
default: default:
return trace.BadParameter("unable to parse port: %v", port) return trace.BadParameter("unable to parse port: %v", port)
} }
@ -564,7 +561,7 @@ func (c *Config) ParseProxyHost(proxyHost string) error {
return nil return nil
} }
// KubeProxyHostPort returns kubernetes proxy host and port // KubeProxyHostPort returns the host and port of the Kubernetes proxy.
func (c *Config) KubeProxyHostPort() (string, int) { func (c *Config) KubeProxyHostPort() (string, int) {
if c.KubeProxyAddr != "" { if c.KubeProxyAddr != "" {
addr, err := utils.ParseAddr(c.KubeProxyAddr) addr, err := utils.ParseAddr(c.KubeProxyAddr)
@ -572,7 +569,35 @@ func (c *Config) KubeProxyHostPort() (string, int) {
return addr.Host(), addr.Port(defaults.KubeProxyListenPort) return addr.Host(), addr.Port(defaults.KubeProxyListenPort)
} }
} }
return c.ProxyWebHost, defaults.KubeProxyListenPort
webProxyHost, _ := c.WebProxyHostPort()
return webProxyHost, defaults.KubeProxyListenPort
}
// WebProxyHostPort returns the host and port of the web proxy.
func (c *Config) WebProxyHostPort() (string, int) {
if c.WebProxyAddr != "" {
addr, err := utils.ParseAddr(c.WebProxyAddr)
if err == nil {
return addr.Host(), addr.Port(defaults.HTTPListenPort)
}
}
webProxyHost, _ := c.WebProxyHostPort()
return webProxyHost, defaults.HTTPListenPort
}
// SSHProxyHostPort returns the host and port of the SSH proxy.
func (c *Config) SSHProxyHostPort() (string, int) {
if c.SSHProxyAddr != "" {
addr, err := utils.ParseAddr(c.SSHProxyAddr)
if err == nil {
return addr.Host(), addr.Port(defaults.SSHProxyListenPort)
}
}
webProxyHost, _ := c.WebProxyHostPort()
return webProxyHost, defaults.SSHProxyListenPort
} }
// ProxyHost returns the hostname of the proxy server (without any port numbers) // ProxyHost returns the hostname of the proxy server (without any port numbers)
@ -584,17 +609,9 @@ func ProxyHost(proxyHost string) string {
return host return host
} }
func (c *Config) ProxyWebHostPort() string { // ProxySpecified returns true if proxy has been specified.
return net.JoinHostPort(c.ProxyWebHost, strconv.Itoa(c.ProxyWebPort))
}
func (c *Config) ProxySSHHostPort() string {
return net.JoinHostPort(c.ProxySSHHost, strconv.Itoa(c.ProxySSHPort))
}
// ProxySpecified returns true if proxy has been specified
func (c *Config) ProxySpecified() bool { func (c *Config) ProxySpecified() bool {
return len(c.ProxyWebHost) > 0 return c.WebProxyAddr != ""
} }
// TeleportClient is a wrapper around SSH client with teleport specific // TeleportClient is a wrapper around SSH client with teleport specific
@ -629,7 +646,7 @@ func NewClient(c *Config) (tc *TeleportClient, err error) {
} }
log.Infof("No teleport login given. defaulting to %s", c.Username) log.Infof("No teleport login given. defaulting to %s", c.Username)
} }
if c.ProxyWebHost == "" { if c.WebProxyAddr == "" {
return nil, trace.BadParameter("No proxy address specified, missed --proxy flag?") return nil, trace.BadParameter("No proxy address specified, missed --proxy flag?")
} }
if c.HostLogin == "" { if c.HostLogin == "" {
@ -678,7 +695,8 @@ func NewClient(c *Config) (tc *TeleportClient, err error) {
} }
} else { } else {
// initialize the local agent (auth agent which uses local SSH keys signed by the CA): // initialize the local agent (auth agent which uses local SSH keys signed by the CA):
tc.localAgent, err = NewLocalAgent(c.KeysDir, tc.ProxyWebHost, c.Username) webProxyHost, _ := tc.WebProxyHostPort()
tc.localAgent, err = NewLocalAgent(c.KeysDir, webProxyHost, c.Username)
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
@ -1326,7 +1344,7 @@ func (tc *TeleportClient) connectToProxy(ctx context.Context) (*ProxyClient, err
var err error var err error
proxyPrincipal := tc.getProxySSHPrincipal() proxyPrincipal := tc.getProxySSHPrincipal()
proxyAddr := tc.Config.ProxySSHHostPort() proxyAddr := tc.Config.SSHProxyAddr
sshConfig := &ssh.ClientConfig{ sshConfig := &ssh.ClientConfig{
User: proxyPrincipal, User: proxyPrincipal,
HostKeyCallback: tc.HostKeyCallback, HostKeyCallback: tc.HostKeyCallback,
@ -1438,11 +1456,10 @@ func (tc *TeleportClient) LogoutAll() error {
// keystore (and into the ssh-agent) for future use. // keystore (and into the ssh-agent) for future use.
// //
func (tc *TeleportClient) Login(ctx context.Context, activateKey bool) (*Key, error) { func (tc *TeleportClient) Login(ctx context.Context, activateKey bool) (*Key, error) {
httpsProxyHostPort := tc.Config.ProxyWebHostPort() certPool := loopbackPool(tc.Config.WebProxyAddr)
certPool := loopbackPool(httpsProxyHostPort)
// ping the endpoint to see if it's up and find the type of authentication supported // ping the endpoint to see if it's up and find the type of authentication supported
pr, err := Ping(httpsProxyHostPort, tc.InsecureSkipVerify, certPool, tc.AuthConnector) pr, err := Ping(tc.Config.WebProxyAddr, tc.InsecureSkipVerify, certPool, tc.AuthConnector)
if err != nil { if err != nil {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
@ -1578,10 +1595,11 @@ func (tc *TeleportClient) applyProxySettings(proxySettings ProxySettings) error
} }
tc.KubeProxyAddr = proxySettings.Kube.PublicAddr tc.KubeProxyAddr = proxySettings.Kube.PublicAddr
} else if proxySettings.Kube.Enabled && tc.KubeProxyAddr == "" { } else if proxySettings.Kube.Enabled && tc.KubeProxyAddr == "" {
tc.KubeProxyAddr = fmt.Sprintf("%s:%d", tc.ProxyWebHost, defaults.KubeProxyListenPort) webProxyHost, _ := tc.WebProxyHostPort()
tc.KubeProxyAddr = fmt.Sprintf("%s:%d", webProxyHost, defaults.KubeProxyListenPort)
} }
// The hostname of the SSH proxy comes from public_addr. // Read in settings for HTTP endpoint of the proxy.
if proxySettings.SSH.PublicAddr != "" { if proxySettings.SSH.PublicAddr != "" {
addr, err := utils.ParseAddr(proxySettings.SSH.PublicAddr) addr, err := utils.ParseAddr(proxySettings.SSH.PublicAddr)
if err != nil { if err != nil {
@ -1589,9 +1607,18 @@ func (tc *TeleportClient) applyProxySettings(proxySettings ProxySettings) error
"failed to parse value received from the server: %q, contact your administrator for help", "failed to parse value received from the server: %q, contact your administrator for help",
proxySettings.SSH.PublicAddr) proxySettings.SSH.PublicAddr)
} }
tc.ProxySSHHost = addr.Host() tc.WebProxyAddr = net.JoinHostPort(addr.Host(), strconv.Itoa(addr.Port(defaults.HTTPListenPort)))
// Update local agent (that reads/writes to ~/.tsh) with the new address
// of the web proxy. This will control where the keys are stored on disk
// after login.
tc.localAgent.UpdateProxyHost(addr.Host())
} }
// The port of the SSH proxy comes from listen_addr. // Read in settings for the SSH endpoint of the proxy.
//
// If listen_addr is set, take host from ProxyWebHost and port from what
// was set. This is to maintain backward compatibility when Teleport only
// supported public_addr.
if proxySettings.SSH.ListenAddr != "" { if proxySettings.SSH.ListenAddr != "" {
addr, err := utils.ParseAddr(proxySettings.SSH.ListenAddr) addr, err := utils.ParseAddr(proxySettings.SSH.ListenAddr)
if err != nil { if err != nil {
@ -1599,7 +1626,18 @@ func (tc *TeleportClient) applyProxySettings(proxySettings ProxySettings) error
"failed to parse value received from the server: %q, contact your administrator for help", "failed to parse value received from the server: %q, contact your administrator for help",
proxySettings.SSH.ListenAddr) proxySettings.SSH.ListenAddr)
} }
tc.ProxySSHPort = addr.Port(defaults.SSHProxyListenPort) webProxyHost, _ := tc.WebProxyHostPort()
tc.SSHProxyAddr = net.JoinHostPort(webProxyHost, strconv.Itoa(addr.Port(defaults.SSHProxyListenPort)))
}
// If ssh_public_addr is set, override settings from listen_addr.
if proxySettings.SSH.SSHPublicAddr != "" {
addr, err := utils.ParseAddr(proxySettings.SSH.SSHPublicAddr)
if err != nil {
return trace.BadParameter(
"failed to parse value received from the server: %q, contact your administrator for help",
proxySettings.SSH.ListenAddr)
}
tc.SSHProxyAddr = net.JoinHostPort(addr.Host(), strconv.Itoa(addr.Port(defaults.SSHProxyListenPort)))
} }
return nil return nil
@ -1654,8 +1692,7 @@ func (tc *TeleportClient) AddKey(host string, key *Key) (*agent.AddedKey, error)
func (tc *TeleportClient) directLogin(secondFactorType string, pub []byte) (*auth.SSHLoginResponse, error) { func (tc *TeleportClient) directLogin(secondFactorType string, pub []byte) (*auth.SSHLoginResponse, error) {
var err error var err error
httpsProxyHostPort := tc.Config.ProxyWebHostPort() certPool := loopbackPool(tc.Config.WebProxyAddr)
certPool := loopbackPool(httpsProxyHostPort)
var password string var password string
var otpToken string var otpToken string
@ -1675,7 +1712,7 @@ func (tc *TeleportClient) directLogin(secondFactorType string, pub []byte) (*aut
// ask the CA (via proxy) to sign our public key: // ask the CA (via proxy) to sign our public key:
response, err := SSHAgentLogin( response, err := SSHAgentLogin(
httpsProxyHostPort, tc.Config.WebProxyAddr,
tc.Config.Username, tc.Config.Username,
password, password,
otpToken, otpToken,
@ -1692,15 +1729,14 @@ func (tc *TeleportClient) directLogin(secondFactorType string, pub []byte) (*aut
func (tc *TeleportClient) ssoLogin(ctx context.Context, connectorID string, pub []byte, protocol string) (*auth.SSHLoginResponse, error) { func (tc *TeleportClient) ssoLogin(ctx context.Context, connectorID string, pub []byte, protocol string) (*auth.SSHLoginResponse, error) {
log.Debugf("samlLogin start") log.Debugf("samlLogin start")
// ask the CA (via proxy) to sign our public key: // ask the CA (via proxy) to sign our public key:
webProxyAddr := tc.Config.ProxyWebHostPort()
response, err := SSHAgentSSOLogin( response, err := SSHAgentSSOLogin(
ctx, ctx,
webProxyAddr, tc.Config.WebProxyAddr,
connectorID, connectorID,
pub, pub,
tc.KeyTTL, tc.KeyTTL,
tc.InsecureSkipVerify, tc.InsecureSkipVerify,
loopbackPool(webProxyAddr), loopbackPool(tc.Config.WebProxyAddr),
protocol, protocol,
tc.CertificateFormat) tc.CertificateFormat)
return response, trace.Wrap(err) return response, trace.Wrap(err)
@ -1714,8 +1750,7 @@ func (tc *TeleportClient) u2fLogin(pub []byte) (*auth.SSHLoginResponse, error) {
return nil, trace.Wrap(err) return nil, trace.Wrap(err)
} }
httpsProxyHostPort := tc.Config.ProxyWebHostPort() certPool := loopbackPool(tc.Config.WebProxyAddr)
certPool := loopbackPool(httpsProxyHostPort)
password, err := tc.AskPassword() password, err := tc.AskPassword()
if err != nil { if err != nil {
@ -1723,7 +1758,7 @@ func (tc *TeleportClient) u2fLogin(pub []byte) (*auth.SSHLoginResponse, error) {
} }
response, err := SSHAgentU2FLogin( response, err := SSHAgentU2FLogin(
httpsProxyHostPort, tc.Config.WebProxyAddr,
tc.Config.Username, tc.Config.Username,
password, password,
pub, pub,

View file

@ -40,39 +40,45 @@ func (s *APITestSuite) SetUpSuite(c *check.C) {
func (s *APITestSuite) TestConfig(c *check.C) { func (s *APITestSuite) TestConfig(c *check.C) {
var conf Config var conf Config
c.Assert(conf.ProxySpecified(), check.Equals, false) c.Assert(conf.ProxySpecified(), check.Equals, false)
conf.ProxyHostPort = "example.org" err := conf.ParseProxyHost("example.org")
c.Assert(err, check.IsNil)
c.Assert(conf.ProxySpecified(), check.Equals, true) c.Assert(conf.ProxySpecified(), check.Equals, true)
c.Assert(conf.ProxySSHHostPort(), check.Equals, "example.org:3023") c.Assert(conf.SSHProxyAddr, check.Equals, "example.org:3023")
c.Assert(conf.ProxyWebHostPort(), check.Equals, "example.org:3080") c.Assert(conf.WebProxyAddr, check.Equals, "example.org:3080")
conf.SetProxy("example.org", 100, 200) conf.WebProxyAddr = "example.org:100"
c.Assert(conf.ProxyWebHostPort(), check.Equals, "example.org:100") conf.SSHProxyAddr = "example.org:200"
c.Assert(conf.ProxySSHHostPort(), check.Equals, "example.org:200") c.Assert(conf.WebProxyAddr, check.Equals, "example.org:100")
c.Assert(conf.SSHProxyAddr, check.Equals, "example.org:200")
conf.ProxyHostPort = "example.org:200" err = conf.ParseProxyHost("example.org:200")
c.Assert(conf.ProxyWebHostPort(), check.Equals, "example.org:200") c.Assert(err, check.IsNil)
c.Assert(conf.ProxySSHHostPort(), check.Equals, "example.org:3023") c.Assert(conf.WebProxyAddr, check.Equals, "example.org:200")
c.Assert(conf.SSHProxyAddr, check.Equals, "example.org:3023")
conf.ProxyHostPort = "example.org:,200" err = conf.ParseProxyHost("example.org:,200")
c.Assert(conf.ProxySSHHostPort(), check.Equals, "example.org:200") c.Assert(err, check.IsNil)
c.Assert(conf.ProxyWebHostPort(), check.Equals, "example.org:3080") c.Assert(conf.SSHProxyAddr, check.Equals, "example.org:200")
c.Assert(conf.WebProxyAddr, check.Equals, "example.org:3080")
conf.SetProxy("example.org", 100, 200)
c.Assert(conf.ProxyWebHostPort(), check.Equals, "example.org:100")
c.Assert(conf.ProxySSHHostPort(), check.Equals, "example.org:200")
conf.WebProxyAddr = "example.org:100"
conf.SSHProxyAddr = "example.org:200"
c.Assert(conf.WebProxyAddr, check.Equals, "example.org:100")
c.Assert(conf.SSHProxyAddr, check.Equals, "example.org:200")
} }
func (s *APITestSuite) TestNew(c *check.C) { func (s *APITestSuite) TestNew(c *check.C) {
conf := Config{ conf := Config{
Host: "localhost", Host: "localhost",
HostLogin: "vincent", HostLogin: "vincent",
HostPort: 22, HostPort: 22,
KeysDir: "/tmp", KeysDir: "/tmp",
Username: "localuser", Username: "localuser",
ProxyHostPort: "proxy", SiteName: "site",
SiteName: "site",
} }
err := conf.ParseProxyHost("proxy")
c.Assert(err, check.IsNil)
tc, err := NewClient(&conf) tc, err := NewClient(&conf)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(tc, check.NotNil) c.Assert(tc, check.NotNil)

View file

@ -39,10 +39,12 @@ func (s *ClientTestSuite) TestHelperFunctions(c *check.C) {
func (s *ClientTestSuite) SetUpSuite(c *check.C) { func (s *ClientTestSuite) SetUpSuite(c *check.C) {
// create the client: // create the client:
client, err := NewClient(&Config{ config := &Config{
ProxyHostPort: "localhost:3023", KeysDir: c.MkDir(),
KeysDir: c.MkDir(), }
}) err := config.ParseProxyHost("localhost")
c.Assert(err, check.IsNil)
client, err := NewClient(config)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(client, check.NotNil) c.Assert(client, check.NotNil)
s.client = client s.client = client

View file

@ -110,6 +110,11 @@ func NewLocalAgent(keyDir string, proxyHost string, username string) (a *LocalKe
return a, nil return a, nil
} }
// UpdateProxyHost changes the proxy host that the local agent operates on.
func (a *LocalKeyAgent) UpdateProxyHost(proxyHost string) {
a.proxyHost = proxyHost
}
// LoadKey adds a key into the Teleport ssh agent as well as the system ssh // LoadKey adds a key into the Teleport ssh agent as well as the system ssh
// agent. // agent.
func (a *LocalKeyAgent) LoadKey(key Key) (*agent.AddedKey, error) { func (a *LocalKeyAgent) LoadKey(key Key) (*agent.AddedKey, error) {

View file

@ -2,6 +2,7 @@ package client
import ( import (
"io/ioutil" "io/ioutil"
"net"
"os" "os"
"os/user" "os/user"
"path/filepath" "path/filepath"
@ -34,19 +35,13 @@ const CurrentProfileSymlink = "profile"
// type fewer CLI args. // type fewer CLI args.
// //
type ClientProfile struct { type ClientProfile struct {
// ProxyWebHost is the address of the web proxy can be accessed at. // WebProxyAddr is the host:port the web proxy can be accessed at.
ProxyWebHost string `yaml:"proxy_host,omitempty"` WebProxyAddr string `yaml:"web_proxy_addr,omitempty"`
// ProxyWebPort is port the web proxy can be accessed at. // SSHProxyAddr is the host:port the SSH proxy can be accessed at.
ProxyWebPort int `yaml:"proxy_port,omitempty"` SSHProxyAddr string `yaml:"ssh_proxy_addr,omitempty"`
// ProxySSHHost is the host the SSH proxy can be accessed at. // KubeProxyAddr is the host:port the Kubernetes proxy can be accessed at.
ProxySSHHost string `yaml:"proxy_ssh_host,omitempty"`
// ProxySSHPort is the port the SSH proxy can be accessed at.
ProxySSHPort int `yaml:"proxy_ssh_port,omitempty"`
// KubeProxyAddr is a kubernetes address in host:port format
KubeProxyAddr string `yaml:"kube_proxy_addr,omitempty"` KubeProxyAddr string `yaml:"kube_proxy_addr,omitempty"`
// Username is the Teleport username for the client. // Username is the Teleport username for the client.
@ -60,6 +55,27 @@ type ClientProfile struct {
// ForwardedPorts is the list of ports to forward to the target node. // ForwardedPorts is the list of ports to forward to the target node.
ForwardedPorts []string `yaml:"forward_ports,omitempty"` ForwardedPorts []string `yaml:"forward_ports,omitempty"`
// DELETE IN: 3.1.0
// The following fields have been deprecated and replaced with
// "proxy_web_addr" and "proxy_ssh_addr".
ProxyHost string `yaml:"proxy_host,omitempty"`
ProxySSHPort int `yaml:"proxy_port,omitempty"`
ProxyWebPort int `yaml:"proxy_web_port,omitempty"`
}
// Name returns the name of the profile.
func (c *ClientProfile) Name() string {
if c.ProxyHost != "" {
return c.ProxyHost
}
addr, _, err := net.SplitHostPort(c.WebProxyAddr)
if err != nil {
return c.WebProxyAddr
}
return addr
} }
// FullProfilePath returns the full path to the user profile directory. // FullProfilePath returns the full path to the user profile directory.

View file

@ -31,9 +31,8 @@ var _ = check.Suite(&ProfileTestSuite{})
func (s *ProfileTestSuite) TestEverything(c *check.C) { func (s *ProfileTestSuite) TestEverything(c *check.C) {
p := &ClientProfile{ p := &ClientProfile{
ProxyHost: "proxy", WebProxyAddr: "proxy:3088",
ProxySSHPort: 3023, SSHProxyAddr: "proxy:3023",
ProxyWebPort: 3088,
Username: "testuser", Username: "testuser",
ForwardedPorts: []string{"8000:example.com:8000"}, ForwardedPorts: []string{"8000:example.com:8000"},
} }

View file

@ -314,8 +314,11 @@ type SSHProxySettings struct {
// connections on. // connections on.
ListenAddr string `json:"listen_addr,omitempty"` ListenAddr string `json:"listen_addr,omitempty"`
// PublicAddr is the public address the SSH proxy is accessible at. // PublicAddr is the public address of the HTTP proxy.
PublicAddr string `json:"public_addr,omitempty"` PublicAddr string `json:"public_addr,omitempty"`
// SSHPublicAddr is the public address of the SSH proxy.
SSHPublicAddr string `json:"ssh_public_addr,omitempty"`
} }
// PingResponse contains the form of authentication the auth server supports. // PingResponse contains the form of authentication the auth server supports.
@ -516,7 +519,7 @@ func SSHAgentU2FLogin(proxyAddr, user, password string, pubKey []byte, ttl time.
// initClient creates and initializes HTTPS client for talking to teleport proxy HTTPS // initClient creates and initializes HTTPS client for talking to teleport proxy HTTPS
// endpoint. // endpoint.
func initClient(proxyAddr string, insecure bool, pool *x509.CertPool) (*WebClient, *url.URL, error) { func initClient(proxyAddr string, insecure bool, pool *x509.CertPool) (*WebClient, *url.URL, error) {
log.Debugf("HTTPS client init(insecure=%v)", insecure) log.Debugf("HTTPS client init(proxyAddr=%v, insecure=%v)", proxyAddr, insecure)
// validate proxyAddr: // validate proxyAddr:
host, port, err := net.SplitHostPort(proxyAddr) host, port, err := net.SplitHostPort(proxyAddr)

View file

@ -315,6 +315,13 @@ func ApplyFileConfig(fc *FileConfig, cfg *service.Config) error {
} }
cfg.Proxy.PublicAddrs = addrs cfg.Proxy.PublicAddrs = addrs
} }
if len(fc.Proxy.SSHPublicAddr) != 0 {
addrs, err := fc.Proxy.SSHPublicAddr.Addrs(defaults.SSHProxyListenPort)
if err != nil {
return trace.Wrap(err)
}
cfg.Proxy.SSHPublicAddrs = addrs
}
if fc.Proxy.KeyFile != "" { if fc.Proxy.KeyFile != "" {
if !fileExists(fc.Proxy.KeyFile) { if !fileExists(fc.Proxy.KeyFile) {
return trace.Errorf("https key does not exist: %s", fc.Proxy.KeyFile) return trace.Errorf("https key does not exist: %s", fc.Proxy.KeyFile)

View file

@ -125,6 +125,7 @@ var (
"dynamic_config": false, "dynamic_config": false,
"seed_config": false, "seed_config": false,
"public_addr": false, "public_addr": false,
"ssh_public_addr": false,
"cache": true, "cache": true,
"ttl": false, "ttl": false,
"issuer": false, "issuer": false,
@ -722,8 +723,6 @@ type Proxy struct {
KeyFile string `yaml:"https_key_file,omitempty"` KeyFile string `yaml:"https_key_file,omitempty"`
// CertFile is a TLS Certificate file // CertFile is a TLS Certificate file
CertFile string `yaml:"https_cert_file,omitempty"` CertFile string `yaml:"https_cert_file,omitempty"`
// PublicAddr is a publicly advertised address of the proxy
PublicAddr utils.Strings `yaml:"public_addr,omitempty"`
// ProxyProtocol turns on support for HAProxy proxy protocol // ProxyProtocol turns on support for HAProxy proxy protocol
// this is the option that has be turned on only by administrator, // this is the option that has be turned on only by administrator,
// as only admin knows whether service is in front of trusted load balancer // as only admin knows whether service is in front of trusted load balancer
@ -731,6 +730,16 @@ type Proxy struct {
ProxyProtocol string `yaml:"proxy_protocol,omitempty"` ProxyProtocol string `yaml:"proxy_protocol,omitempty"`
// Kube configures kubernetes protocol support of the proxy // Kube configures kubernetes protocol support of the proxy
Kube Kube `yaml:"kubernetes,omitempty"` Kube Kube `yaml:"kubernetes,omitempty"`
// PublicAddr sets the hostport the proxy advertises for the HTTP endpoint.
// The hosts in PublicAddr are included in the list of host principals
// on the SSH certificate.
PublicAddr utils.Strings `yaml:"public_addr,omitempty"`
// SSHPublicAddr sets the hostport the proxy advertises for the SSH endpoint.
// The hosts in PublicAddr are included in the list of host principals
// on the SSH certificate.
SSHPublicAddr utils.Strings `yaml:"ssh_public_addr,omitempty"`
} }
// Kube is a `kubernetes_service` // Kube is a `kubernetes_service`

View file

@ -278,10 +278,16 @@ type ProxyConfig struct {
Limiter limiter.LimiterConfig Limiter limiter.LimiterConfig
// PublicAddrs is a list of the public addresses the Teleport UI can be accessed at, // PublicAddrs is a list of the public addresses the proxy advertises
// it also affects the SSH host principals and DNS names added to the SSH and TLS certs. // for the HTTP endpoint. The hosts in in PublicAddr are included in the
// list of host principals on the TLS and SSH certificate.
PublicAddrs []utils.NetAddr PublicAddrs []utils.NetAddr
// SSHPublicAddrs is a list of the public addresses the proxy advertises
// for the SSH endpoint. The hosts in in PublicAddr are included in the
// list of host principals on the TLS and SSH certificate.
SSHPublicAddrs []utils.NetAddr
// Kube specifies kubernetes proxy configuration // Kube specifies kubernetes proxy configuration
Kube KubeProxyConfig Kube KubeProxyConfig
} }

View file

@ -1483,6 +1483,7 @@ func (process *TeleportProcess) getAdditionalPrincipals(role teleport.Role) ([]s
switch role { switch role {
case teleport.RoleProxy: case teleport.RoleProxy:
addrs = append(process.Config.Proxy.PublicAddrs, utils.NetAddr{Addr: reversetunnel.RemoteKubeProxy}) addrs = append(process.Config.Proxy.PublicAddrs, utils.NetAddr{Addr: reversetunnel.RemoteKubeProxy})
addrs = append(addrs, process.Config.Proxy.SSHPublicAddrs...)
addrs = append(addrs, process.Config.Proxy.Kube.PublicAddrs...) addrs = append(addrs, process.Config.Proxy.Kube.PublicAddrs...)
case teleport.RoleAuth, teleport.RoleAdmin: case teleport.RoleAuth, teleport.RoleAdmin:
addrs = process.Config.Auth.PublicAddrs addrs = process.Config.Auth.PublicAddrs
@ -1760,6 +1761,9 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
if len(cfg.Proxy.PublicAddrs) > 0 { if len(cfg.Proxy.PublicAddrs) > 0 {
proxySettings.SSH.PublicAddr = cfg.Proxy.PublicAddrs[0].String() proxySettings.SSH.PublicAddr = cfg.Proxy.PublicAddrs[0].String()
} }
if len(cfg.Proxy.SSHPublicAddrs) > 0 {
proxySettings.SSH.SSHPublicAddr = cfg.Proxy.SSHPublicAddrs[0].String()
}
if len(cfg.Proxy.Kube.PublicAddrs) > 0 { if len(cfg.Proxy.Kube.PublicAddrs) > 0 {
proxySettings.Kube.PublicAddr = cfg.Proxy.Kube.PublicAddrs[0].String() proxySettings.Kube.PublicAddr = cfg.Proxy.Kube.PublicAddrs[0].String()
} }

View file

@ -417,7 +417,11 @@ func onLogin(cf *CLIConf) {
utils.FatalError(err) utils.FatalError(err)
} }
// Print status to show information of the logged in user. // Print status to show information of the logged in user. Update the
// command line flag (used to print status) for the proxy to make sure any
// advertised settings are picked up.
webProxyHost, _ := tc.WebProxyHostPort()
cf.Proxy = webProxyHost
onStatus(cf) onStatus(cf)
} }

View file

@ -64,8 +64,8 @@ func (s *MainTestSuite) TestMakeClient(c *check.C) {
tc, err = makeClient(&conf, true) tc, err = makeClient(&conf, true)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(tc, check.NotNil) c.Assert(tc, check.NotNil)
c.Assert(tc.Config.ProxySSHHostPort(), check.Equals, "proxy:3023") c.Assert(tc.Config.SSHProxyAddr, check.Equals, "proxy:3023")
c.Assert(tc.Config.ProxyWebHostPort(), check.Equals, "proxy:3080") c.Assert(tc.Config.WebProxyAddr, check.Equals, "proxy:3080")
localUser, err := client.Username() localUser, err := client.Username()
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(tc.Config.HostLogin, check.Equals, localUser) c.Assert(tc.Config.HostLogin, check.Equals, localUser)