Skip to content

Commit

Permalink
Add --bind-addr, fixes #2620
Browse files Browse the repository at this point in the history
This commit adds `--bind-addr` flag to tsh login
and TELEPORT_LOGIN_BIND_ADDR environment variable
to set up login bind address for SSO redirect flows.

Usage examples:

```
tsh login  --bind-addr=localhost:3333
tsh login --bind-addr=:3333
tsh login --bind-addr=[::1]:3333
TELEPORT_LOGIN_BIND_ADDR=localhost:7777 tsh login
```
  • Loading branch information
klizhentas committed Apr 10, 2019
1 parent 74003a3 commit b8acc79
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 24 deletions.
25 changes: 15 additions & 10 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ type Config struct {
// CheckVersions will check that client version is compatible
// with auth server version when connecting.
CheckVersions bool

// BindAddr is an optional host:port to bind to for SSO redirect flows
BindAddr string
}

// CachePolicy defines cache policy for local clients
Expand Down Expand Up @@ -1821,16 +1824,18 @@ func (tc *TeleportClient) directLogin(ctx context.Context, secondFactorType stri
func (tc *TeleportClient) ssoLogin(ctx context.Context, connectorID string, pub []byte, protocol string) (*auth.SSHLoginResponse, error) {
log.Debugf("samlLogin start")
// ask the CA (via proxy) to sign our public key:
response, err := SSHAgentSSOLogin(
ctx,
tc.Config.WebProxyAddr,
connectorID,
pub,
tc.KeyTTL,
tc.InsecureSkipVerify,
loopbackPool(tc.Config.WebProxyAddr),
protocol,
tc.CertificateFormat)
response, err := SSHAgentSSOLogin(SSHLogin{
Context: ctx,
ProxyAddr: tc.Config.WebProxyAddr,
ConnectorID: connectorID,
PubKey: pub,
TTL: tc.KeyTTL,
Insecure: tc.InsecureSkipVerify,
Pool: loopbackPool(tc.Config.WebProxyAddr),
Protocol: protocol,
Compatibility: tc.CertificateFormat,
BindAddr: tc.BindAddr,
})
return response, trace.Wrap(err)
}

Expand Down
63 changes: 52 additions & 11 deletions lib/client/weblogin.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,34 @@ type sealData struct {
Nonce []byte `json:"nonce"`
}

// SSHLogin contains SSH login parameters
type SSHLogin struct {
// Context is an external context
Context context.Context
// ProxyAddr is the target proxy address
ProxyAddr string
// ConnectorID is the OIDC or SAML connector ID to use
ConnectorID string
// PubKey is SSH public key to sign
PubKey []byte
// TTL is requested TTL of the client certificates
TTL time.Duration
// Insecure turns off verification for x509 target proxy
Insecure bool
// Pool is x509 cert pool to use for server certifcate verification
Pool *x509.CertPool
// Protocol is an optional protocol selection
Protocol string
// Compatibility sets compatibility mode for SSH certificates
Compatibility string
// BindAddr is an optional host:port address to bind
// to for SSO login flows
BindAddr string
}

// SSHAgentSSOLogin is used by SSH Agent (tsh) to login using OpenID connect
func SSHAgentSSOLogin(ctx context.Context, proxyAddr, connectorID string, pubKey []byte, ttl time.Duration, insecure bool, pool *x509.CertPool, protocol string, compatibility string) (*auth.SSHLoginResponse, error) {
clt, proxyURL, err := initClient(proxyAddr, insecure, pool)
func SSHAgentSSOLogin(login SSHLogin) (*auth.SSHLoginResponse, error) {
clt, proxyURL, err := initClient(login.ProxyAddr, login.Insecure, login.Pool)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down Expand Up @@ -172,7 +197,7 @@ func SSHAgentSSOLogin(ctx context.Context, proxyAddr, connectorID string, pubKey
})
}

server := httptest.NewServer(makeHandler(func(w http.ResponseWriter, r *http.Request) (*auth.SSHLoginResponse, error) {
handler := makeHandler(func(w http.ResponseWriter, r *http.Request) (*auth.SSHLoginResponse, error) {
if r.URL.Path != "/callback" {
return nil, trace.NotFound("path not found")
}
Expand All @@ -198,7 +223,23 @@ func SSHAgentSSOLogin(ctx context.Context, proxyAddr, connectorID string, pubKey
return nil, trace.BadParameter("failed to decode response: in %v, err: %v", r.URL.String(), err)
}
return re, nil
}))
})

var server *httptest.Server
if login.BindAddr != "" {
log.Debugf("Binding to %v.", login.BindAddr)
listener, err := net.Listen("tcp", login.BindAddr)
if err != nil {
return nil, trace.Wrap(err, "%v: could not bind to %v, make sure the address is host:port format for ipv4 and [ipv6]:port format for ipv6, and the address is not in use", err, login.BindAddr)
}
server = &httptest.Server{
Listener: listener,
Config: &http.Server{Handler: handler},
}
server.Start()
} else {
server = httptest.NewServer(handler)
}
defer server.Close()

u, err := url.Parse(server.URL + "/callback")
Expand All @@ -209,12 +250,12 @@ func SSHAgentSSOLogin(ctx context.Context, proxyAddr, connectorID string, pubKey
query.Set("secret", secret.KeyToEncodedString(keyBytes))
u.RawQuery = query.Encode()

out, err := clt.PostJSON(ctx, clt.Endpoint("webapi", protocol, "login", "console"), SSOLoginConsoleReq{
out, err := clt.PostJSON(login.Context, clt.Endpoint("webapi", login.Protocol, "login", "console"), SSOLoginConsoleReq{
RedirectURL: u.String(),
PublicKey: pubKey,
CertTTL: ttl,
ConnectorID: connectorID,
Compatibility: compatibility,
PublicKey: login.PubKey,
CertTTL: login.TTL,
ConnectorID: login.ConnectorID,
Compatibility: login.Compatibility,
})
if err != nil {
return nil, trace.Wrap(err)
Expand Down Expand Up @@ -280,9 +321,9 @@ func SSHAgentSSOLogin(ctx context.Context, proxyAddr, connectorID string, pubKey
case <-time.After(defaults.CallbackTimeout):
log.Debugf("Timed out waiting for callback after %v.", defaults.CallbackTimeout)
return nil, trace.Wrap(trace.Errorf("timed out waiting for callback"))
case <-ctx.Done():
case <-login.Context.Done():
log.Debugf("Canceled by user.")
return nil, trace.Wrap(ctx.Err())
return nil, trace.Wrap(login.Context.Err())
}
}

Expand Down
12 changes: 9 additions & 3 deletions tool/tsh/tsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ type CLIConf struct {
// format to use with --out to store a fershly retreived certificate
IdentityFormat client.IdentityFileFormat

// BindAddr is an address in the form of host:port to bind to
// during `tsh login` command
BindAddr string

// AuthConnector is the name of the connector to use.
AuthConnector string

Expand Down Expand Up @@ -164,8 +168,9 @@ func main() {
}

const (
clusterEnvVar = "TELEPORT_SITE"
clusterHelp = "Specify the cluster to connect"
clusterEnvVar = "TELEPORT_SITE"
clusterHelp = "Specify the cluster to connect"
bindAddrEnvVar = "TELEPORT_LOGIN_BIND_ADDR"
)

// Run executes TSH client. same as main() but easier to test
Expand Down Expand Up @@ -235,6 +240,7 @@ func Run(args []string, underTest bool) {
// login logs in with remote proxy and obtains a "session certificate" which gets
// stored in ~/.tsh directory
login := app.Command("login", "Log in to a cluster and retrieve the session certificate")
login.Flag("bind-addr", "Address in the form of host:port to bind to for login command webhook").Envar(bindAddrEnvVar).StringVar(&cf.BindAddr)
login.Flag("out", "Identity output").Short('o').StringVar(&cf.IdentityFileOut)
login.Flag("format", fmt.Sprintf("Identity format [%s] or %s (for OpenSSH compatibility)",
client.DefaultIdentityFormat,
Expand Down Expand Up @@ -912,7 +918,7 @@ func makeClient(cf *CLIConf, useProfileLogin bool) (tc *client.TeleportClient, e
if options.StrictHostKeyChecking == false {
c.HostKeyCallback = client.InsecureSkipHostKeyChecking
}

c.BindAddr = cf.BindAddr
return client.NewClient(c)
}

Expand Down

0 comments on commit b8acc79

Please sign in to comment.