mirror of
https://github.com/gravitational/teleport
synced 2024-10-20 17:23:22 +00:00
Added support for auth-server and tokens
This commit is contained in:
parent
058f6eb9cf
commit
d6d5cb7d9c
5
Makefile
5
Makefile
|
@ -13,6 +13,11 @@ export GO15VENDOREXPERIMENT=1
|
|||
teleport:
|
||||
go build -o teleport -i github.com/gravitational/teleport/tool/teleport
|
||||
|
||||
.PHONY: t
|
||||
t:
|
||||
go test github.com/gravitational/teleport/lib/utils/...
|
||||
|
||||
|
||||
all:
|
||||
go build -o teleport -i github.com/gravitational/teleport/tool/teleport
|
||||
go build -o tctl -i github.com/gravitational/teleport/tool/tctl
|
||||
|
|
|
@ -65,9 +65,16 @@ const (
|
|||
LimiterMaxConcurrentUsers = 25
|
||||
)
|
||||
|
||||
// list of roles teleport service can run as:
|
||||
const (
|
||||
RoleNode = "node"
|
||||
RoleProxy = "proxy"
|
||||
RoleAuthService = "auth"
|
||||
)
|
||||
|
||||
var (
|
||||
// Default roles teleport assumes when started via 'start' command
|
||||
StartRoles = []string{"node", "proxy", "auth"}
|
||||
StartRoles = []string{RoleProxy, RoleNode, RoleAuthService}
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -82,33 +89,38 @@ func ConfigureLimiter(lc *limiter.LimiterConfig) {
|
|||
|
||||
// AuthListenAddr returns the default listening address for the Auth service
|
||||
func AuthListenAddr() *utils.NetAddr {
|
||||
return makeDefaultAddr(AuthListenPort)
|
||||
return makeAddr(BindIP, AuthListenPort)
|
||||
}
|
||||
|
||||
// AuthConnectAddr returns the default address to search for auth. service on
|
||||
func AuthConnectAddr() *utils.NetAddr {
|
||||
return makeAddr("127.0.0.1", AuthListenPort)
|
||||
}
|
||||
|
||||
// ProxyListenAddr returns the default listening address for the SSH Proxy service
|
||||
func ProxyListenAddr() *utils.NetAddr {
|
||||
return makeDefaultAddr(SSHProxyListenPort)
|
||||
return makeAddr(BindIP, SSHProxyListenPort)
|
||||
}
|
||||
|
||||
// ProxyWebListenAddr returns the default listening address for the Web-based SSH Proxy service
|
||||
func ProxyWebListenAddr() *utils.NetAddr {
|
||||
return makeDefaultAddr(HTTPListenPort)
|
||||
return makeAddr(BindIP, HTTPListenPort)
|
||||
}
|
||||
|
||||
// SSHServerListenAddr returns the default listening address for the Web-based SSH Proxy service
|
||||
func SSHServerListenAddr() *utils.NetAddr {
|
||||
return makeDefaultAddr(SSHServerListenPort)
|
||||
return makeAddr(BindIP, SSHServerListenPort)
|
||||
}
|
||||
|
||||
// ReverseTunnellAddr returns the default listening address for the SSH Proxy service used
|
||||
// by the SSH nodes to establish proxy<->ssh_node connection from behind a firewall which
|
||||
// blocks inbound connecions to ssh_nodes
|
||||
func ReverseTunnellAddr() *utils.NetAddr {
|
||||
return makeDefaultAddr(SSHProxyTunnelListenPort)
|
||||
return makeAddr(BindIP, SSHProxyTunnelListenPort)
|
||||
}
|
||||
|
||||
func makeDefaultAddr(port int16) *utils.NetAddr {
|
||||
addrSpec := fmt.Sprintf("tcp://%v:%d", BindIP, port)
|
||||
func makeAddr(host string, port int16) *utils.NetAddr {
|
||||
addrSpec := fmt.Sprintf("tcp://%s:%d", host, port)
|
||||
retval, err := utils.ParseAddr(addrSpec)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("%s: error parsing '%v'", initError, addrSpec))
|
||||
|
|
|
@ -18,6 +18,7 @@ package service
|
|||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"gopkg.in/yaml.v2"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
|
@ -78,6 +79,16 @@ func (cfg *Config) RoleConfig() RoleConfig {
|
|||
}
|
||||
}
|
||||
|
||||
// DebugDumpToYAML() is useful for debugging: it dumps the Config structure into
|
||||
// a string
|
||||
func (cfg *Config) DebugDumpToYAML() string {
|
||||
out, err := yaml.Marshal(cfg)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
type ProxyConfig struct {
|
||||
// Enabled turns proxy role on or off for this process
|
||||
Enabled bool `yaml:"enabled" env:"TELEPORT_PROXY_ENABLED"`
|
||||
|
|
|
@ -17,7 +17,9 @@ package utils
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -71,6 +73,8 @@ func (a *NetAddr) Set(s string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ParseAddr takes strings like "tcp://host:port/path" and returns
|
||||
// *NetAddr or an error
|
||||
func ParseAddr(a string) (*NetAddr, error) {
|
||||
u, err := url.Parse(a)
|
||||
if err != nil {
|
||||
|
@ -86,6 +90,24 @@ func ParseAddr(a string) (*NetAddr, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// ParseHostPortAddr takes strings like "host:port" and returns
|
||||
// *NetAddr or an error
|
||||
//
|
||||
// If defaultPort == -1 it expects 'hostport' string to have it
|
||||
func ParseHostPortAddr(hostport string, defaultPort int) (*NetAddr, error) {
|
||||
host, port, err := net.SplitHostPort(hostport)
|
||||
if err != nil {
|
||||
if defaultPort > 0 {
|
||||
host, port, err = net.SplitHostPort(
|
||||
net.JoinHostPort(hostport, strconv.Itoa(defaultPort)))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse '%v': %v", hostport, err)
|
||||
}
|
||||
}
|
||||
return ParseAddr(fmt.Sprintf("tcp://%s", net.JoinHostPort(host, port)))
|
||||
}
|
||||
|
||||
func NewNetAddrVal(defaultVal NetAddr, val *NetAddr) *NetAddrVal {
|
||||
*val = defaultVal
|
||||
return (*NetAddrVal)(val)
|
||||
|
|
62
lib/utils/addr_test.go
Normal file
62
lib/utils/addr_test.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
Copyright 2015 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 utils
|
||||
|
||||
import (
|
||||
. "gopkg.in/check.v1"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddrSturct(t *testing.T) { TestingT(t) }
|
||||
|
||||
type AddrTestSuite struct {
|
||||
}
|
||||
|
||||
var _ = Suite(&AddrTestSuite{})
|
||||
|
||||
func (s *AddrTestSuite) TestParseHostPort(c *C) {
|
||||
// success
|
||||
addr, err := ParseHostPortAddr("localhost:22", -1)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(addr.AddrNetwork, Equals, "tcp")
|
||||
c.Assert(addr.Addr, Equals, "localhost:22")
|
||||
|
||||
// success
|
||||
addr, err = ParseHostPortAddr("localhost", 1111)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(addr.AddrNetwork, Equals, "tcp")
|
||||
c.Assert(addr.Addr, Equals, "localhost:1111")
|
||||
|
||||
// missing port
|
||||
addr, err = ParseHostPortAddr("localhost", -1)
|
||||
c.Assert(err, NotNil)
|
||||
c.Assert(addr, IsNil)
|
||||
}
|
||||
|
||||
func (s *AddrTestSuite) TestEmpty(c *C) {
|
||||
var a NetAddr
|
||||
c.Assert(a.IsEmpty(), Equals, true)
|
||||
}
|
||||
|
||||
func (s *AddrTestSuite) TestParse(c *C) {
|
||||
addr, err := ParseAddr("tcp://one:25/path")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(addr, NotNil)
|
||||
c.Assert(addr.Addr, Equals, "one:25")
|
||||
c.Assert(addr.Path, Equals, "/path")
|
||||
c.Assert(addr.FullAddress(), Equals, "tcp://one:25")
|
||||
c.Assert(addr.IsEmpty(), Equals, false)
|
||||
}
|
|
@ -26,15 +26,17 @@ import (
|
|||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CLIConfig represents command line flags+args
|
||||
type CLIConfig struct {
|
||||
ProxyAddr string
|
||||
ListenIP net.IP
|
||||
ConfigFile string
|
||||
Roles []string
|
||||
Debug bool
|
||||
AuthServerAddr string
|
||||
AuthToken string
|
||||
ListenIP net.IP
|
||||
ConfigFile string
|
||||
Roles string
|
||||
Debug bool
|
||||
}
|
||||
|
||||
// confnigure merges command line arguments with what's in a configuration file
|
||||
|
@ -52,6 +54,9 @@ func configure(ccf *CLIConfig) (cfg service.Config, err error) {
|
|||
}
|
||||
// parse the config file. these values will override defaults:
|
||||
utils.ConsoleMessage(os.Stdout, "Using config file: %s", configPath)
|
||||
|
||||
// TODO: replace with simplified config file
|
||||
log.Warning("Need to implement simplified config file")
|
||||
if err := service.ParseYAMLFile(configPath, &cfg); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
@ -65,9 +70,43 @@ func configure(ccf *CLIConfig) (cfg service.Config, err error) {
|
|||
utils.InitLoggerDebug()
|
||||
}
|
||||
|
||||
// apply --auth-server flag:
|
||||
if ccf.AuthServerAddr != "" {
|
||||
addr, err := utils.ParseHostPortAddr(ccf.AuthServerAddr, int(defaults.AuthListenPort))
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
log.Infof("Using auth server: %v", addr.FullAddress())
|
||||
cfg.AuthServers = append(cfg.AuthServers, *addr)
|
||||
}
|
||||
|
||||
// apply --token flag:
|
||||
if ccf.AuthToken != "" {
|
||||
log.Infof("Using auth token: %s", ccf.AuthToken)
|
||||
cfg.SSH.Token = ccf.AuthToken
|
||||
cfg.Proxy.Token = ccf.AuthToken
|
||||
}
|
||||
|
||||
// apply --roles flag:
|
||||
if ccf.Roles != "" {
|
||||
if err := validateRoles(ccf.Roles); err != nil {
|
||||
log.Error(err.Error())
|
||||
return cfg, err
|
||||
}
|
||||
if strings.Index(ccf.Roles, defaults.RoleNode) == -1 {
|
||||
cfg.SSH.Enabled = false
|
||||
}
|
||||
if strings.Index(ccf.Roles, defaults.RoleAuthService) == -1 {
|
||||
cfg.Auth.Enabled = false
|
||||
}
|
||||
if strings.Index(ccf.Roles, defaults.RoleProxy) == -1 {
|
||||
cfg.Proxy.Enabled = false
|
||||
cfg.ReverseTunnel.Enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
// apply --listen-ip flag:
|
||||
if ccf.ListenIP != nil {
|
||||
log.Infof("applying listen-ip flag: '%v'", ccf.ListenIP)
|
||||
if err = applyListenIP(ccf.ListenIP, &cfg); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
@ -126,8 +165,10 @@ func applyDefaults(cfg *service.Config) error {
|
|||
cfg.Proxy.AssetsDir = defaults.DataDir
|
||||
cfg.Proxy.SSHAddr = *defaults.ProxyListenAddr()
|
||||
cfg.Proxy.WebAddr = *defaults.ProxyWebListenAddr()
|
||||
cfg.ReverseTunnel.Enabled = true
|
||||
cfg.Proxy.ReverseTunnelListenAddr = *defaults.ReverseTunnellAddr()
|
||||
defaults.ConfigureLimiter(&cfg.Proxy.Limiter)
|
||||
defaults.ConfigureLimiter(&cfg.ReverseTunnel.Limiter)
|
||||
|
||||
// defaults for the SSH service:
|
||||
cfg.SSH.Enabled = true
|
||||
|
@ -137,7 +178,9 @@ func applyDefaults(cfg *service.Config) error {
|
|||
// global defaults
|
||||
cfg.Hostname = hostname
|
||||
cfg.DataDir = defaults.DataDir
|
||||
cfg.AuthServers = []utils.NetAddr{cfg.Auth.SSHAddr}
|
||||
if cfg.Auth.Enabled {
|
||||
cfg.AuthServers = []utils.NetAddr{cfg.Auth.SSHAddr}
|
||||
}
|
||||
cfg.Console = os.Stdout
|
||||
return nil
|
||||
}
|
||||
|
@ -155,3 +198,18 @@ func fileExists(fp string) bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// validateRoles makes sure that value upassed to --roles flag is valid
|
||||
func validateRoles(roles string) error {
|
||||
for _, role := range strings.Split(roles, ",") {
|
||||
switch role {
|
||||
case defaults.RoleAuthService,
|
||||
defaults.RoleNode,
|
||||
defaults.RoleProxy:
|
||||
break
|
||||
default:
|
||||
return fmt.Errorf("unknown role: '%s'", role)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -53,17 +53,17 @@ func main() {
|
|||
start.Flag("roles",
|
||||
fmt.Sprintf("Comma-separated list of roles to start with [%s]", strings.Join(defaults.StartRoles, ","))).
|
||||
Short('r').
|
||||
StringsVar(&ccf.Roles)
|
||||
StringVar(&ccf.Roles)
|
||||
start.Flag("listen-ip",
|
||||
fmt.Sprintf("IP address to bind to [%s]", defaults.BindIP)).
|
||||
Short('l').
|
||||
IPVar(&ccf.ListenIP)
|
||||
start.Flag("proxy",
|
||||
"Address and port of the proxy server [none]").
|
||||
StringVar(&ccf.ProxyAddr)
|
||||
start.Flag("proxy-token",
|
||||
"One-time join token to connect to a proxy [none]").
|
||||
StringVar(&ccf.ProxyAddr)
|
||||
start.Flag("auth-server",
|
||||
fmt.Sprintf("Address of the auth server [%s]", defaults.AuthConnectAddr().Addr)).
|
||||
StringVar(&ccf.AuthServerAddr)
|
||||
start.Flag("token",
|
||||
"One-time token to register with an auth server [none]").
|
||||
StringVar(&ccf.AuthToken)
|
||||
start.Flag("config",
|
||||
fmt.Sprintf("Path to a configuration file [%v]", defaults.ConfigFilePath)).
|
||||
Short('c').
|
||||
|
|
|
@ -4,20 +4,16 @@ const (
|
|||
usageNotes = `
|
||||
Notes:
|
||||
|
||||
--no-ssh=false
|
||||
--roles=node,proxy,auth
|
||||
|
||||
When set, Teleport does not start the SSH service, only allowing proxied connections to
|
||||
other SSH nodes.
|
||||
Use this flag to tell Teleport which services to run. By default it runs all three, but
|
||||
in a production environment you may want to separate them all.
|
||||
|
||||
--proxy-addr=<host>[:port]/<token>
|
||||
--token=xyz
|
||||
|
||||
Tells teleport to run as an SSH node behind the given proxy host. You need to obtain the
|
||||
token by executing "tctl nodes add" on the host where Teleport proxy is running.
|
||||
|
||||
--advertise-ip=ip_address
|
||||
|
||||
When connecting to a proxy from behind a NAT, this tells the proxy which IP to find
|
||||
this node on.
|
||||
This token is needed to connect any node (web proxy or SSH service) to an auth server.
|
||||
Obtain it by running "tctl nodes add" on the auth server. It is only used once and ignored
|
||||
on subsequent restarts.
|
||||
`
|
||||
|
||||
usageExamples = `
|
||||
|
@ -25,20 +21,22 @@ Examples:
|
|||
|
||||
> teleport start
|
||||
|
||||
Without cofiguration, teleport starts by default in a "showcase mode": it becomes an
|
||||
SSH server and a proxy to itself with a Web UI.
|
||||
Without any cofiguration teleport starts in a "showroom mode": it's the equivalent of
|
||||
running with --roles=node,proxy,auth
|
||||
|
||||
> teleport start --no-ssh
|
||||
> teleport start --listen-ip=10.5.0.1 --roles=node --auth-server=10.5.0.2 --token=xyz
|
||||
|
||||
Starts teleport in a proxy+auth mode, serving the Web UI for 2-factor auth. You must
|
||||
execute 'tctl nodes add' now to generate one-time tokens to add nodes to the cluster.
|
||||
Starts a SSH node listening on 10.5.0.1 and authenticating incoming clients via the
|
||||
auth server running on 10.5.0.2.
|
||||
|
||||
> teleport start --proxy-addr=bastion.host:3023/token \
|
||||
--listen_interface=0.0.0.0 \
|
||||
--advertise_interface=10.0.1.50
|
||||
> teleport start --roles=proxy,auth
|
||||
|
||||
Starts teleport as an SSH node connected to the SSH proxy/bastion on bastion.host:3023
|
||||
Tells the proxy that this node is reachable via 10.0.1.50
|
||||
Starts Teleport auth server with a web proxy (which also serves Web UI).
|
||||
|
||||
> teleport start --roles=proxy --auth-server=10.5.0.2 --token=xyz
|
||||
|
||||
Starts Teleport Web proxy and configure it to authenticate/authorize against an auth
|
||||
server running on 10.5.0.2
|
||||
`
|
||||
|
||||
sampleConfig = `##
|
||||
|
|
Loading…
Reference in a new issue