Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v15] Machine ID: Allow the proxy address to be explicitly configured #39055

Merged
merged 8 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions lib/tbot/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ type CLIConf struct {
// AuthServer is a Teleport auth server address. It may either point
// directly to an auth server, or to a Teleport proxy server in which case
// a tunneled auth connection will be established.
// Prefer using Address() to pick an address.
AuthServer string

// DataDir stores the bot's internal data.
Expand Down Expand Up @@ -154,9 +155,10 @@ type CLIConf struct {
// should be written to
ConfigureOutput string

// Proxy is the teleport proxy address. Unlike `AuthServer` this must
// ProxyServer is the teleport proxy address. Unlike `AuthServer` this must
// explicitly point to a Teleport proxy.
Proxy string
// Example: "example.teleport.sh:443"
ProxyServer string

// Cluster is the name of the Teleport cluster on which resources should
// be accessed.
Expand Down Expand Up @@ -267,8 +269,12 @@ type BotConfig struct {
Outputs Outputs `yaml:"outputs,omitempty"`
Services ServiceConfigs `yaml:"services,omitempty"`

Debug bool `yaml:"debug"`
AuthServer string `yaml:"auth_server"`
Debug bool `yaml:"debug"`
AuthServer string `yaml:"auth_server,omitempty"`
// ProxyServer is the teleport proxy address. Unlike `AuthServer` this must
// explicitly point to a Teleport proxy.
// Example: "example.teleport.sh:443"
ProxyServer string `yaml:"proxy_server,omitempty"`
CertificateTTL time.Duration `yaml:"certificate_ttl"`
RenewalInterval time.Duration `yaml:"renewal_interval"`
Oneshot bool `yaml:"oneshot"`
Expand All @@ -292,6 +298,30 @@ type BotConfig struct {
Insecure bool `yaml:"insecure,omitempty"`
}

type AddressKind string

const (
AddressKindUnspecified AddressKind = ""
AddressKindProxy AddressKind = "proxy"
AddressKindAuth AddressKind = "auth"
)

// Address returns the address to the auth server, either directly or via
// a proxy, and the kind of address it is.
func (conf *BotConfig) Address() (string, AddressKind) {
switch {
case conf.AuthServer != "" && conf.ProxyServer != "":
// This is an error case that should be prevented by the validation.
return "", AddressKindUnspecified
case conf.ProxyServer != "":
return conf.ProxyServer, AddressKindProxy
case conf.AuthServer != "":
return conf.AuthServer, AddressKindAuth
default:
return "", AddressKindUnspecified
}
}

func (conf *BotConfig) CipherSuites() []uint16 {
if conf.FIPS {
return defaults.FIPSCipherSuites
Expand Down Expand Up @@ -641,6 +671,13 @@ func FromCLIConf(cf *CLIConf) (*BotConfig, error) {
config.AuthServer = cf.AuthServer
}

if cf.ProxyServer != "" {
if config.ProxyServer != "" {
log.Warnf("CLI parameters are overriding proxy configured in %s", cf.ConfigPath)
}
config.ProxyServer = cf.ProxyServer
}

if cf.CertificateTTL != 0 {
if config.CertificateTTL != 0 {
log.Warnf("CLI parameters are overriding certificate TTL configured in %s", cf.ConfigPath)
Expand Down
14 changes: 14 additions & 0 deletions lib/tbot/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,20 @@ func TestBotConfig_YAML(t *testing.T) {
},
},
},
{
name: "minimal config using proxy addr",
in: BotConfig{
Version: V2,
ProxyServer: "example.teleport.sh:443",
CertificateTTL: time.Minute,
RenewalInterval: time.Second * 30,
Outputs: Outputs{
&IdentityOutput{
Destination: &DestinationMemory{},
},
},
},
},
}

testYAML(t, tests)
Expand Down
6 changes: 3 additions & 3 deletions lib/tbot/config/template_ssh_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,14 @@ func (c *templateSSHClient) render(
)
defer span.End()

ping, err := bot.AuthPing(ctx)
ping, err := bot.ProxyPing(ctx)
if err != nil {
return trace.Wrap(err)
}

proxyHost, proxyPort, err := utils.SplitHostPort(ping.ProxyPublicAddr)
proxyHost, proxyPort, err := utils.SplitHostPort(ping.Proxy.SSH.PublicAddr)
if err != nil {
return trace.BadParameter("proxy %+v has no usable public address: %v", ping.ProxyPublicAddr, err)
return trace.BadParameter("proxy %+v has no usable public address: %v", ping.Proxy.SSH.PublicAddr, err)
}

clusterNames, err := getClusterNames(bot, ping.ClusterName)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: v2
outputs:
- type: identity
destination:
type: memory
debug: false
proxy_server: example.teleport.sh:443
certificate_ttl: 1m0s
renewal_interval: 30s
oneshot: false
fips: false
23 changes: 18 additions & 5 deletions lib/tbot/service_bot_identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,10 +382,6 @@ func botIdentityFromToken(ctx context.Context, log logrus.FieldLogger, cfg *conf
defer span.End()

log.Info("Fetching bot identity using token.")
addr, err := utils.ParseAddr(cfg.AuthServer)
if err != nil {
return nil, trace.Wrap(err, "invalid auth server address %+v", cfg.AuthServer)
}

tlsPrivateKey, sshPublicKey, tlsPublicKey, err := generateKeys()
if err != nil {
Expand All @@ -403,7 +399,6 @@ func botIdentityFromToken(ctx context.Context, log logrus.FieldLogger, cfg *conf
ID: auth.IdentityID{
Role: types.RoleBot,
},
AuthServers: []utils.NetAddr{*addr},
PublicTLSKey: tlsPublicKey,
PublicSSHKey: sshPublicKey,
CAPins: cfg.Onboarding.CAPins,
Expand All @@ -416,6 +411,24 @@ func botIdentityFromToken(ctx context.Context, log logrus.FieldLogger, cfg *conf
Insecure: cfg.Insecure,
}

addr, addrKind := cfg.Address()
switch addrKind {
case config.AddressKindAuth:
parsed, err := utils.ParseAddr(addr)
if err != nil {
return nil, trace.Wrap(err, "failed to parse addr")
}
params.AuthServers = []utils.NetAddr{*parsed}
case config.AddressKindProxy:
parsed, err := utils.ParseAddr(addr)
if err != nil {
return nil, trace.Wrap(err, "failed to parse addr")
}
params.ProxyServer = *parsed
default:
return nil, trace.BadParameter("unsupported address kind: %v", addrKind)
}

if params.JoinMethod == types.JoinMethodAzure {
params.AzureParams = auth.AzureParams{
ClientID: cfg.Onboarding.Azure.ClientID,
Expand Down
21 changes: 15 additions & 6 deletions lib/tbot/service_outputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -724,18 +724,27 @@ func (orc *outputRenewalCache) proxyPing(ctx context.Context) (*webclient.PingRe
return orc._proxyPong, nil
}

// Note: this relies on the auth server's proxy address. We could
// potentially support some manual parameter here in the future if desired.
authPong, err := orc.authPing(ctx)
if err != nil {
return nil, trace.Wrap(err)
// Determine the Proxy address to use.
addr, addrKind := orc.cfg.Address()
switch addrKind {
case config.AddressKindAuth:
// If the address is an auth address, ping auth to determine proxy addr.
authPong, err := orc.authPing(ctx)
if err != nil {
return nil, trace.Wrap(err)
}
addr = authPong.ProxyPublicAddr
case config.AddressKindProxy:
// If the address is a proxy address, use it directly.
default:
return nil, trace.BadParameter("unsupported address kind: %v", addrKind)
}

// We use find instead of Ping as it's less resource intense and we can
// ping the AuthServer directly for its configuration if necessary.
proxyPong, err := webclient.Find(&webclient.Config{
Context: ctx,
ProxyAddr: authPong.ProxyPublicAddr,
ProxyAddr: addr,
Insecure: orc.cfg.Insecure,
})
if err != nil {
Expand Down
23 changes: 16 additions & 7 deletions lib/tbot/tbot.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,12 @@ func (b *Bot) Run(ctx context.Context) error {
return trace.Wrap(err)
}

addr, _ := b.cfg.Address()
resolver, err := reversetunnelclient.CachingResolver(
ctx,
reversetunnelclient.WebClientResolver(&webclient.Config{
Context: ctx,
ProxyAddr: b.cfg.AuthServer,
ProxyAddr: addr,
Insecure: b.cfg.Insecure,
}),
nil /* clock */)
Expand Down Expand Up @@ -300,11 +301,16 @@ func (b *Bot) preRunChecks(ctx context.Context) (func() error, error) {
ctx, span := tracer.Start(ctx, "Bot/preRunChecks")
defer span.End()

if b.cfg.AuthServer == "" {
switch _, addrKind := b.cfg.Address(); addrKind {
case config.AddressKindUnspecified:
return nil, trace.BadParameter(
"an auth or proxy server must be set via --auth-server or configuration",
"either a proxy or auth address must be set using --proxy, --auth-server or configuration",
)
case config.AddressKindAuth:
// TODO(noah): DELETE IN V17.0.0
b.log.Warn("We recently introduced the ability to explicitly configure the address of the Teleport Proxy using --proxy-server. We recommend switching to this if you currently provide the address of the Proxy to --auth-server.")
}

// Ensure they have provided a join method.
if b.cfg.Onboarding.JoinMethod == types.JoinMethodUnspecified {
return nil, trace.BadParameter("join method must be provided")
Expand Down Expand Up @@ -427,15 +433,18 @@ func clientForFacade(
return nil, trace.Wrap(err)
}

authAddr, err := utils.ParseAddr(cfg.AuthServer)
addr, _ := cfg.Address()
parsedAddr, err := utils.ParseAddr(addr)
if err != nil {
return nil, trace.Wrap(err)
}

authClientConfig := &authclient.Config{
TLS: tlsConfig,
SSH: sshConfig,
AuthServers: []utils.NetAddr{*authAddr},
TLS: tlsConfig,
SSH: sshConfig,
// TODO(noah): It'd be ideal to distinguish the proxy addr and auth addr
// here to avoid pointlessly hitting the address as an auth server.
AuthServers: []utils.NetAddr{*parsedAddr},
Log: log,
Insecure: cfg.Insecure,
Resolver: resolver,
Expand Down
2 changes: 1 addition & 1 deletion tool/tbot/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func onDBCommand(botConfig *config.BotConfig, cf *config.CLIConf) error {
return trace.Wrap(err)
}

args := []string{"-i", identityPath, "db", "--proxy=" + cf.Proxy}
args := []string{"-i", identityPath, "db", "--proxy=" + cf.ProxyServer}
if cf.Cluster != "" {
// If we caught --cluster in our args, pass it through.
args = append(args, "--cluster="+cf.Cluster)
Expand Down
30 changes: 24 additions & 6 deletions tool/tbot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ var log = logrus.WithFields(logrus.Fields{
})

const (
authServerEnvVar = "TELEPORT_AUTH_SERVER"
tokenEnvVar = "TELEPORT_BOT_TOKEN"
authServerEnvVar = "TELEPORT_AUTH_SERVER"
tokenEnvVar = "TELEPORT_BOT_TOKEN"
proxyServerEnvVar = "TELEPORT_PROXY"
)

func main() {
Expand Down Expand Up @@ -83,7 +84,8 @@ func Run(args []string, stdout io.Writer) error {
versionCmd := app.Command("version", "Print the version of your tbot binary.")

startCmd := app.Command("start", "Starts the renewal bot, writing certificates to the data dir at a set interval.")
startCmd.Flag("auth-server", "Address of the Teleport Auth Server or Proxy Server.").Short('a').Envar(authServerEnvVar).StringVar(&cf.AuthServer)
startCmd.Flag("auth-server", "Address of the Teleport Auth Server. Prefer using --proxy-server where possible.").Short('a').Envar(authServerEnvVar).StringVar(&cf.AuthServer)
startCmd.Flag("proxy-server", "Address of the Teleport Proxy Server.").Envar(proxyServerEnvVar).StringVar(&cf.ProxyServer)
startCmd.Flag("token", "A bot join token or path to file with token value, if attempting to onboard a new bot; used on first connect.").Envar(tokenEnvVar).StringVar(&cf.Token)
startCmd.Flag("ca-pin", "CA pin to validate the Teleport Auth Server; used on first connect.").StringsVar(&cf.CAPins)
startCmd.Flag("data-dir", "Directory to store internal bot data. Access to this directory should be limited.").StringVar(&cf.DataDir)
Expand All @@ -110,7 +112,8 @@ func Run(args []string, stdout io.Writer) error {
EnumVar(&cf.LogFormat, utils.LogFormatJSON, utils.LogFormatText)

configureCmd := app.Command("configure", "Creates a config file based on flags provided, and writes it to stdout or a file (-c <path>).")
configureCmd.Flag("auth-server", "Address of the Teleport Auth Server (On-Prem installs) or Proxy Server (Cloud installs).").Short('a').Envar(authServerEnvVar).StringVar(&cf.AuthServer)
configureCmd.Flag("auth-server", "Address of the Teleport Auth Server. Prefer using --proxy-server where possible.").Short('a').Envar(authServerEnvVar).StringVar(&cf.AuthServer)
configureCmd.Flag("proxy-server", "Address of the Teleport Proxy Server.").Envar(proxyServerEnvVar).StringVar(&cf.ProxyServer)
configureCmd.Flag("ca-pin", "CA pin to validate the Teleport Auth Server; used on first connect.").StringsVar(&cf.CAPins)
configureCmd.Flag("certificate-ttl", "TTL of short-lived machine certificates.").Default("60m").DurationVar(&cf.CertificateTTL)
configureCmd.Flag("data-dir", "Directory to store internal bot data. Access to this directory should be limited.").StringVar(&cf.DataDir)
Expand All @@ -127,8 +130,14 @@ func Run(args []string, stdout io.Writer) error {
migrateCmd := app.Command("migrate", "Migrates a config file from an older version to the newest version. Outputs to stdout by default.")
migrateCmd.Flag("output", "Path to write the generated configuration file to rather than write to stdout.").Short('o').StringVar(&cf.ConfigureOutput)

legacyProxyFlag := ""

dbCmd := app.Command("db", "Execute database commands through tsh.")
dbCmd.Flag("proxy", "The Teleport proxy server to use, in host:port form.").Required().StringVar(&cf.Proxy)
dbCmd.Flag("proxy-server", "The Teleport proxy server to use, in host:port form.").StringVar(&cf.ProxyServer)
// We're migrating from --proxy to --proxy-server so this flag is hidden
// but still supported.
// TODO(strideynet): DELETE IN 17.0.0
dbCmd.Flag("proxy", "The Teleport proxy server to use, in host:port form.").Hidden().Envar(proxyServerEnvVar).StringVar(&legacyProxyFlag)
dbCmd.Flag("destination-dir", "The destination directory with which to authenticate tsh").StringVar(&cf.DestinationDir)
dbCmd.Flag("cluster", "The cluster name. Extracted from the certificate if unset.").StringVar(&cf.Cluster)
dbRemaining := config.RemainingArgs(dbCmd.Arg(
Expand All @@ -137,7 +146,11 @@ func Run(args []string, stdout io.Writer) error {
))

proxyCmd := app.Command("proxy", "Start a local TLS proxy via tsh to connect to Teleport in single-port mode.")
proxyCmd.Flag("proxy", "The Teleport proxy server to use, in host:port form.").Required().StringVar(&cf.Proxy)
proxyCmd.Flag("proxy-server", "The Teleport proxy server to use, in host:port form.").Envar(proxyServerEnvVar).StringVar(&cf.ProxyServer)
// We're migrating from --proxy to --proxy-server so this flag is hidden
// but still supported.
// TODO(strideynet): DELETE IN 17.0.0
proxyCmd.Flag("proxy", "The Teleport proxy server to use, in host:port form.").Hidden().StringVar(&legacyProxyFlag)
proxyCmd.Flag("destination-dir", "The destination directory with which to authenticate tsh").StringVar(&cf.DestinationDir)
proxyCmd.Flag("cluster", "The cluster name. Extracted from the certificate if unset.").StringVar(&cf.Cluster)
proxyRemaining := config.RemainingArgs(proxyCmd.Arg(
Expand All @@ -156,6 +169,11 @@ func Run(args []string, stdout io.Writer) error {
return trace.Wrap(err)
}

if legacyProxyFlag != "" {
cf.ProxyServer = legacyProxyFlag
log.Warn("The --proxy flag is deprecated and will be removed in v17.0.0. Use --proxy-server instead.")
}

// Remaining args are stored directly to a []string rather than written to
// a shared ref like most other kingpin args, so we'll need to manually
// move them to the remaining args field.
Expand Down
6 changes: 6 additions & 0 deletions tool/tbot/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ func TestRun_Configure(t *testing.T) {
"--fips",
}...),
},
{
name: "all parameters provided",
args: append(baseArgs, []string{
"--proxy-server", "proxy.example.com:443",
}...),
},
}

for _, tt := range tests {
Expand Down
2 changes: 1 addition & 1 deletion tool/tbot/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func onProxyCommand(botConfig *config.BotConfig, cf *config.CLIConf) error {

// TODO(timothyb89): We could consider supporting a --cluster passthrough
// here as in `tbot db ...`.
args := []string{"-i", identityPath, "proxy", "--proxy=" + cf.Proxy}
args := []string{"-i", identityPath, "proxy", "--proxy=" + cf.ProxyServer}
args = append(args, cf.RemainingArgs...)

// Pass through the debug flag, and prepend to satisfy argument ordering
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# tbot config file generated by `configure` command
version: v2
onboarding:
join_method: token
storage:
type: directory
path: /var/lib/teleport/bot
symlinks: secure
acls: try
debug: false
proxy_server: proxy.example.com:443
certificate_ttl: 1h0m0s
renewal_interval: 20m0s
oneshot: false
fips: false
Loading
Loading