mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 01:34:01 +00:00
Add public_addr support for auth and ssh services.
This commit fixes #1803, fixes #1889 * Adds support for public_addr for Proxy and Auth * Parameter advertise_ip now supports host:port format * Fixes incorrect output for tctl get proxies * Fixes duplicate output of some error messages.
This commit is contained in:
parent
2d77a8c1e3
commit
a4c86e0603
|
@ -539,8 +539,8 @@ func (i *Identity) HasTLSConfig() bool {
|
|||
|
||||
// HasPrincipals returns whether identity has principals
|
||||
func (i *Identity) HasPrincipals(additionalPrincipals []string) bool {
|
||||
set := utils.StringsSet(additionalPrincipals)
|
||||
for _, principal := range i.Cert.ValidPrincipals {
|
||||
set := utils.StringsSet(i.Cert.ValidPrincipals)
|
||||
for _, principal := range additionalPrincipals {
|
||||
if _, ok := set[principal]; !ok {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ type CommandLineFlags struct {
|
|||
// --listen-ip flag
|
||||
ListenIP net.IP
|
||||
// --advertise-ip flag
|
||||
AdvertiseIP net.IP
|
||||
AdvertiseIP string
|
||||
// --config flag
|
||||
ConfigFile string
|
||||
// ConfigString is a base64 encoded configuration string
|
||||
|
@ -133,8 +133,8 @@ func ApplyFileConfig(fc *FileConfig, cfg *service.Config) error {
|
|||
|
||||
// apply "advertise_ip" setting:
|
||||
advertiseIP := fc.AdvertiseIP
|
||||
if advertiseIP != nil {
|
||||
if err := validateAdvertiseIP(advertiseIP); err != nil {
|
||||
if advertiseIP != "" {
|
||||
if _, _, err := utils.ParseAdvertiseAddr(advertiseIP); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
cfg.AdvertiseIP = advertiseIP
|
||||
|
@ -276,12 +276,12 @@ func ApplyFileConfig(fc *FileConfig, cfg *service.Config) error {
|
|||
}
|
||||
cfg.Proxy.ReverseTunnelListenAddr = *addr
|
||||
}
|
||||
if fc.Proxy.PublicAddr != "" {
|
||||
addr, err := utils.ParseHostPortAddr(fc.Proxy.PublicAddr, int(defaults.HTTPListenPort))
|
||||
if len(fc.Proxy.PublicAddr) != 0 {
|
||||
addrs, err := fc.Proxy.PublicAddr.Addrs(defaults.HTTPListenPort)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
cfg.Proxy.PublicAddr = *addr
|
||||
cfg.Proxy.PublicAddrs = addrs
|
||||
}
|
||||
if fc.Proxy.KeyFile != "" {
|
||||
if !fileExists(fc.Proxy.KeyFile) {
|
||||
|
@ -362,6 +362,13 @@ func ApplyFileConfig(fc *FileConfig, cfg *service.Config) error {
|
|||
}
|
||||
cfg.ReverseTunnels = append(cfg.ReverseTunnels, tun)
|
||||
}
|
||||
if len(fc.Auth.PublicAddr) != 0 {
|
||||
addrs, err := fc.Auth.PublicAddr.Addrs(defaults.AuthListenPort)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
cfg.Auth.PublicAddrs = addrs
|
||||
}
|
||||
// DELETE IN: 2.4.0
|
||||
if len(fc.Auth.TrustedClusters) > 0 {
|
||||
warningMessage := "Configuring Trusted Clusters using file configuration has " +
|
||||
|
@ -481,7 +488,13 @@ func ApplyFileConfig(fc *FileConfig, cfg *service.Config) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(fc.SSH.PublicAddr) != 0 {
|
||||
addrs, err := fc.SSH.PublicAddr.Addrs(defaults.SSHServerListenPort)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
cfg.SSH.PublicAddrs = addrs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -748,8 +761,8 @@ func Configure(clf *CommandLineFlags, cfg *service.Config) error {
|
|||
}
|
||||
|
||||
// --advertise-ip flag
|
||||
if clf.AdvertiseIP != nil {
|
||||
if err := validateAdvertiseIP(clf.AdvertiseIP); err != nil {
|
||||
if clf.AdvertiseIP != "" {
|
||||
if _, _, err := utils.ParseAdvertiseAddr(clf.AdvertiseIP); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
cfg.AdvertiseIP = clf.AdvertiseIP
|
||||
|
@ -901,10 +914,3 @@ func validateRoles(roles string) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAdvertiseIP(advertiseIP net.IP) error {
|
||||
if advertiseIP.IsLoopback() || advertiseIP.IsUnspecified() || advertiseIP.IsMulticast() {
|
||||
return trace.BadParameter("unreachable advertise IP: %v", advertiseIP)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
@ -313,6 +312,7 @@ func (s *ConfigTestSuite) TestApplyConfig(c *check.C) {
|
|||
conf, err := ReadConfig(bytes.NewBufferString(SmallConfigString))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(conf, check.NotNil)
|
||||
c.Assert(conf.Proxy.PublicAddr, check.DeepEquals, Strings{"web3:443"})
|
||||
|
||||
cfg := service.MakeDefaultConfig()
|
||||
err = ApplyFileConfig(conf, cfg)
|
||||
|
@ -330,7 +330,7 @@ func (s *ConfigTestSuite) TestApplyConfig(c *check.C) {
|
|||
},
|
||||
})
|
||||
c.Assert(cfg.Auth.ClusterName.GetClusterName(), check.Equals, "magadan")
|
||||
c.Assert(cfg.AdvertiseIP, check.DeepEquals, net.ParseIP("10.10.10.1"))
|
||||
c.Assert(cfg.AdvertiseIP, check.Equals, "10.10.10.1")
|
||||
|
||||
c.Assert(cfg.Proxy.Enabled, check.Equals, true)
|
||||
c.Assert(cfg.Proxy.WebAddr.FullAddress(), check.Equals, "tcp://webhost:3080")
|
||||
|
@ -406,7 +406,7 @@ func checkStaticConfig(c *check.C, conf *FileConfig) {
|
|||
c.Assert(conf.Proxy.Configured(), check.Equals, false) // Missing "proxy_service" section must lead to 'not configured'
|
||||
c.Assert(conf.Proxy.Enabled(), check.Equals, true) // Missing "proxy_service" section must lead to 'true'
|
||||
c.Assert(conf.Proxy.Disabled(), check.Equals, false) // Missing "proxy_service" does NOT mean it's been disabled
|
||||
c.Assert(conf.AdvertiseIP.String(), check.Equals, "10.10.10.1")
|
||||
c.Assert(conf.AdvertiseIP, check.Equals, "10.10.10.1:3022")
|
||||
c.Assert(conf.PIDFile, check.Equals, "/var/run/teleport.pid")
|
||||
|
||||
c.Assert(conf.Limits.MaxConnections, check.Equals, int64(90))
|
||||
|
@ -427,6 +427,9 @@ func checkStaticConfig(c *check.C, conf *FileConfig) {
|
|||
c.Assert(conf.SSH.Commands[1].Name, check.Equals, "date")
|
||||
c.Assert(conf.SSH.Commands[1].Command, check.DeepEquals, []string{"/bin/date"})
|
||||
c.Assert(conf.SSH.Commands[1].Period.Nanoseconds(), check.Equals, int64(20000000))
|
||||
c.Assert(conf.SSH.PublicAddr, check.DeepEquals, Strings{
|
||||
"luna3:22",
|
||||
})
|
||||
|
||||
c.Assert(conf.Global.Keys[0].PrivateKey, check.Equals, "private key")
|
||||
c.Assert(conf.Global.Keys[0].Cert, check.Equals, "node.cert")
|
||||
|
@ -451,6 +454,10 @@ func checkStaticConfig(c *check.C, conf *FileConfig) {
|
|||
c.Assert(conf.Auth.StaticTokens, check.DeepEquals,
|
||||
StaticTokens{"proxy,node:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "auth:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"})
|
||||
|
||||
c.Assert(conf.Auth.PublicAddr, check.DeepEquals, Strings{
|
||||
"auth.default.svc.cluster.local:3080",
|
||||
})
|
||||
|
||||
policy, err := conf.CachePolicy.Parse()
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(policy.Enabled, check.Equals, true)
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -354,7 +353,7 @@ type Global struct {
|
|||
Limits ConnectionLimits `yaml:"connection_limits,omitempty"`
|
||||
Logger Log `yaml:"log,omitempty"`
|
||||
Storage backend.Config `yaml:"storage,omitempty"`
|
||||
AdvertiseIP net.IP `yaml:"advertise_ip,omitempty"`
|
||||
AdvertiseIP string `yaml:"advertise_ip,omitempty"`
|
||||
CachePolicy CachePolicy `yaml:"cache,omitempty"`
|
||||
SeedConfig *bool `yaml:"seed_config,omitempty"`
|
||||
|
||||
|
@ -518,6 +517,10 @@ type Auth struct {
|
|||
// it here overrides defaults.
|
||||
// Deprecated: Remove in Teleport 2.4.1.
|
||||
DynamicConfig *bool `yaml:"dynamic_config,omitempty"`
|
||||
|
||||
// PublicAddr sets SSH host principals and TLS DNS names to auth
|
||||
// server certificates
|
||||
PublicAddr Strings `yaml:"public_addr,omitempty"`
|
||||
}
|
||||
|
||||
// TrustedCluster struct holds configuration values under "trusted_clusters" key
|
||||
|
@ -651,6 +654,8 @@ type SSH struct {
|
|||
Commands []CommandLabel `yaml:"commands,omitempty"`
|
||||
PermitUserEnvironment bool `yaml:"permit_user_env,omitempty"`
|
||||
PAM *PAM `yaml:"pam,omitempty"`
|
||||
// PublicAddr sets SSH host principals for SSH service
|
||||
PublicAddr Strings `yaml:"public_addr,omitempty"`
|
||||
}
|
||||
|
||||
// CommandLabel is `command` section of `ssh_service` in the config file
|
||||
|
@ -695,7 +700,7 @@ type Proxy struct {
|
|||
// CertFile is a TLS Certificate file
|
||||
CertFile string `yaml:"https_cert_file,omitempty"`
|
||||
// PublicAddr is a publicly advertised address of the proxy
|
||||
PublicAddr string `yaml:"public_addr,omitempty"`
|
||||
PublicAddr Strings `yaml:"public_addr,omitempty"`
|
||||
// ProxyProtocol turns on support for HAProxy proxy protocol
|
||||
// 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
|
||||
|
@ -938,3 +943,41 @@ func (u *U2F) Parse() (*services.U2F, error) {
|
|||
Facets: facets,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Strings is a list of string that can unmarshal from list or a single yaml value
|
||||
type Strings []string
|
||||
|
||||
// UnmarshalYAML is used to allow Strings to unmarshal from
|
||||
// scalar string value or from the list
|
||||
func (s *Strings) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
// try unmarshal as string
|
||||
var val string
|
||||
err := unmarshal(&val)
|
||||
if err == nil {
|
||||
*s = []string{val}
|
||||
return nil
|
||||
}
|
||||
|
||||
// try unmarshal as slice
|
||||
var slice []string
|
||||
err = unmarshal(&slice)
|
||||
if err == nil {
|
||||
*s = slice
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Addrs returns strings list converted to address list
|
||||
func (s Strings) Addrs(defaultPort int) ([]utils.NetAddr, error) {
|
||||
addrs := make([]utils.NetAddr, len(s))
|
||||
for i, val := range s {
|
||||
addr, err := utils.ParseHostPortAddr(val, defaultPort)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
addrs[i] = *addr
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ const StaticConfigString = `
|
|||
#
|
||||
teleport:
|
||||
nodename: edsger.example.com
|
||||
advertise_ip: 10.10.10.1
|
||||
advertise_ip: 10.10.10.1:3022
|
||||
pid_file: /var/run/teleport.pid
|
||||
auth_servers:
|
||||
- auth0.server.example.org:3024
|
||||
|
@ -77,6 +77,7 @@ auth_service:
|
|||
addresses: ["com-1", "com-2"]
|
||||
- domain_name: tunnel.example.org
|
||||
addresses: ["org-1"]
|
||||
public_addr: ["auth.default.svc.cluster.local:3080"]
|
||||
|
||||
ssh_service:
|
||||
enabled: no
|
||||
|
@ -91,6 +92,7 @@ ssh_service:
|
|||
- name: date
|
||||
command: [/bin/date]
|
||||
period: 20ms
|
||||
public_addr: "luna3:22"
|
||||
`
|
||||
|
||||
const SmallConfigString = `
|
||||
|
@ -129,6 +131,7 @@ proxy_service:
|
|||
enabled: yes
|
||||
web_listen_addr: webhost
|
||||
tunnel_listen_addr: tunnelhost:1001
|
||||
public_addr: web3:443
|
||||
`
|
||||
|
||||
// LegacyAuthenticationSection is the deprecated format for authentication method. We still
|
||||
|
|
|
@ -19,7 +19,6 @@ package service
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
@ -62,9 +61,9 @@ type Config struct {
|
|||
// for teleport roles, this is helpful when server is preconfigured
|
||||
Identities []*auth.Identity
|
||||
|
||||
// AdvertiseIP is used to "publish" an alternative IP address this node
|
||||
// AdvertiseIP is used to "publish" an alternative IP address or hostname this node
|
||||
// can be reached on, if running behind NAT
|
||||
AdvertiseIP net.IP
|
||||
AdvertiseIP string
|
||||
|
||||
// CachePolicy sets caching policy for nodes and proxies
|
||||
// in case if they loose connection to auth servers
|
||||
|
@ -275,8 +274,9 @@ type ProxyConfig struct {
|
|||
|
||||
Limiter limiter.LimiterConfig
|
||||
|
||||
// PublicAddr is the public address the Teleport UI can be accessed at.
|
||||
PublicAddr utils.NetAddr
|
||||
// PublicAddrs is a list of the public addresses the Teleport UI can be accessed at,
|
||||
// it also affects the SSH host principals and DNS names added to the SSH and TLS certs.
|
||||
PublicAddrs []utils.NetAddr
|
||||
}
|
||||
|
||||
// AuthConfig is a configuration of the auth server
|
||||
|
@ -324,6 +324,9 @@ type AuthConfig struct {
|
|||
|
||||
// LicenseFile is a full path to the license file
|
||||
LicenseFile string
|
||||
|
||||
// PublicAddrs affects the SSH host principals and DNS names added to the SSH and TLS certs.
|
||||
PublicAddrs []utils.NetAddr
|
||||
}
|
||||
|
||||
// SSHConfig configures SSH server node role
|
||||
|
@ -339,6 +342,9 @@ type SSHConfig struct {
|
|||
|
||||
// PAM holds PAM configuration for Teleport.
|
||||
PAM *pam.Config
|
||||
|
||||
// PublicAddrs affects the SSH host principals and DNS names added to the SSH and TLS certs.
|
||||
PublicAddrs []utils.NetAddr
|
||||
}
|
||||
|
||||
// MakeDefaultConfig creates a new Config structure and populates it with defaults
|
||||
|
|
|
@ -219,7 +219,7 @@ func (process *TeleportProcess) periodicSyncRotationState() error {
|
|||
process.WaitForEvent(process.ExitContext(), TeleportReadyEvent, eventC)
|
||||
select {
|
||||
case <-eventC:
|
||||
process.Infof("The new service has started successfully. Starting syncing rotation status.")
|
||||
process.Infof("The new service has started successfully. Starting syncing rotation status with period %v.", process.Config.PollingPeriod)
|
||||
case <-process.ExitContext().Done():
|
||||
process.Infof("Periodic rotation sync has exited.")
|
||||
return nil
|
||||
|
@ -296,15 +296,17 @@ func (process *TeleportProcess) syncServiceRotationState(ca services.CertAuthori
|
|||
func (process *TeleportProcess) rotate(conn *Connector, localState auth.StateV2, remote services.Rotation) (bool, error) {
|
||||
id := conn.ClientIdentity.ID
|
||||
local := localState.Spec.Rotation
|
||||
if local.Matches(remote) {
|
||||
// nothing to do, local state and rotation state are in sync
|
||||
return false, nil
|
||||
}
|
||||
|
||||
additionalPrincipals, err := process.getAdditionalPrincipals(id.Role)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err)
|
||||
}
|
||||
principalsChanged := len(additionalPrincipals) != 0 && !conn.ServerIdentity.HasPrincipals(additionalPrincipals)
|
||||
|
||||
if local.Matches(remote) && !principalsChanged {
|
||||
// nothing to do, local state and rotation state are in sync
|
||||
return false, nil
|
||||
}
|
||||
|
||||
storage := process.storage
|
||||
|
||||
|
@ -330,8 +332,8 @@ func (process *TeleportProcess) rotate(conn *Connector, localState auth.StateV2,
|
|||
// that the old node came up and missed the whole rotation
|
||||
// rollback cycle.
|
||||
case "", services.RotationStateStandby:
|
||||
if len(additionalPrincipals) != 0 && !conn.ServerIdentity.HasPrincipals(additionalPrincipals) {
|
||||
process.Infof("%v has updated principals to %q, going to request new principals and update")
|
||||
if principalsChanged {
|
||||
process.Infof("Service %v has updated principals to %q, going to request new principals and update.", id.Role, additionalPrincipals)
|
||||
identity, err := conn.ReRegister(additionalPrincipals)
|
||||
if err != nil {
|
||||
return false, trace.Wrap(err)
|
||||
|
|
|
@ -949,12 +949,20 @@ func (process *TeleportProcess) initAuthService() error {
|
|||
},
|
||||
}
|
||||
host, port, err := net.SplitHostPort(srv.GetAddr())
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
// advertise-ip is explicitly set:
|
||||
if process.Config.AdvertiseIP != nil {
|
||||
if process.Config.AdvertiseIP != "" {
|
||||
ahost, aport, err := utils.ParseAdvertiseAddr(process.Config.AdvertiseIP)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
srv.SetAddr(fmt.Sprintf("%v:%v", process.Config.AdvertiseIP.String(), port))
|
||||
// if port is not set in the advertise addr, use the default one
|
||||
if aport == "" {
|
||||
aport = port
|
||||
}
|
||||
srv.SetAddr(fmt.Sprintf("%v:%v", ahost, aport))
|
||||
} else {
|
||||
// advertise-ip is not set, while the CA is listening on 0.0.0.0? lets try
|
||||
// to guess the 'advertise ip' then:
|
||||
|
@ -1075,6 +1083,13 @@ func (process *TeleportProcess) getRotation(role teleport.Role) (*services.Rotat
|
|||
return &state.Spec.Rotation, nil
|
||||
}
|
||||
|
||||
func (process *TeleportProcess) proxyPublicAddr() utils.NetAddr {
|
||||
if len(process.Config.Proxy.PublicAddrs) == 0 {
|
||||
return utils.NetAddr{}
|
||||
}
|
||||
return process.Config.Proxy.PublicAddrs[0]
|
||||
}
|
||||
|
||||
// initSSH initializes the "node" role, i.e. a simple SSH server connected to the auth server.
|
||||
func (process *TeleportProcess) initSSH() error {
|
||||
process.registerWithAuthServer(teleport.RoleNode, SSHIdentityEvent)
|
||||
|
@ -1138,7 +1153,7 @@ func (process *TeleportProcess) initSSH() error {
|
|||
authClient,
|
||||
cfg.DataDir,
|
||||
cfg.AdvertiseIP,
|
||||
cfg.Proxy.PublicAddr,
|
||||
process.proxyPublicAddr(),
|
||||
regular.SetLimiter(limiter),
|
||||
regular.SetShell(cfg.SSH.Shell),
|
||||
regular.SetAuditLog(conn.Client),
|
||||
|
@ -1349,14 +1364,23 @@ func (process *TeleportProcess) initDiagnosticService() error {
|
|||
}
|
||||
|
||||
// getAdditionalPrincipals returns a list of additional principals to add
|
||||
// to role's service certificate.
|
||||
// to role's service certificates.
|
||||
func (process *TeleportProcess) getAdditionalPrincipals(role teleport.Role) ([]string, error) {
|
||||
var principals []string
|
||||
if process.Config.Hostname != "" {
|
||||
principals = append(principals, process.Config.Hostname)
|
||||
}
|
||||
if process.Config.Proxy.PublicAddr.Addr != "" {
|
||||
host, err := utils.Host(process.Config.Proxy.PublicAddr.Addr)
|
||||
var addrs []utils.NetAddr
|
||||
switch role {
|
||||
case teleport.RoleProxy:
|
||||
addrs = process.Config.Proxy.PublicAddrs
|
||||
case teleport.RoleAuth, teleport.RoleAdmin:
|
||||
addrs = process.Config.Auth.PublicAddrs
|
||||
case teleport.RoleNode:
|
||||
addrs = process.Config.SSH.PublicAddrs
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
host, err := utils.Host(addr.Addr)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
@ -1649,8 +1673,8 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
|
|||
[]ssh.Signer{conn.ServerIdentity.KeySigner},
|
||||
accessPoint,
|
||||
cfg.DataDir,
|
||||
nil,
|
||||
cfg.Proxy.PublicAddr,
|
||||
"",
|
||||
process.proxyPublicAddr(),
|
||||
regular.SetLimiter(proxyLimiter),
|
||||
regular.SetProxyMode(tsrv),
|
||||
regular.SetSessionServer(conn.Client),
|
||||
|
|
|
@ -79,7 +79,7 @@ type Server struct {
|
|||
proxyMode bool
|
||||
proxyTun reversetunnel.Server
|
||||
|
||||
advertiseIP net.IP
|
||||
advertiseIP string
|
||||
proxyPublicAddr utils.NetAddr
|
||||
|
||||
// server UUID gets generated once on the first start and never changes
|
||||
|
@ -344,7 +344,7 @@ func New(addr utils.NetAddr,
|
|||
signers []ssh.Signer,
|
||||
authService auth.AccessPoint,
|
||||
dataDir string,
|
||||
advertiseIP net.IP,
|
||||
advertiseIP string,
|
||||
proxyPublicAddr utils.NetAddr,
|
||||
options ...ServerOption) (*Server, error) {
|
||||
|
||||
|
@ -458,13 +458,13 @@ func (s *Server) PermitUserEnvironment() bool {
|
|||
return s.permitUserEnvironment
|
||||
}
|
||||
|
||||
func (s *Server) setAdvertiseIP(ip net.IP) {
|
||||
func (s *Server) setAdvertiseIP(ip string) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
s.advertiseIP = ip
|
||||
}
|
||||
|
||||
func (s *Server) getAdvertiseIP() net.IP {
|
||||
func (s *Server) getAdvertiseIP() string {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.advertiseIP
|
||||
|
@ -474,11 +474,20 @@ func (s *Server) getAdvertiseIP() net.IP {
|
|||
// as, in "ip:host" form
|
||||
func (s *Server) AdvertiseAddr() string {
|
||||
// set if we have explicit --advertise-ip option
|
||||
if s.getAdvertiseIP() == nil {
|
||||
advertiseIP := s.getAdvertiseIP()
|
||||
if advertiseIP == "" {
|
||||
return s.addr.Addr
|
||||
}
|
||||
_, port, _ := net.SplitHostPort(s.addr.Addr)
|
||||
return net.JoinHostPort(s.getAdvertiseIP().String(), port)
|
||||
ahost, aport, err := utils.ParseAdvertiseAddr(advertiseIP)
|
||||
if err != nil {
|
||||
log.Warningf("Failed to parse advertise address %q, %v, using default value %q.", advertiseIP, err, s.addr.Addr)
|
||||
return s.addr.Addr
|
||||
}
|
||||
if aport == "" {
|
||||
aport = port
|
||||
}
|
||||
return fmt.Sprintf("%v:%v", ahost, aport)
|
||||
}
|
||||
|
||||
func (s *Server) getRole() teleport.Role {
|
||||
|
|
|
@ -137,7 +137,7 @@ func (s *SrvSuite) SetUpTest(c *C) {
|
|||
[]ssh.Signer{s.signer},
|
||||
s.nodeClient,
|
||||
nodeDir,
|
||||
nil,
|
||||
"",
|
||||
utils.NetAddr{},
|
||||
SetNamespace(defaults.Namespace),
|
||||
SetAuditLog(s.nodeClient),
|
||||
|
@ -188,9 +188,9 @@ func (s *SrvSuite) TearDownTest(c *C) {
|
|||
|
||||
func (s *SrvSuite) TestAdvertiseAddr(c *C) {
|
||||
c.Assert(strings.Index(s.srv.AdvertiseAddr(), "127.0.0.1:"), Equals, 0)
|
||||
s.srv.setAdvertiseIP(net.ParseIP("10.10.10.1"))
|
||||
s.srv.setAdvertiseIP("10.10.10.1")
|
||||
c.Assert(strings.Index(s.srv.AdvertiseAddr(), "10.10.10.1:"), Equals, 0)
|
||||
s.srv.setAdvertiseIP(nil)
|
||||
s.srv.setAdvertiseIP("")
|
||||
}
|
||||
|
||||
// TestAgentForwardPermission makes sure if RBAC rules don't allow agent
|
||||
|
@ -492,7 +492,7 @@ func (s *SrvSuite) TestProxyReverseTunnel(c *C) {
|
|||
[]ssh.Signer{s.signer},
|
||||
s.proxyClient,
|
||||
c.MkDir(),
|
||||
nil,
|
||||
"",
|
||||
utils.NetAddr{},
|
||||
SetProxyMode(reverseTunnelServer),
|
||||
SetSessionServer(s.proxyClient),
|
||||
|
@ -564,7 +564,7 @@ func (s *SrvSuite) TestProxyReverseTunnel(c *C) {
|
|||
[]ssh.Signer{s.signer},
|
||||
s.nodeClient,
|
||||
c.MkDir(),
|
||||
nil,
|
||||
"",
|
||||
utils.NetAddr{},
|
||||
SetShell("/bin/sh"),
|
||||
SetLabels(
|
||||
|
@ -666,7 +666,7 @@ func (s *SrvSuite) TestProxyRoundRobin(c *C) {
|
|||
[]ssh.Signer{s.signer},
|
||||
s.proxyClient,
|
||||
c.MkDir(),
|
||||
nil,
|
||||
"",
|
||||
utils.NetAddr{},
|
||||
SetProxyMode(reverseTunnelServer),
|
||||
SetSessionServer(s.proxyClient),
|
||||
|
@ -767,7 +767,7 @@ func (s *SrvSuite) TestProxyDirectAccess(c *C) {
|
|||
[]ssh.Signer{s.signer},
|
||||
s.proxyClient,
|
||||
c.MkDir(),
|
||||
nil,
|
||||
"",
|
||||
utils.NetAddr{},
|
||||
SetProxyMode(reverseTunnelServer),
|
||||
SetSessionServer(s.proxyClient),
|
||||
|
@ -876,7 +876,7 @@ func (s *SrvSuite) TestLimiter(c *C) {
|
|||
[]ssh.Signer{s.signer},
|
||||
s.nodeClient,
|
||||
nodeStateDir,
|
||||
nil,
|
||||
"",
|
||||
utils.NetAddr{},
|
||||
SetLimiter(limiter),
|
||||
SetShell("/bin/sh"),
|
||||
|
|
|
@ -139,10 +139,10 @@ func UserMessageFromError(err error) string {
|
|||
// error occured" message.
|
||||
if er, ok := err.(*trace.TraceErr); ok {
|
||||
if er.Message != "" {
|
||||
return fmt.Sprintf("%v: %v", er.Message, er.Err.Error())
|
||||
return fmt.Sprintf("error: %v", er.Message)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("A fatal error occurred: %v", err.Error())
|
||||
return fmt.Sprintf("error: %v", err.Error())
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -32,6 +32,36 @@ import (
|
|||
"github.com/pborman/uuid"
|
||||
)
|
||||
|
||||
// ParseAdvertiseAddress validates advertise address,
|
||||
// makes sure it's not an unreachable or multicast address
|
||||
// returns address split into host and port, port could be empty
|
||||
// if not specified
|
||||
func ParseAdvertiseAddr(advertiseIP string) (string, string, error) {
|
||||
advertiseIP = strings.TrimSpace(advertiseIP)
|
||||
host := advertiseIP
|
||||
port := ""
|
||||
if len(net.ParseIP(host)) == 0 && strings.Contains(advertiseIP, ":") {
|
||||
var err error
|
||||
host, port, err = net.SplitHostPort(advertiseIP)
|
||||
if err != nil {
|
||||
return "", "", trace.BadParameter("failed to parse address %q", advertiseIP)
|
||||
}
|
||||
if _, err := strconv.Atoi(port); err != nil {
|
||||
return "", "", trace.BadParameter("bad port %q, expected integer", port)
|
||||
}
|
||||
if host == "" {
|
||||
return "", "", trace.BadParameter("missing host parameter")
|
||||
}
|
||||
}
|
||||
ip := net.ParseIP(host)
|
||||
if len(ip) != 0 {
|
||||
if ip.IsLoopback() || ip.IsUnspecified() || ip.IsMulticast() {
|
||||
return "", "", trace.BadParameter("unreachable advertise IP: %v", advertiseIP)
|
||||
}
|
||||
}
|
||||
return host, port, nil
|
||||
}
|
||||
|
||||
// StringsSet creates set of string (map[string]struct{})
|
||||
// from a list of strings
|
||||
func StringsSet(in []string) map[string]struct{} {
|
||||
|
|
|
@ -142,3 +142,38 @@ func (s *UtilsSuite) TestParseSessionsURI(c *check.C) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseAdvertiseAddr tests parsing of advertise address
|
||||
func (s *UtilsSuite) TestParseAdvertiseAddr(c *check.C) {
|
||||
testCases := []struct {
|
||||
info string
|
||||
in string
|
||||
host string
|
||||
port string
|
||||
err error
|
||||
}{
|
||||
{info: "ok address", in: "192.168.1.1", host: "192.168.1.1"},
|
||||
{info: "trim space", in: " 192.168.1.1 ", host: "192.168.1.1"},
|
||||
{info: "multicast address", in: "224.0.0.0", err: trace.BadParameter("")},
|
||||
{info: "multicast address", in: " 224.0.0.0 ", err: trace.BadParameter("")},
|
||||
{info: "ok address and port", in: "192.168.1.1:22", host: "192.168.1.1", port: "22"},
|
||||
{info: "ok address and bad port", in: "192.168.1.1:b", err: trace.BadParameter("")},
|
||||
{info: "ok host", in: "localhost", host: "localhost"},
|
||||
{info: "ok host and port", in: "localhost:33", host: "localhost", port: "33"},
|
||||
{info: "missing host ", in: ":33", err: trace.BadParameter("")},
|
||||
{info: "missing port", in: "localhost:", err: trace.BadParameter("")},
|
||||
{info: "ipv6 address", in: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", host: "2001:0db8:85a3:0000:0000:8a2e:0370:7334"},
|
||||
{info: "ipv6 address and port", in: "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:443", host: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", port: "443"},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
comment := check.Commentf("test case %v %q", i, testCase.info)
|
||||
host, port, err := ParseAdvertiseAddr(testCase.in)
|
||||
if testCase.err == nil {
|
||||
c.Assert(err, check.IsNil, comment)
|
||||
c.Assert(host, check.Equals, testCase.host)
|
||||
c.Assert(port, check.Equals, testCase.port)
|
||||
} else {
|
||||
c.Assert(err, check.FitsTypeOf, testCase.err, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,7 +174,7 @@ func (s *WebSuite) SetUpTest(c *C) {
|
|||
[]ssh.Signer{signer},
|
||||
nodeClient,
|
||||
nodeDataDir,
|
||||
nil,
|
||||
"",
|
||||
utils.NetAddr{},
|
||||
regular.SetNamespace(defaults.Namespace),
|
||||
regular.SetShell("/bin/sh"),
|
||||
|
@ -221,7 +221,7 @@ func (s *WebSuite) SetUpTest(c *C) {
|
|||
[]ssh.Signer{signer},
|
||||
s.proxyClient,
|
||||
c.MkDir(),
|
||||
nil,
|
||||
"",
|
||||
utils.NetAddr{},
|
||||
regular.SetProxyMode(revTunServer),
|
||||
regular.SetSessionServer(s.proxyClient),
|
||||
|
|
|
@ -376,7 +376,7 @@ func (g *ResourceCommand) getCollection(client auth.ClientI) (c ResourceCollecti
|
|||
}
|
||||
return &serverCollection{servers: servers}, nil
|
||||
case services.KindProxy:
|
||||
servers, err := client.GetAuthServers()
|
||||
servers, err := client.GetProxies()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ func Run(options Options) (executedCommand string, conf *service.Config) {
|
|||
"Full path to the PID file. By default no PID file will be created").StringVar(&ccf.PIDFile)
|
||||
start.Flag("advertise-ip",
|
||||
"IP to advertise to clients if running behind NAT").
|
||||
IPVar(&ccf.AdvertiseIP)
|
||||
StringVar(&ccf.AdvertiseIP)
|
||||
start.Flag("listen-ip",
|
||||
fmt.Sprintf("IP address to bind to [%s]", defaults.BindIP)).
|
||||
Short('l').
|
||||
|
@ -159,7 +159,6 @@ func Run(options Options) (executedCommand string, conf *service.Config) {
|
|||
if err != nil {
|
||||
utils.FatalError(err)
|
||||
}
|
||||
log.Debug("Clean exit.")
|
||||
return command, conf
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ package common
|
|||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
@ -119,7 +118,7 @@ func (s *MainTestSuite) TestConfigFile(c *check.C) {
|
|||
c.Assert(log.GetLevel(), check.Equals, log.DebugLevel)
|
||||
c.Assert(conf.Hostname, check.Equals, "hvostongo.example.org")
|
||||
c.Assert(conf.Token, check.Equals, "xxxyyy")
|
||||
c.Assert(conf.AdvertiseIP, check.DeepEquals, net.ParseIP("10.5.5.5"))
|
||||
c.Assert(conf.AdvertiseIP, check.DeepEquals, "10.5.5.5")
|
||||
c.Assert(conf.SSH.Labels, check.DeepEquals, map[string]string{"a": "a1", "b": "b1"})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue