Files
teleport/lib/config/fileconf.go
T
STeve (Xin) Huang 2643b81533 MCP access part 1: update app definition and config (#54706)
* MCP access part 1: update app definition and config

* address feedback

* make -C integrations/operator crd
2025-05-29 16:17:06 +00:00

2873 lines
106 KiB
Go

/*
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package config
import (
"bytes"
"context"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"io"
"io/fs"
"log/slog"
"net"
"net/url"
"os"
"slices"
"strings"
"time"
"github.com/gravitational/trace"
"golang.org/x/crypto/acme"
"golang.org/x/crypto/ssh"
"gopkg.in/yaml.v2"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/keys/hardwarekey"
"github.com/gravitational/teleport/api/utils/tlsutils"
"github.com/gravitational/teleport/lib/automaticupgrades"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/sshutils/x11"
"github.com/gravitational/teleport/lib/utils"
)
// FileConfig structure represents the teleport configuration stored in a config file
// in YAML format (usually /etc/teleport.yaml)
//
// Use config.ReadFromFile() to read the parsed FileConfig from a YAML file.
type FileConfig struct {
Version string `yaml:"version,omitempty"`
Global `yaml:"teleport,omitempty"`
Auth Auth `yaml:"auth_service,omitempty"`
SSH SSH `yaml:"ssh_service,omitempty"`
Proxy Proxy `yaml:"proxy_service,omitempty"`
Kube Kube `yaml:"kubernetes_service,omitempty"`
// Apps is the "app_service" section in Teleport file configuration which
// defines application access configuration.
Apps Apps `yaml:"app_service,omitempty"`
// Databases is the "db_service" section in Teleport configuration file
// that defines database access configuration.
Databases Databases `yaml:"db_service,omitempty"`
// Metrics is the "metrics_service" section in Teleport configuration file
// that defines the metrics service configuration
Metrics Metrics `yaml:"metrics_service,omitempty"`
// Debug is the "debug_service" section that defines the configuration for
// the Debug service.
Debug DebugService `yaml:"debug_service,omitempty"`
// WindowsDesktop is the "windows_desktop_service" that defines the
// configuration for Windows Desktop Access.
WindowsDesktop WindowsDesktopService `yaml:"windows_desktop_service,omitempty"`
// Tracing is the "tracing_service" section in Teleport configuration file
Tracing TracingService `yaml:"tracing_service,omitempty"`
// Discovery is the "discovery_service" section in the Teleport
// configuration file
Discovery Discovery `yaml:"discovery_service,omitempty"`
// Okta is the "okta_service" section in the Teleport configuration file
Okta Okta `yaml:"okta_service,omitempty"`
// Jamf is the "jamf_service" section in the config file.
Jamf JamfService `yaml:"jamf_service,omitempty"`
// Plugins is the section of the config for configuring the plugin service.
Plugins PluginService `yaml:"plugin_service,omitempty"`
// AccessGraph is the section of the config describing AccessGraph service
AccessGraph AccessGraph `yaml:"access_graph,omitempty"`
}
// ReadFromFile reads Teleport configuration from a file. Currently only YAML
// format is supported
func ReadFromFile(filePath string) (*FileConfig, error) {
f, err := utils.OpenFileAllowingUnsafeLinks(filePath)
if err != nil {
if errors.Is(err, fs.ErrPermission) {
return nil, trace.Wrap(err, "failed to open file for Teleport configuration: %v. Ensure that you are running as a user with appropriate permissions.", filePath)
}
return nil, trace.Wrap(err, "failed to open file for Teleport configuration at %v", filePath)
}
defer f.Close()
return ReadConfig(f)
}
// ReadFromString reads values from base64 encoded byte string
func ReadFromString(configString string) (*FileConfig, error) {
data, err := base64.StdEncoding.DecodeString(configString)
if err != nil {
return nil, trace.BadParameter(
"configuration should be base64 encoded: %v", err)
}
return ReadConfig(bytes.NewBuffer(data))
}
// ReadConfig reads Teleport configuration from reader in YAML format
func ReadConfig(reader io.Reader) (*FileConfig, error) {
// read & parse YAML config:
bytes, err := io.ReadAll(reader)
if err != nil {
return nil, trace.Wrap(err, "failed reading Teleport configuration")
}
var fc FileConfig
if err := yaml.UnmarshalStrict(bytes, &fc); err != nil {
// Remove all newlines in the YAML error, to avoid escaping when printing.
return nil, trace.BadParameter("failed parsing the config file: %s", strings.ReplaceAll(err.Error(), "\n", ""))
}
if err := fc.CheckAndSetDefaults(); err != nil {
return nil, trace.BadParameter("failed to parse Teleport configuration: %v", err)
}
return &fc, nil
}
// SampleFlags specifies standalone configuration parameters
type SampleFlags struct {
// ClusterName is an optional cluster name
ClusterName string
// LicensePath adds license path to config
LicensePath string
// ACMEEmail is acme email
ACMEEmail string
// ACMEEnabled turns on ACME
ACMEEnabled bool
// Version is the Teleport Configuration version.
Version string
// PublicAddr sets the hostport the proxy advertises for the HTTP endpoint.
PublicAddr string
// KeyFile is a TLS key file
KeyFile string
// CertFile is a TLS Certificate file
CertFile string
// DataDir is a path to a directory where Teleport keep its data
DataDir string
// AuthToken is a token to register with an auth server
AuthToken string
// Roles is a list of comma-separated roles to create a config file with
Roles string
// AuthServer is the address of the auth server
AuthServer string
// ProxyAddress is the address of the proxy
ProxyAddress string
// AppName is the name of the application to start
AppName string
// AppURI is the internal address of the application to proxy
AppURI string
// NodeLabels is list of labels in the format `foo=bar,baz=bax` to add to newly created nodes.
NodeLabels string
// CAPin is the SKPI hash of the CA used to verify the Auth Server. Can be
// a single value or a list.
CAPin string
// JoinMethod is the method that will be used to join the cluster, either "token", "iam" or "ec2"
JoinMethod string
// NodeName is the name of the teleport node
NodeName string
// Silent suppresses user hint printed after config has been generated.
Silent bool
// AzureClientID is the client ID of the managed identity to use when joining
// the cluster. Only applicable for the azure join method.
AzureClientID string
}
// MakeSampleFileConfig returns a sample config to start
// a standalone server
func MakeSampleFileConfig(flags SampleFlags) (fc *FileConfig, err error) {
if (flags.KeyFile == "") != (flags.CertFile == "") { // xor
return nil, trace.BadParameter("please provide both --key-file and --cert-file")
}
if flags.ACMEEnabled {
if flags.ClusterName == "" {
return nil, trace.BadParameter("please provide --cluster-name when using ACME, for example --cluster-name=example.com")
}
if flags.CertFile != "" {
return nil, trace.BadParameter("could not use --key-file/--cert-file when ACME is enabled")
}
}
conf := servicecfg.MakeDefaultConfig()
var g Global
if flags.NodeName != "" {
g.NodeName = flags.NodeName
} else {
g.NodeName = conf.Hostname
}
g.Logger.Output = "stderr"
g.Logger.Severity = "INFO"
g.Logger.Format.Output = "text"
g.DataDir = flags.DataDir
if g.DataDir == "" {
g.DataDir = defaults.DataDir
}
if err := setJoinParams(&g, flags); err != nil {
return nil, trace.Wrap(err)
}
if flags.Version == defaults.TeleportConfigVersionV3 {
if flags.AuthServer != "" && flags.ProxyAddress != "" {
return nil, trace.BadParameter("--proxy and --auth-server cannot both be set")
} else if flags.AuthServer != "" {
g.AuthServer = flags.AuthServer
} else if flags.ProxyAddress != "" {
g.ProxyServer = flags.ProxyAddress
}
} else {
if flags.AuthServer != "" {
g.AuthServers = []string{flags.AuthServer}
}
if flags.ProxyAddress != "" {
return nil, trace.BadParameter("--proxy cannot be used with configuration versions older than v3")
}
}
g.CAPin = strings.Split(flags.CAPin, ",")
roles := roleMapFromFlags(flags)
// SSH config:
s, err := makeSampleSSHConfig(conf, flags, roles[defaults.RoleNode])
if err != nil {
return nil, trace.Wrap(err)
}
// Auth config:
a := makeSampleAuthConfig(conf, flags, roles[defaults.RoleAuthService])
// sample proxy config:
p, err := makeSampleProxyConfig(conf, flags, roles[defaults.RoleProxy])
if err != nil {
return nil, trace.Wrap(err)
}
// Apps config:
apps, err := makeSampleAppsConfig(conf, flags, roles[defaults.RoleApp])
if err != nil {
return nil, trace.Wrap(err)
}
// DB config:
var dbs Databases
if roles[defaults.RoleDatabase] {
// keep it disable since `teleport configure` don't have all the necessary flags
// for this kind of resource
dbs.EnabledFlag = "no"
}
// WindowsDesktop config:
var d WindowsDesktopService
if roles[defaults.RoleWindowsDesktop] {
// keep it disable since `teleport configure` don't have all the necessary flags
// for this kind of resource
d.EnabledFlag = "no"
}
fc = &FileConfig{
Version: flags.Version,
Global: g,
Proxy: p,
SSH: s,
Auth: a,
Apps: apps,
Databases: dbs,
WindowsDesktop: d,
}
return fc, nil
}
func setJoinParams(g *Global, flags SampleFlags) error {
joinMethod := flags.JoinMethod
if joinMethod == "" && flags.AuthToken != "" {
joinMethod = string(types.JoinMethodToken)
}
g.JoinParams = JoinParams{
TokenName: flags.AuthToken,
Method: types.JoinMethod(joinMethod),
}
if flags.AzureClientID != "" {
g.JoinParams.Azure = AzureJoinParams{
ClientID: flags.AzureClientID,
}
}
return nil
}
func makeSampleSSHConfig(conf *servicecfg.Config, flags SampleFlags, enabled bool) (SSH, error) {
var s SSH
if enabled {
s.EnabledFlag = "yes"
s.ListenAddress = conf.SSH.Addr.Addr
labels, err := client.ParseLabelSpec(flags.NodeLabels)
if err != nil {
return s, trace.Wrap(err)
}
s.Labels = labels
} else {
s.EnabledFlag = "no"
}
return s, nil
}
func makeSampleAuthConfig(conf *servicecfg.Config, flags SampleFlags, enabled bool) Auth {
var a Auth
if enabled {
a.ListenAddress = conf.Auth.ListenAddr.Addr
a.ClusterName = ClusterName(flags.ClusterName)
a.EnabledFlag = "yes"
if flags.LicensePath != "" {
a.LicenseFile = flags.LicensePath
}
// from config v2 onwards, we support `proxy_listener_mode`, so we set it to `multiplex`
if flags.Version != defaults.TeleportConfigVersionV1 {
a.ProxyListenerMode = types.ProxyListenerMode_Multiplex
}
} else {
a.EnabledFlag = "no"
}
return a
}
func makeSampleProxyConfig(conf *servicecfg.Config, flags SampleFlags, enabled bool) (Proxy, error) {
var p Proxy
if enabled {
p.EnabledFlag = "yes"
p.ListenAddress = conf.Proxy.SSHAddr.Addr
if flags.ACMEEnabled {
p.ACME.EnabledFlag = "yes"
p.ACME.Email = flags.ACMEEmail
// ACME uses TLS-ALPN-01 challenge that requires port 443
// https://letsencrypt.org/docs/challenge-types/#tls-alpn-01
p.PublicAddr = apiutils.Strings{net.JoinHostPort(flags.ClusterName, fmt.Sprintf("%d", teleport.StandardHTTPSPort))}
p.WebAddr = net.JoinHostPort(defaults.BindIP, fmt.Sprintf("%d", teleport.StandardHTTPSPort))
}
if flags.PublicAddr != "" {
// default to 443 if port is not specified
publicAddr, err := utils.ParseHostPortAddr(flags.PublicAddr, teleport.StandardHTTPSPort)
if err != nil {
return Proxy{}, trace.Wrap(err)
}
p.PublicAddr = apiutils.Strings{publicAddr.String()}
// use same port for web addr
webPort := publicAddr.Port(teleport.StandardHTTPSPort)
p.WebAddr = net.JoinHostPort(defaults.BindIP, fmt.Sprintf("%d", webPort))
}
if flags.KeyFile != "" && flags.CertFile != "" {
if _, err := tls.LoadX509KeyPair(flags.CertFile, flags.KeyFile); err != nil {
return Proxy{}, trace.Wrap(err, "failed to load x509 key pair from --key-file and --cert-file")
}
p.KeyPairs = append(p.KeyPairs, KeyPair{
PrivateKey: flags.KeyFile,
Certificate: flags.CertFile,
})
}
} else {
p.EnabledFlag = "no"
}
return p, nil
}
func makeSampleAppsConfig(conf *servicecfg.Config, flags SampleFlags, enabled bool) (Apps, error) {
var apps Apps
// assume users want app role if they added app name and/or uri but didn't add app role
if enabled || flags.AppURI != "" || flags.AppName != "" {
if flags.AppURI == "" || flags.AppName == "" {
return Apps{}, trace.BadParameter("please provide both --app-name and --app-uri")
}
apps.EnabledFlag = "yes"
apps.Apps = []*App{
{
Name: flags.AppName,
URI: flags.AppURI,
},
}
}
return apps, nil
}
func roleMapFromFlags(flags SampleFlags) map[string]bool {
// if no roles are provided via CLI, return the default roles
if flags.Roles == "" {
return map[string]bool{
defaults.RoleProxy: true,
defaults.RoleNode: true,
defaults.RoleAuthService: true,
}
}
roles := splitRoles(flags.Roles)
m := make(map[string]bool)
for _, r := range roles {
m[r] = true
}
return m
}
// DebugDumpToYAML allows for quick YAML dumping of the config
func (conf *FileConfig) DebugDumpToYAML() string {
bytes, err := yaml.Marshal(&conf)
if err != nil {
panic(err)
}
return string(bytes)
}
// CheckAndSetDefaults sets defaults and ensures that the ciphers, kex
// algorithms, and mac algorithms set are supported by golang.org/x/crypto/ssh.
// This ensures we don't start Teleport with invalid configuration.
func (conf *FileConfig) CheckAndSetDefaults() error {
conf.Auth.defaultEnabled = true
conf.Proxy.defaultEnabled = true
conf.SSH.defaultEnabled = true
conf.Kube.defaultEnabled = false
conf.Okta.defaultEnabled = false
conf.Debug.defaultEnabled = true
if conf.Version == "" {
conf.Version = defaults.TeleportConfigVersionV1
}
var sc ssh.Config
sc.SetDefaults()
for _, c := range conf.Ciphers {
if !slices.Contains(sc.Ciphers, c) {
return trace.BadParameter("cipher algorithm %q is not supported; supported algorithms: %q", c, sc.Ciphers)
}
}
for _, k := range conf.KEXAlgorithms {
if !slices.Contains(sc.KeyExchanges, k) {
return trace.BadParameter("KEX algorithm %q is not supported; supported algorithms: %q", k, sc.KeyExchanges)
}
}
for _, m := range conf.MACAlgorithms {
if !slices.Contains(sc.MACs, m) {
return trace.BadParameter("MAC algorithm %q is not supported; supported algorithms: %q", m, sc.MACs)
}
}
return nil
}
// JoinParams configures the parameters for Simplified Node Joining.
type JoinParams struct {
TokenName string `yaml:"token_name"`
Method types.JoinMethod `yaml:"method"`
Azure AzureJoinParams `yaml:"azure,omitempty"`
}
// AzureJoinParams configures the parameters specific to the Azure join method.
type AzureJoinParams struct {
ClientID string `yaml:"client_id"`
}
// ConnectionRate configures rate limiter
type ConnectionRate struct {
Period time.Duration `yaml:"period"`
Average int64 `yaml:"average"`
Burst int64 `yaml:"burst"`
}
// ConnectionLimits sets up connection limiter
type ConnectionLimits struct {
MaxConnections int64 `yaml:"max_connections"`
Rates []ConnectionRate `yaml:"rates,omitempty"`
// Deprecated: MaxUsers has no effect.
MaxUsers int `yaml:"max_users"`
}
// LegacyLog contains the old format of the 'format' field
// It is kept here for backwards compatibility and should always be maintained
// The custom yaml unmarshaler should automatically convert it into the new
// expected format.
type LegacyLog struct {
// Output defines where logs go. It can be one of the following: "stderr", "stdout" or
// a path to a log file
Output string `yaml:"output,omitempty"`
// Severity defines how verbose the log will be. Possible values are "error", "info", "warn"
Severity string `yaml:"severity,omitempty"`
// Format lists the output fields from KnownFormatFields. Example format: [timestamp, component, caller]
Format []string `yaml:"format,omitempty"`
}
// Log configures teleport logging
type Log struct {
// Output defines where logs go. It can be one of the following: "stderr", "stdout" or
// a path to a log file
Output string `yaml:"output,omitempty"`
// Severity defines how verbose the log will be. Possible values are "error", "info", "warn"
Severity string `yaml:"severity,omitempty"`
// Format defines the logs output format and extra fields
Format LogFormat `yaml:"format,omitempty"`
}
// LogFormat specifies the logs output format and extra fields
type LogFormat struct {
// Output defines the output format. Possible values are 'text' and 'json'.
Output string `yaml:"output,omitempty"`
// ExtraFields lists the output fields from KnownFormatFields. Example format: [timestamp, component, caller]
ExtraFields []string `yaml:"extra_fields,omitempty"`
}
func (l *Log) UnmarshalYAML(unmarshal func(interface{}) error) error {
// the next two lines are needed because of an infinite loop issue
// https://github.com/go-yaml/yaml/issues/107
type logYAML Log
log := (*logYAML)(l)
if err := unmarshal(log); err != nil {
var typeError *yaml.TypeError
if !errors.As(err, &typeError) {
return err
}
var legacyLog LegacyLog
if lerr := unmarshal(&legacyLog); lerr != nil {
// return the original unmarshal error
return err
}
l.Output = legacyLog.Output
l.Severity = legacyLog.Severity
l.Format.Output = "text"
l.Format.ExtraFields = legacyLog.Format
return nil
}
return nil
}
// Global is 'teleport' (global) section of the config file
type Global struct {
NodeName string `yaml:"nodename,omitempty"`
DataDir string `yaml:"data_dir,omitempty"`
PIDFile string `yaml:"pid_file,omitempty"`
JoinParams JoinParams `yaml:"join_params,omitempty"`
// v1, v2
AuthServers []string `yaml:"auth_servers,omitempty"`
// AuthToken is the old way of configuring the token to be used by the
// node to join the Teleport cluster. `JoinParams.TokenName` should be
// used instead with `JoinParams.JoinMethod = types.JoinMethodToken`.
AuthToken string `yaml:"auth_token,omitempty"`
// v3
AuthServer string `yaml:"auth_server,omitempty"`
ProxyServer string `yaml:"proxy_server,omitempty"`
Limits ConnectionLimits `yaml:"connection_limits,omitempty"`
Logger Log `yaml:"log,omitempty"`
Storage backend.Config `yaml:"storage,omitempty"`
AdvertiseIP string `yaml:"advertise_ip,omitempty"`
CachePolicy CachePolicy `yaml:"cache,omitempty"`
// CipherSuites is a list of TLS ciphersuites that Teleport supports. If
// omitted, a Teleport selected list of defaults will be used.
CipherSuites []string `yaml:"ciphersuites,omitempty"`
// Ciphers is a list of SSH ciphers that the server supports. If omitted,
// the defaults will be used.
Ciphers []string `yaml:"ciphers,omitempty"`
// KEXAlgorithms is a list of SSH key exchange (KEX) algorithms that the
// server supports. If omitted, the defaults will be used.
KEXAlgorithms []string `yaml:"kex_algos,omitempty"`
// MACAlgorithms is a list of SSH message authentication codes (MAC) that
// the server supports. If omitted the defaults will be used.
MACAlgorithms []string `yaml:"mac_algos,omitempty"`
// CASignatureAlgorithm is ignored but ketp for config backwards compat
CASignatureAlgorithm *string `yaml:"ca_signature_algo,omitempty"`
// CAPin is the SKPI hash of the CA used to verify the Auth Server. Can be
// a single value or a list.
CAPin apiutils.Strings `yaml:"ca_pin"`
// DiagAddr is the address to expose a diagnostics HTTP endpoint.
DiagAddr string `yaml:"diag_addr"`
}
// CachePolicy is used to control local cache
type CachePolicy struct {
// Type is for cache type `sqlite` or `in-memory`
Type string `yaml:"type,omitempty"`
// EnabledFlag enables or disables cache
EnabledFlag string `yaml:"enabled,omitempty"`
// TTL sets maximum TTL for the cached values
TTL string `yaml:"ttl,omitempty"`
// MaxBackoff sets the maximum backoff on error.
MaxBackoff time.Duration `yaml:"max_backoff,omitempty"`
}
// Enabled determines if a given "_service" section has been set to 'true'
func (c *CachePolicy) Enabled() bool {
if c.EnabledFlag == "" {
return true
}
enabled, _ := apiutils.ParseBool(c.EnabledFlag)
return enabled
}
// Parse parses cache policy from Teleport config
func (c *CachePolicy) Parse() (*servicecfg.CachePolicy, error) {
out := servicecfg.CachePolicy{
Enabled: c.Enabled(),
MaxRetryPeriod: c.MaxBackoff,
}
if err := out.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err)
}
return &out, nil
}
// Service is a common configuration of a teleport service
type Service struct {
defaultEnabled bool
EnabledFlag string `yaml:"enabled,omitempty"`
ListenAddress string `yaml:"listen_addr,omitempty"`
}
// Configured determines if a given "_service" section has been specified
func (s *Service) Configured() bool {
return s.EnabledFlag != ""
}
// Enabled determines if a given "_service" section has been set to 'true'
func (s *Service) Enabled() bool {
if !s.Configured() {
return s.defaultEnabled
}
v, err := apiutils.ParseBool(s.EnabledFlag)
if err != nil {
return false
}
return v
}
// Disabled returns 'true' if the service has been deliberately turned off
func (s *Service) Disabled() bool {
if !s.Configured() {
return !s.defaultEnabled
}
return !s.Enabled()
}
// Auth is 'auth_service' section of the config file
type Auth struct {
Service `yaml:",inline"`
// ProxyProtocol controls support for HAProxy PROXY protocol.
// Possible values:
// - 'on': one PROXY header is accepted and required per incoming connection.
// - 'off': no PROXY headers are allows, otherwise connection is rejected.
// If unspecified - one PROXY header is allowed, but not required. Connection is marked with source port set to 0
// and IP pinning will not be allowed. It is supposed to be used only as default mode for test setups.
// In production you should always explicitly set the mode based on your network setup - if you have L4 load balancer
// with enabled PROXY protocol in front of Teleport you should set it to 'on', if you don't have it, set it to 'off'
ProxyProtocol string `yaml:"proxy_protocol,omitempty"`
// ClusterName is the name of the CA who manages this cluster
ClusterName ClusterName `yaml:"cluster_name,omitempty"`
// StaticTokens are pre-defined host provisioning tokens supplied via config file for
// environments where paranoid security is not needed
//
// Each token string has the following format: "role1,role2,..:token",
// for example: "auth,proxy,node:MTIzNGlvemRmOWE4MjNoaQo"
StaticTokens StaticTokens `yaml:"tokens,omitempty"`
// Authentication holds authentication configuration information like authentication
// type, second factor type, specific connector information, etc.
Authentication *AuthenticationConfig `yaml:"authentication,omitempty"`
// SessionRecording determines where the session is recorded:
// node, node-sync, proxy, proxy-sync, or off.
SessionRecording string `yaml:"session_recording,omitempty"`
// ProxyChecksHostKeys is used when the proxy is in recording mode and
// determines if the proxy will check the host key of the client or not.
ProxyChecksHostKeys *types.BoolOption `yaml:"proxy_checks_host_keys,omitempty"`
// LicenseFile is a path to the license file. The path can be either absolute or
// relative to the global data dir
LicenseFile string `yaml:"license_file,omitempty"`
// FOR INTERNAL USE:
// ReverseTunnels is a list of SSH tunnels to 3rd party proxy services (used to talk
// to 3rd party auth servers we trust)
ReverseTunnels []ReverseTunnel `yaml:"reverse_tunnels,omitempty"`
// PublicAddr sets SSH host principals and TLS DNS names to auth
// server certificates
PublicAddr apiutils.Strings `yaml:"public_addr,omitempty"`
// ClientIdleTimeout sets global cluster default setting for client idle timeouts
ClientIdleTimeout types.Duration `yaml:"client_idle_timeout,omitempty"`
// DisconnectExpiredCert provides disconnect expired certificate setting -
// if true, connections with expired client certificates will get disconnected
DisconnectExpiredCert *types.BoolOption `yaml:"disconnect_expired_cert,omitempty"`
// SessionControlTimeout specifies the maximum amount of time a node can be out
// of contact with the auth server before it starts terminating controlled sessions.
SessionControlTimeout types.Duration `yaml:"session_control_timeout,omitempty"`
// KubeconfigFile is an optional path to kubeconfig file,
// if specified, teleport will use API server address and
// trusted certificate authority information from it
KubeconfigFile string `yaml:"kubeconfig_file,omitempty"`
// KeepAliveInterval set the keep-alive interval for server to client
// connections.
KeepAliveInterval types.Duration `yaml:"keep_alive_interval,omitempty"`
// KeepAliveCountMax set the number of keep-alive messages that can be
// missed before the server disconnects the client.
KeepAliveCountMax int64 `yaml:"keep_alive_count_max,omitempty"`
// ClientIdleTimeoutMessage is sent to the client when the inactivity timeout
// expires. The empty string implies no message should be sent prior to
// disconnection.
ClientIdleTimeoutMessage string `yaml:"client_idle_timeout_message,omitempty"`
// MessageOfTheDay is a banner that a user must acknowledge during a `tsh login`.
MessageOfTheDay string `yaml:"message_of_the_day,omitempty"`
// WebIdleTimeout sets global cluster default setting for WebUI client
// idle timeouts
WebIdleTimeout types.Duration `yaml:"web_idle_timeout,omitempty"`
// CAKeyParams configures how CA private keys will be created and stored.
CAKeyParams *CAKeyParams `yaml:"ca_key_params,omitempty"`
// ProxyListenerMode is a listener mode user by the proxy.
ProxyListenerMode types.ProxyListenerMode `yaml:"proxy_listener_mode,omitempty"`
// RoutingStrategy configures the routing strategy to nodes.
RoutingStrategy types.RoutingStrategy `yaml:"routing_strategy,omitempty"`
// TunnelStrategy configures the tunnel strategy used by the cluster.
TunnelStrategy *types.TunnelStrategyV1 `yaml:"tunnel_strategy,omitempty"`
// ProxyPingInterval defines in which interval the TLS routing ping message
// should be sent. This is applicable only when using ping-wrapped
// connections, regular TLS routing connections are not affected.
ProxyPingInterval types.Duration `yaml:"proxy_ping_interval,omitempty"`
// CaseInsensitiveRouting causes proxies to use case-insensitive hostname matching.
CaseInsensitiveRouting bool `yaml:"case_insensitive_routing,omitempty"`
// SSHDialTimeout is the timeout value that should be used for SSH connections.
SSHDialTimeout types.Duration `yaml:"ssh_dial_timeout,omitempty"`
// LoadAllCAs tells tsh to load the CAs for all clusters when trying
// to ssh into a node, instead of just the CA for the current cluster.
LoadAllCAs bool `yaml:"load_all_cas,omitempty"`
// HostedPlugins configures the hosted plugins runtime.
// This is currently Cloud-specific.
HostedPlugins HostedPlugins `yaml:"hosted_plugins,omitempty"`
// AccessMonitoring is a set of options related to the Access Monitoring feature.
AccessMonitoring *servicecfg.AccessMonitoringOptions `yaml:"access_monitoring,omitempty"`
}
// PluginService represents the configuration for the plugin service.
type PluginService struct {
Enabled bool `yaml:"enabled"`
// Plugins is a map of matchers for enabled plugin resources.
Plugins map[string]string `yaml:"plugins,omitempty"`
}
// AccessGraph represents the configuration for the AccessGraph service.
type AccessGraph struct {
// Enabled enables the AccessGraph service.
Enabled bool `yaml:"enabled"`
// Endpoint is the endpoint of the AccessGraph service.
Endpoint string `yaml:"endpoint"`
// CA is the path to the CA certificate for the AccessGraph service.
CA string `yaml:"ca"`
// Insecure is true if the AccessGraph service should not verify the CA.
Insecure bool `yaml:"insecure"`
// AuditLog contains audit log export details.
AuditLog AuditLogConfig `yaml:"audit_log"`
}
// AuditLogConfig specifies the audit log event export setup.
type AuditLogConfig struct {
// Enabled indicates if Audit Log event exporting is enabled.
Enabled bool `yaml:"enabled"`
// StartDate is the start date for exporting audit logs. It defaults to 90 days ago on the first export.
StartDate time.Time `yaml:"start_date"`
}
// Opsgenie represents the configuration for the Opsgenie plugin.
type Opsgenie struct {
// APIKeyFile is the path to a file containing an Opsgenie API key.
APIKeyFile string `yaml:"api_key_file"`
}
// hasCustomNetworkingConfig returns true if any of the networking
// configuration fields have values different from an empty Auth.
func (a *Auth) hasCustomNetworkingConfig() bool {
empty := Auth{}
return a.ClientIdleTimeout != empty.ClientIdleTimeout ||
a.ClientIdleTimeoutMessage != empty.ClientIdleTimeoutMessage ||
a.WebIdleTimeout != empty.WebIdleTimeout ||
a.KeepAliveInterval != empty.KeepAliveInterval ||
a.KeepAliveCountMax != empty.KeepAliveCountMax ||
a.SessionControlTimeout != empty.SessionControlTimeout ||
a.ProxyListenerMode != empty.ProxyListenerMode ||
a.RoutingStrategy != empty.RoutingStrategy ||
a.TunnelStrategy != empty.TunnelStrategy ||
a.ProxyPingInterval != empty.ProxyPingInterval ||
a.SSHDialTimeout != empty.SSHDialTimeout
}
// hasCustomSessionRecording returns true if any of the session recording
// configuration fields have values different from an empty Auth.
func (a *Auth) hasCustomSessionRecording() bool {
empty := Auth{}
return a.SessionRecording != empty.SessionRecording ||
a.ProxyChecksHostKeys != empty.ProxyChecksHostKeys
}
// CAKeyParams configures how CA private keys will be created and stored.
type CAKeyParams struct {
// PKCS11 configures a PKCS#11 HSM to be used for all CA private key generation and
// storage.
PKCS11 *PKCS11 `yaml:"pkcs11,omitempty"`
// GoogleCloudKMS configures Google Cloud Key Management Service to to be used for
// all CA private key crypto operations.
GoogleCloudKMS *GoogleCloudKMS `yaml:"gcp_kms,omitempty"`
// AWSKMS configures AWS Key Management Service to to be used for
// all CA private key crypto operations.
AWSKMS *AWSKMS `yaml:"aws_kms,omitempty"`
}
// PKCS11 configures a PKCS#11 HSM to be used for private key generation and
// storage.
type PKCS11 struct {
// ModulePath is the path to the PKCS#11 library.
ModulePath string `yaml:"module_path"`
// TokenLabel is the CKA_LABEL of the HSM token to use. Set this or
// SlotNumber to select a token.
TokenLabel string `yaml:"token_label,omitempty"`
// SlotNumber is the slot number of the HSM token to use. Set this or
// TokenLabel to select a token.
SlotNumber *int `yaml:"slot_number,omitempty"`
// PIN is the raw pin for connecting to the HSM. Set this or PINPath to set
// the pin.
PIN string `yaml:"pin,omitempty"`
// PINPath is a path to a file containing a pin for connecting to the HSM.
// Trailing newlines will be removed, other whitespace will be left. Set
// this or Pin to set the pin.
PINPath string `yaml:"pin_path,omitempty"`
// MaxSessions is the upper limit of sessions allowed by the HSM.
MaxSessions int `yaml:"max_sessions"`
}
// GoogleCloudKMS configures Google Cloud Key Management Service to to be used for
// all CA private key crypto operations.
type GoogleCloudKMS struct {
// KeyRing is the GCP key ring where all keys generated by this auth server
// should be held. This must be the fully qualified resource name of the key
// ring, including the project and location, e.g.
// projects/teleport-project/locations/us-west1/keyRings/teleport-keyring
KeyRing string `yaml:"keyring"`
// ProtectionLevel specifies how cryptographic operations are performed.
// For more information, see https://cloud.google.com/kms/docs/algorithms#protection_levels
// Supported options are "HSM" and "SOFTWARE".
ProtectionLevel string `yaml:"protection_level"`
}
// AWSKMS configures AWS Key Management Service to to be used for all CA private
// key crypto operations.
type AWSKMS struct {
// Account is the AWS account to use.
Account string `yaml:"account"`
// Region is the AWS region to use.
Region string `yaml:"region"`
// MultiRegion contains configuration for multi-region AWS KMS.
MultiRegion servicecfg.MultiRegionKeyStore `yaml:"multi_region,omitempty"`
// Tags are key/value pairs used as AWS resource tags. The 'TeleportCluster'
// tag is added automatically if not specified in the set of tags. Changing tags
// after Teleport has already created KMS keys may require manually updating
// the tags of existing keys.
Tags map[string]string `yaml:"tags,omitempty"`
}
// TrustedCluster struct holds configuration values under "trusted_clusters" key
type TrustedCluster struct {
// KeyFile is a path to a remote authority (AKA "trusted cluster") public keys
KeyFile string `yaml:"key_file,omitempty"`
// AllowedLogins is a comma-separated list of user logins allowed from that cluster
AllowedLogins string `yaml:"allow_logins,omitempty"`
// TunnelAddr is a comma-separated list of reverse tunnel addresses to
// connect to
TunnelAddr string `yaml:"tunnel_addr,omitempty"`
}
type ClusterName string
func (c ClusterName) Parse() (types.ClusterName, error) {
if string(c) == "" {
return nil, nil
}
return services.NewClusterNameWithRandomID(types.ClusterNameSpecV2{
ClusterName: string(c),
})
}
type StaticTokens []StaticToken
func (t StaticTokens) Parse() (types.StaticTokens, error) {
var provisionTokens []types.ProvisionTokenV1
for _, st := range t {
tokens, err := st.Parse()
if err != nil {
return nil, trace.Wrap(err)
}
provisionTokens = append(provisionTokens, tokens...)
}
return types.NewStaticTokens(types.StaticTokensSpecV2{
StaticTokens: provisionTokens,
})
}
type StaticToken string
// Parse is applied to a string in "role,role,role:token" format. It breaks it
// apart and constructs a list of services.ProvisionToken which contains the token,
// role, and expiry (infinite).
// If the token string is a file path, the file may contain multiple newline delimited
// tokens, in which case each token is used to construct a services.ProvisionToken
// with the same roles.
func (t StaticToken) Parse() ([]types.ProvisionTokenV1, error) {
// Split only on the first ':', for future cross platform compat with windows paths
parts := strings.SplitN(string(t), ":", 2)
if len(parts) != 2 {
return nil, trace.BadParameter("invalid static token spec: %q", t)
}
roles, err := types.ParseTeleportRoles(parts[0])
if err != nil {
return nil, trace.Wrap(err)
}
tokenPart, err := utils.TryReadValueAsFile(parts[1])
if err != nil {
return nil, trace.Wrap(err)
}
tokens := strings.Split(tokenPart, "\n")
provisionTokens := make([]types.ProvisionTokenV1, 0, len(tokens))
for _, token := range tokens {
provisionTokens = append(provisionTokens, types.ProvisionTokenV1{
Token: token,
Roles: roles,
Expires: time.Unix(0, 0).UTC(),
})
}
return provisionTokens, nil
}
// AuthenticationConfig describes the auth_service/authentication section of teleport.yaml
type AuthenticationConfig struct {
Type string `yaml:"type"`
SecondFactor constants.SecondFactorType `yaml:"second_factor,omitempty"`
SecondFactors []types.SecondFactorType `yaml:"second_factors,omitempty"`
ConnectorName string `yaml:"connector_name,omitempty"`
U2F *UniversalSecondFactor `yaml:"u2f,omitempty"`
Webauthn *Webauthn `yaml:"webauthn,omitempty"`
RequireMFAType types.RequireMFAType `yaml:"require_session_mfa,omitempty"`
LockingMode constants.LockingMode `yaml:"locking_mode,omitempty"`
// LocalAuth controls if local authentication is allowed.
LocalAuth *types.BoolOption `yaml:"local_auth"`
// Passwordless enables/disables passwordless support.
// Requires Webauthn to work.
// Defaults to true if the Webauthn is configured, defaults to false
// otherwise.
Passwordless *types.BoolOption `yaml:"passwordless"`
// Headless enables/disables headless support.
// Requires Webauthn to work.
// Defaults to true if the Webauthn is configured, defaults to false
// otherwise.
Headless *types.BoolOption `yaml:"headless"`
// DeviceTrust holds settings related to trusted device verification.
// Requires Teleport Enterprise.
DeviceTrust *DeviceTrust `yaml:"device_trust,omitempty"`
// DefaultSessionTTL is the default cluster max session ttl
DefaultSessionTTL types.Duration `yaml:"default_session_ttl"`
// Deprecated. HardwareKey.PIVSlot should be used instead.
PIVSlot hardwarekey.PIVSlotKeyString `yaml:"piv_slot,omitempty"`
// HardwareKey holds settings related to hardware key support.
// Requires Teleport Enterprise.
HardwareKey *HardwareKey `yaml:"hardware_key,omitempty"`
// SignatureAlgorithmSuite is the configured signature algorithm suite for the cluster.
SignatureAlgorithmSuite types.SignatureAlgorithmSuite `yaml:"signature_algorithm_suite"`
// StableUNIXUserConfig is [types.AuthPreferenceSpecV2.StableUnixUserConfig].
StableUNIXUserConfig *StableUNIXUserConfig `yaml:"stable_unix_user_config,omitempty"`
}
// Parse returns valid types.AuthPreference instance.
func (a *AuthenticationConfig) Parse() (types.AuthPreference, error) {
var err error
var u *types.U2F
if a.U2F != nil {
u, err = a.U2F.Parse()
if err != nil {
return nil, trace.Wrap(err)
}
}
var w *types.Webauthn
if a.Webauthn != nil {
w, err = a.Webauthn.Parse()
if err != nil {
return nil, trace.Wrap(err)
}
}
var dt *types.DeviceTrust
if a.DeviceTrust != nil {
dt, err = a.DeviceTrust.Parse()
if err != nil {
return nil, trace.Wrap(err)
}
}
var h *types.HardwareKey
switch {
case a.HardwareKey != nil:
if a.PIVSlot != "" {
slog.WarnContext(context.Background(), `Both "piv_slot" and "hardware_key" settings were populated, using "hardware_key" setting`)
}
h, err = a.HardwareKey.Parse()
if err != nil {
return nil, trace.Wrap(err)
}
case a.HardwareKey == nil && a.PIVSlot != "":
if err = a.PIVSlot.Validate(); err != nil {
return nil, trace.Wrap(err, "failed to parse piv_slot")
}
h = &types.HardwareKey{
PIVSlot: string(a.PIVSlot),
}
default:
}
if a.SecondFactor != "" && a.SecondFactors != nil {
const msg = `second_factor and second_factors are both set. second_factors will take precedence. ` +
`second_factor should be unset to remove this warning.`
slog.WarnContext(context.Background(), msg)
}
stableUNIXUserConfig, err := a.StableUNIXUserConfig.Parse()
if err != nil {
return nil, trace.Wrap(err, "failed to parse stable_unix_user_config")
}
ap, err := types.NewAuthPreferenceFromConfigFile(types.AuthPreferenceSpecV2{
Type: a.Type,
SecondFactor: a.SecondFactor,
SecondFactors: a.SecondFactors,
ConnectorName: a.ConnectorName,
U2F: u,
Webauthn: w,
RequireMFAType: a.RequireMFAType,
LockingMode: a.LockingMode,
AllowLocalAuth: a.LocalAuth,
AllowPasswordless: a.Passwordless,
AllowHeadless: a.Headless,
DeviceTrust: dt,
DefaultSessionTTL: a.DefaultSessionTTL,
HardwareKey: h,
SignatureAlgorithmSuite: a.SignatureAlgorithmSuite,
StableUnixUserConfig: stableUNIXUserConfig,
})
if err != nil {
return nil, trace.Wrap(err)
}
if err := services.ValidateAuthPreference(ap); err != nil {
return nil, trace.Wrap(err)
}
return ap, nil
}
type UniversalSecondFactor struct {
AppID string `yaml:"app_id"`
// Facets kept only to avoid breakages during Teleport updates.
// Webauthn is now used instead of U2F.
Facets []string `yaml:"facets"`
DeviceAttestationCAs []string `yaml:"device_attestation_cas"`
}
func (u *UniversalSecondFactor) Parse() (*types.U2F, error) {
attestationCAs, err := getCertificatePEMs(u.DeviceAttestationCAs)
if err != nil {
return nil, trace.BadParameter("u2f.device_attestation_cas: %v", err)
}
return &types.U2F{
AppID: u.AppID,
DeviceAttestationCAs: attestationCAs,
}, nil
}
type Webauthn struct {
RPID string `yaml:"rp_id,omitempty"`
AttestationAllowedCAs []string `yaml:"attestation_allowed_cas,omitempty"`
AttestationDeniedCAs []string `yaml:"attestation_denied_cas,omitempty"`
// Deprecated: Disabled has no effect, it is kept solely to not break existing
// configurations.
Disabled bool `yaml:"disabled,omitempty"`
}
func (w *Webauthn) Parse() (*types.Webauthn, error) {
allowedCAs, err := getCertificatePEMs(w.AttestationAllowedCAs)
if err != nil {
return nil, trace.BadParameter("webauthn.attestation_allowed_cas: %v", err)
}
deniedCAs, err := getCertificatePEMs(w.AttestationDeniedCAs)
if err != nil {
return nil, trace.BadParameter("webauthn.attestation_denied_cas: %v", err)
}
if w.Disabled {
const msg = `The "webauthn.disabled" setting is marked for removal and currently has no effect. ` +
`Please update your configuration to use WebAuthn. ` +
`Refer to https://goteleport.com/docs/admin-guides/access-controls/guides/webauthn/`
slog.WarnContext(context.Background(), msg)
}
return &types.Webauthn{
// Allow any RPID to go through, we rely on
// types.Webauthn.CheckAndSetDefaults to correct it.
RPID: w.RPID,
AttestationAllowedCAs: allowedCAs,
AttestationDeniedCAs: deniedCAs,
}, nil
}
func getCertificatePEMs(certOrPaths []string) ([]string, error) {
res := make([]string, len(certOrPaths))
for i, certOrPath := range certOrPaths {
pem, err := getCertificatePEM(certOrPath)
if err != nil {
return nil, err
}
res[i] = pem
}
return res, nil
}
func getCertificatePEM(certOrPath string) (string, error) {
_, parseErr := tlsutils.ParseCertificatePEM([]byte(certOrPath))
if parseErr == nil {
return certOrPath, nil // OK, valid inline PEM
}
// Try reading as a file and parsing that.
data, err := os.ReadFile(certOrPath)
if err != nil {
// Don't use trace in order to keep a clean error message.
return "", fmt.Errorf("%q is not a valid x509 certificate (%w) and can't be read as a file (%w)", certOrPath, parseErr, err)
}
if _, err := tlsutils.ParseCertificatePEM(data); err != nil {
// Don't use trace in order to keep a clean error message.
return "", fmt.Errorf("file %q contains an invalid x509 certificate: %w", certOrPath, err)
}
return string(data), nil // OK, valid PEM file
}
// DeviceTrust holds settings related to trusted device verification.
// Requires Teleport Enterprise.
type DeviceTrust struct {
// Mode is the trusted device verification mode.
// Mirrors types.DeviceTrust.Mode.
Mode string `yaml:"mode,omitempty"`
// AutoEnroll is the toggle for the device auto-enroll feature.
AutoEnroll string `yaml:"auto_enroll,omitempty"`
// EKCertAllowedCAs is an allow list of EKCert CAs. These may be specified
// as a PEM encoded certificate or as a path to a PEM encoded certificate.
//
// If present, only TPM devices that present an EKCert that is signed by a
// CA specified here may be enrolled (existing enrollments are
// unchanged).
//
// If not present, then the CA of TPM EKCerts will not be checked during
// enrollment, this allows any device to enroll.
EKCertAllowedCAs []string `yaml:"ekcert_allowed_cas,omitempty"`
}
func (dt *DeviceTrust) Parse() (*types.DeviceTrust, error) {
autoEnroll := false
if dt.AutoEnroll != "" {
var err error
autoEnroll, err = apiutils.ParseBool(dt.AutoEnroll)
if err != nil {
return nil, trace.Wrap(err)
}
}
allowedCAs, err := getCertificatePEMs(dt.EKCertAllowedCAs)
if err != nil {
return nil, trace.BadParameter("device_trust.ekcert_allowed_cas: %v", err)
}
return &types.DeviceTrust{
Mode: dt.Mode,
AutoEnroll: autoEnroll,
EKCertAllowedCAs: allowedCAs,
}, nil
}
// HardwareKey holds settings related to hardware key support.
// Requires Teleport Enterprise.
type HardwareKey struct {
// PIVSlot is a PIV slot that Teleport clients should use instead of the
// default based on private key policy. For example, "9a" or "9e".
PIVSlot hardwarekey.PIVSlotKeyString `yaml:"piv_slot,omitempty"`
// SerialNumberValidation contains optional settings for hardware key
// serial number validation, including whether it is enabled.
SerialNumberValidation *HardwareKeySerialNumberValidation `yaml:"serial_number_validation,omitempty"`
// PINCacheTTL specifies how long to cache the user's PIV PIN.
PINCacheTTL time.Duration `yaml:"pin_cache_ttl,omitempty"`
}
func (h *HardwareKey) Parse() (*types.HardwareKey, error) {
if h.PIVSlot != "" {
if err := h.PIVSlot.Validate(); err != nil {
return nil, trace.Wrap(err, "failed to parse hardware_key.piv_slot")
}
}
hk := &types.HardwareKey{
PIVSlot: string(h.PIVSlot),
PinCacheTTL: types.Duration(h.PINCacheTTL),
}
if h.SerialNumberValidation != nil {
var err error
hk.SerialNumberValidation, err = h.SerialNumberValidation.Parse()
if err != nil {
return nil, trace.Wrap(err)
}
}
return hk, nil
}
// HardwareKeySerialNumberValidation holds settings related to hardware key serial number validation.
// Requires Teleport Enterprise.
type HardwareKeySerialNumberValidation struct {
// Enabled indicates whether hardware key serial number validation is enabled.
Enabled string `yaml:"enabled"`
// SerialNumberTraitName is an optional custom user trait name for hardware key
// serial numbers to replace the default: "hardware_key_serial_numbers".
SerialNumberTraitName string `yaml:"serial_number_trait_name"`
}
func (h *HardwareKeySerialNumberValidation) Parse() (*types.HardwareKeySerialNumberValidation, error) {
enabled, err := apiutils.ParseBool(h.Enabled)
if err != nil {
return nil, trace.Wrap(err)
}
return &types.HardwareKeySerialNumberValidation{
Enabled: enabled,
SerialNumberTraitName: h.SerialNumberTraitName,
}, nil
}
// StableUNIXUserConfig is [types.StableUNIXUserConfig].
type StableUNIXUserConfig struct {
// Enabled is [types.StableUNIXUserConfig.Enabled].
Enabled bool `yaml:"enabled"`
// FirstUID is [types.StableUNIXUserConfig.FirstUid].
FirstUID int32 `yaml:"first_uid"`
// LastUID is [types.StableUNIXUserConfig.LastUid].
LastUID int32 `yaml:"last_uid"`
}
func (s *StableUNIXUserConfig) Parse() (*types.StableUNIXUserConfig, error) {
if s == nil {
return nil, nil
}
c := &types.StableUNIXUserConfig{
Enabled: s.Enabled,
FirstUid: s.FirstUID,
LastUid: s.LastUID,
}
if err := services.ValidateStableUNIXUserConfig(c); err != nil {
return nil, trace.Wrap(err)
}
return c, nil
}
// HostedPlugins defines 'auth_service/plugins' Enterprise extension
type HostedPlugins struct {
Enabled bool `yaml:"enabled"`
OAuthProviders PluginOAuthProviders `yaml:"oauth_providers,omitempty"`
}
// PluginOAuthProviders holds application credentials for each
// 3rd party API provider.
type PluginOAuthProviders struct {
Slack *OAuthClientCredentials `yaml:"slack,omitempty"`
}
func (p *PluginOAuthProviders) Parse() (servicecfg.PluginOAuthProviders, error) {
out := servicecfg.PluginOAuthProviders{}
if p.Slack != nil {
slack, err := p.Slack.Parse()
if err != nil {
return out, trace.Wrap(err)
}
out.SlackCredentials = slack
}
return out, nil
}
// OAuthClientCredentials holds paths from which to read
// client credentials for Teleport's OAuth app.
type OAuthClientCredentials struct {
// ClientID is the path to the file containing the Client ID
ClientID string `yaml:"client_id"`
// ClientSecret is the path to the file containing the Client Secret
ClientSecret string `yaml:"client_secret"`
}
func (o *OAuthClientCredentials) Parse() (*servicecfg.OAuthClientCredentials, error) {
if o.ClientID == "" || o.ClientSecret == "" {
return nil, trace.BadParameter("both client_id and client_secret paths must be specified")
}
var clientID, clientSecret string
content, err := os.ReadFile(o.ClientID)
if err != nil {
return nil, trace.Wrap(err)
}
clientID = strings.TrimSpace(string(content))
content, err = os.ReadFile(o.ClientSecret)
if err != nil {
return nil, trace.Wrap(err)
}
clientSecret = strings.TrimSpace(string(content))
return &servicecfg.OAuthClientCredentials{
ClientID: clientID,
ClientSecret: clientSecret,
}, nil
}
// SSH is 'ssh_service' section of the config file
type SSH struct {
Service `yaml:",inline"`
Namespace string `yaml:"namespace,omitempty"`
Labels map[string]string `yaml:"labels,omitempty"`
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 apiutils.Strings `yaml:"public_addr,omitempty"`
// BPF is used to configure BPF-based auditing for this node.
BPF *BPF `yaml:"enhanced_recording,omitempty"`
// RestrictedSession is used to restrict access to kernel objects
RestrictedSession *RestrictedSession `yaml:"restricted_session,omitempty"`
// MaybeAllowTCPForwarding enables or disables TCP port forwarding. We're
// using a pointer-to-bool here because the system default is to allow TCP
// forwarding, we need to distinguish between an unset value and a false
// value so we can an override unset value with `true`.
//
// Don't read this value directly: call the AllowTCPForwarding method
// instead.
MaybeAllowTCPForwarding *bool `yaml:"port_forwarding,omitempty"`
// X11 is used to configure X11 forwarding settings
X11 *X11 `yaml:"x11,omitempty"`
// MaybeSSHFileCopy enables or disables remote file operations via SCP/SFTP.
// We're using a pointer-to-bool here because the system default is to allow
// SCP/SFTP, we need to distinguish between an unset value and a false
// value so we can an override unset value with `true`.
//
// Don't read this value directly: call the SSHFileCopy method
// instead.
MaybeSSHFileCopy *bool `yaml:"ssh_file_copy,omitempty"`
// DisableCreateHostUser disables automatic user provisioning on this
// SSH node.
DisableCreateHostUser bool `yaml:"disable_create_host_user,omitempty"`
// ForceListen enables listening on the configured ListenAddress
// when connected to the cluster via a reverse tunnel. If no ListenAddress is
// configured, the default address is used.
//
// This allows the service to be connectable by users with direct network access.
// All connections still require a valid user certificate to be presented and will
// not permit any additional access. This is intended to provide an optional connection
// path to reduce latency if the Proxy is not co-located with the user and service.
ForceListen bool `yaml:"force_listen,omitempty"`
}
// AllowTCPForwarding checks whether the config file allows TCP forwarding or not.
func (ssh *SSH) AllowTCPForwarding() bool {
if ssh.MaybeAllowTCPForwarding == nil {
return true
}
return *ssh.MaybeAllowTCPForwarding
}
// SSHFileCopy checks whether the config file allows for file copying
// via SCP/SFTP.
func (ssh *SSH) SSHFileCopy() bool {
if ssh.MaybeSSHFileCopy == nil {
return true
}
return *ssh.MaybeSSHFileCopy
}
// X11ServerConfig returns the X11 forwarding server configuration.
func (ssh *SSH) X11ServerConfig() (*x11.ServerConfig, error) {
// Start with default configuration
cfg := &x11.ServerConfig{Enabled: false}
if ssh.X11 == nil {
return cfg, nil
}
var err error
cfg.Enabled, err = apiutils.ParseBool(ssh.X11.Enabled)
if err != nil {
return nil, trace.Wrap(err)
}
if !cfg.Enabled {
return cfg, nil
}
cfg.DisplayOffset = x11.DefaultDisplayOffset
if ssh.X11.DisplayOffset != nil {
cfg.DisplayOffset = int(*ssh.X11.DisplayOffset)
if cfg.DisplayOffset > x11.MaxDisplayNumber {
cfg.DisplayOffset = x11.MaxDisplayNumber
}
}
cfg.MaxDisplay = cfg.DisplayOffset + x11.DefaultMaxDisplays
if ssh.X11.MaxDisplay != nil {
cfg.MaxDisplay = int(*ssh.X11.MaxDisplay)
if cfg.MaxDisplay < cfg.DisplayOffset {
return nil, trace.BadParameter("x11.MaxDisplay cannot be smaller than x11.DisplayOffset")
}
}
if cfg.MaxDisplay > x11.MaxDisplayNumber {
cfg.MaxDisplay = x11.MaxDisplayNumber
}
return cfg, nil
}
// Discovery represents a discovery_service section in the config file.
type Discovery struct {
Service `yaml:",inline"`
// AWSMatchers are used to match EC2 instances
AWSMatchers []AWSMatcher `yaml:"aws,omitempty"`
// AzureMatchers are used to match Azure resources.
AzureMatchers []AzureMatcher `yaml:"azure,omitempty"`
// GCPMatchers are used to match GCP resources.
GCPMatchers []GCPMatcher `yaml:"gcp,omitempty"`
// KubernetesMatchers are used to match services inside Kubernetes cluster for auto discovery
KubernetesMatchers []KubernetesMatcher `yaml:"kubernetes,omitempty"`
// AccessGraph is used to configure the cloud sync into AccessGraph.
AccessGraph *AccessGraphSync `yaml:"access_graph,omitempty"`
// DiscoveryGroup is the name of the discovery group that the current
// discovery service is a part of.
// It is used to filter out discovered resources that belong to another
// discovery services. When running in high availability mode and the agents
// have access to the same cloud resources, this field value must be the same
// for all discovery services. If different agents are used to discover different
// sets of cloud resources, this field must be different for each set of agents.
DiscoveryGroup string `yaml:"discovery_group,omitempty"`
// PollInterval is the cadence at which the discovery server will run each of its
// discovery cycles.
// Default: [github.com/gravitational/teleport/lib/srv/discovery/common.DefaultDiscoveryPollInterval]
PollInterval time.Duration `yaml:"poll_interval,omitempty"`
}
// GCPMatcher matches GCP resources.
type GCPMatcher struct {
// Types are GKE resource types to match: "gke", "gce".
Types []string `yaml:"types,omitempty"`
// Locations are GKE locations to search resources for.
Locations []string `yaml:"locations,omitempty"`
// Labels are GCP labels to match.
Labels map[string]apiutils.Strings `yaml:"labels,omitempty"`
// Tags are an alias for Labels, for backwards compatibility.
Tags map[string]apiutils.Strings `yaml:"tags,omitempty"`
// ProjectIDs are the GCP project ID where the resources are deployed.
ProjectIDs []string `yaml:"project_ids,omitempty"`
// ServiceAccounts are the emails of service accounts attached to VMs.
ServiceAccounts []string `yaml:"service_accounts,omitempty"`
// InstallParams sets the join method when installing on
// discovered GCP VMs.
InstallParams *InstallParams `yaml:"install,omitempty"`
}
// AccessGraphSync represents the configuration for the AccessGraph Sync service.
type AccessGraphSync struct {
// AWS is the AWS configuration for the AccessGraph Sync service.
AWS []AccessGraphAWSSync `yaml:"aws,omitempty"`
// Azure is the Azure configuration for the AccessGraph Sync service.
Azure []AccessGraphAzureSync `yaml:"azure,omitempty"`
// PollInterval is the frequency at which to poll for AWS resources
PollInterval time.Duration `yaml:"poll_interval,omitempty"`
}
// AccessGraphAWSSyncCloudTrailLogs represents the configuration for the SQS queue
// to poll for CloudTrail notifications.
type AccessGraphAWSSyncCloudTrailLogs struct {
// QueueURL is the URL of the SQS queue to poll for AWS resources.
QueueURL string `yaml:"queue_url,omitempty"`
// QueueRegion is the AWS region of the SQS queue to poll for AWS resources.
QueueRegion string `yaml:"queue_region,omitempty"`
}
// AccessGraphAWSSync represents the configuration for the AWS AccessGraph Sync service.
type AccessGraphAWSSync struct {
// Regions are AWS regions to poll for resources.
Regions []string `yaml:"regions,omitempty"`
// AssumeRoleARN is the AWS role to assume for database discovery.
AssumeRoleARN string `yaml:"assume_role_arn,omitempty"`
// ExternalID is the AWS external ID to use when assuming a role for
// database discovery in an external AWS account.
ExternalID string `yaml:"external_id,omitempty"`
// CloudTrailLogs is the configuration for the SQS queue to poll for
// CloudTrail logs.
CloudTrailLogs *AccessGraphAWSSyncCloudTrailLogs `yaml:"cloud_trail_logs,omitempty"`
}
// AccessGraphAzureSync represents the configuration for the Azure AccessGraph Sync service.
type AccessGraphAzureSync struct {
// SubscriptionID is the Azure subscription ID configured for syncing
SubscriptionID string `yaml:"subscription_id,omitempty"`
}
// CommandLabel is `command` section of `ssh_service` in the config file
type CommandLabel struct {
Name string `yaml:"name"`
Command []string `yaml:"command,flow"`
Period time.Duration `yaml:"period"`
}
// PAM is configuration for Pluggable Authentication Modules (PAM).
type PAM struct {
// Enabled controls if PAM will be used or not.
Enabled string `yaml:"enabled"`
// ServiceName is the name of the PAM policy to apply.
ServiceName string `yaml:"service_name"`
// UsePAMAuth specifies whether to trigger the "auth" PAM modules from the
// policy.
UsePAMAuth bool `yaml:"use_pam_auth"`
// Environment represents environment variables to pass to PAM.
// These may contain role-style interpolation syntax.
Environment map[string]string `yaml:"environment,omitempty"`
}
// Parse returns a parsed PAM config.
func (p *PAM) Parse() *servicecfg.PAMConfig {
serviceName := p.ServiceName
if serviceName == "" {
serviceName = defaults.PAMServiceName
}
enabled, _ := apiutils.ParseBool(p.Enabled)
return &servicecfg.PAMConfig{
Enabled: enabled,
ServiceName: serviceName,
UsePAMAuth: p.UsePAMAuth,
Environment: p.Environment,
}
}
// BPF is configuration for BPF-based auditing.
type BPF struct {
// Enabled enables or disables enhanced session recording for this node.
Enabled string `yaml:"enabled"`
// CommandBufferSize is the size of the perf buffer for command events.
CommandBufferSize *int `yaml:"command_buffer_size,omitempty"`
// DiskBufferSize is the size of the perf buffer for disk events.
DiskBufferSize *int `yaml:"disk_buffer_size,omitempty"`
// NetworkBufferSize is the size of the perf buffer for network events.
NetworkBufferSize *int `yaml:"network_buffer_size,omitempty"`
// CgroupPath controls where cgroupv2 hierarchy is mounted.
CgroupPath string `yaml:"cgroup_path"`
// RootPath root directory for the Teleport cgroups.
// Optional, defaults to /teleport
RootPath string `yaml:"root_path"`
}
// Parse will parse the enhanced session recording configuration.
func (b *BPF) Parse() *servicecfg.BPFConfig {
enabled, _ := apiutils.ParseBool(b.Enabled)
return &servicecfg.BPFConfig{
Enabled: enabled,
CommandBufferSize: b.CommandBufferSize,
DiskBufferSize: b.DiskBufferSize,
NetworkBufferSize: b.NetworkBufferSize,
CgroupPath: b.CgroupPath,
RootPath: b.RootPath,
}
}
// RestrictedSession is a configuration for limiting access to kernel objects
type RestrictedSession struct {
// Enabled enables or disables enforcement for this node.
Enabled string `yaml:"enabled"`
// EventsBufferSize is the size in bytes of the channel to report events
// from the kernel to us.
EventsBufferSize *int `yaml:"events_buffer_size,omitempty"`
}
// X11 is a configuration for X11 forwarding
type X11 struct {
// Enabled controls whether X11 forwarding requests can be granted by the server.
Enabled string `yaml:"enabled"`
// DisplayOffset tells the server what X11 display number to start from when
// searching for an open X11 unix socket for XServer proxies.
DisplayOffset *uint `yaml:"display_offset,omitempty"`
// MaxDisplay tells the server what X11 display number to stop at when
// searching for an open X11 unix socket for XServer proxies.
MaxDisplay *uint `yaml:"max_display,omitempty"`
}
// Databases represents the database proxy service configuration.
//
// In the configuration file this section will be "db_service".
type Databases struct {
// Service contains common service fields.
Service `yaml:",inline"`
// Databases is a list of databases proxied by the service.
Databases []*Database `yaml:"databases"`
// ResourceMatchers match cluster database resources.
ResourceMatchers []ResourceMatcher `yaml:"resources,omitempty"`
// AWSMatchers match AWS-hosted databases.
AWSMatchers []AWSMatcher `yaml:"aws,omitempty"`
// AzureMatchers match Azure hosted databases.
AzureMatchers []AzureMatcher `yaml:"azure,omitempty"`
}
// ResourceMatcher matches cluster resources.
type ResourceMatcher struct {
// Labels match resource labels.
Labels map[string]apiutils.Strings `yaml:"labels,omitempty"`
// AWS contains AWS specific settings.
AWS ResourceMatcherAWS `yaml:"aws,omitempty"`
}
// ResourceMatcherAWS contains AWS specific settings for resource matcher.
type ResourceMatcherAWS struct {
// AssumeRoleARN is the AWS role to assume to before accessing the
// database.
AssumeRoleARN string `yaml:"assume_role_arn,omitempty"`
// ExternalID is an optional AWS external ID used to enable assuming an AWS
// role across accounts.
ExternalID string `yaml:"external_id,omitempty"`
}
// AWSMatcher matches AWS EC2 instances and AWS Databases
type AWSMatcher struct {
// Types are AWS database types to match, "ec2", "rds", "redshift", "elasticache",
// or "memorydb".
Types []string `yaml:"types,omitempty"`
// Regions are AWS regions to query for databases.
Regions []string `yaml:"regions,omitempty"`
// AssumeRoleARN is the AWS role to assume for database discovery.
AssumeRoleARN string `yaml:"assume_role_arn,omitempty"`
// ExternalID is the AWS external ID to use when assuming a role for
// database discovery in an external AWS account.
ExternalID string `yaml:"external_id,omitempty"`
// Tags are AWS tags to match.
Tags map[string]apiutils.Strings `yaml:"tags,omitempty"`
// InstallParams sets the join method when installing on
// discovered EC2 nodes
InstallParams *InstallParams `yaml:"install,omitempty"`
// SSM provides options to use when sending a document command to
// an EC2 node
SSM AWSSSM `yaml:"ssm,omitempty"`
// Integration is the integration name used to generate credentials to interact with AWS APIs.
// Environment credentials will not be used when this value is set.
Integration string `yaml:"integration"`
// KubeAppDiscovery controls whether Kubernetes App Discovery will be enabled for agents running on
// discovered clusters, currently only affects AWS EKS discovery in integration mode.
KubeAppDiscovery bool `yaml:"kube_app_discovery"`
// SetupAccessForARN is the role that the discovery service should create EKS Access Entries for.
SetupAccessForARN string `yaml:"setup_access_for_arn"`
}
// InstallParams sets join method to use on discovered nodes
type InstallParams struct {
// JoinParams sets the token and method to use when generating
// config on cloud instances
JoinParams JoinParams `yaml:"join_params,omitempty"`
// ScriptName is the name of the teleport installer script
// resource for the cloud instance to execute
ScriptName string `yaml:"script_name,omitempty"`
// InstallTeleport disables agentless discovery
InstallTeleport string `yaml:"install_teleport,omitempty"`
// SSHDConfig provides the path to write sshd configuration changes
SSHDConfig string `yaml:"sshd_config,omitempty"`
// PublicProxyAddr is the address of the proxy the discovered node should use
// to connect to the cluster.
PublicProxyAddr string `yaml:"public_proxy_addr,omitempty"`
// Azure is te set of installation parameters specific to Azure.
Azure *AzureInstallParams `yaml:"azure,omitempty"`
// EnrollMode indicates the mode used to enroll the node into Teleport.
// Valid values: script, eice.
// Optional.
EnrollMode string `yaml:"enroll_mode"`
}
const (
installEnrollModeEICE = "eice"
installEnrollModeScript = "script"
)
var validInstallEnrollModes = []string{installEnrollModeEICE, installEnrollModeScript}
func (ip *InstallParams) parse() (*types.InstallerParams, error) {
install := &types.InstallerParams{
JoinMethod: ip.JoinParams.Method,
JoinToken: ip.JoinParams.TokenName,
ScriptName: ip.ScriptName,
InstallTeleport: true,
SSHDConfig: ip.SSHDConfig,
EnrollMode: types.InstallParamEnrollMode_INSTALL_PARAM_ENROLL_MODE_UNSPECIFIED,
}
switch ip.EnrollMode {
case installEnrollModeEICE:
install.EnrollMode = types.InstallParamEnrollMode_INSTALL_PARAM_ENROLL_MODE_EICE
case installEnrollModeScript:
install.EnrollMode = types.InstallParamEnrollMode_INSTALL_PARAM_ENROLL_MODE_SCRIPT
case "":
install.EnrollMode = types.InstallParamEnrollMode_INSTALL_PARAM_ENROLL_MODE_UNSPECIFIED
default:
return nil, trace.BadParameter("enroll mode %q is invalid, valid values: %v", ip.EnrollMode, validInstallEnrollModes)
}
if ip.InstallTeleport == "" {
return install, nil
}
var err error
install.InstallTeleport, err = apiutils.ParseBool(ip.InstallTeleport)
if err != nil {
return nil, trace.Wrap(err)
}
return install, nil
}
// AWSSSM provides options to use when executing SSM documents
type AWSSSM struct {
// DocumentName is the name of the document to use when executing an
// SSM command
DocumentName string `yaml:"document_name,omitempty"`
}
// Azure is te set of installation parameters specific to Azure.
type AzureInstallParams struct {
// ClientID is the client ID of the managed identity to use for installation.
ClientID string `yaml:"client_id"`
}
// AzureMatcher matches Azure resources.
type AzureMatcher struct {
// Subscriptions are Azure subscriptions to query for resources.
Subscriptions []string `yaml:"subscriptions,omitempty"`
// ResourceGroups are Azure resource groups to query for resources.
ResourceGroups []string `yaml:"resource_groups,omitempty"`
// Types are Azure types to match: "mysql", "postgres", "aks", "vm"
Types []string `yaml:"types,omitempty"`
// Regions are Azure locations to match for databases.
Regions []string `yaml:"regions,omitempty"`
// ResourceTags are Azure tags on resources to match.
ResourceTags map[string]apiutils.Strings `yaml:"tags,omitempty"`
// InstallParams sets the join method when installing on
// discovered Azure nodes.
InstallParams *InstallParams `yaml:"install,omitempty"`
}
// KubernetesMatcher matches Kubernetes resources.
type KubernetesMatcher struct {
// Types are Kubernetes services types to match. Currently only 'app' is supported.
Types []string `yaml:"types,omitempty"`
// Namespaces are Kubernetes namespaces in which to discover services
Namespaces []string `yaml:"namespaces,omitempty"`
// Labels are Kubernetes services labels to match.
Labels map[string]apiutils.Strings `yaml:"labels,omitempty"`
}
// Database represents a single database proxied by the service.
type Database struct {
// Name is the name for the database proxy service.
Name string `yaml:"name"`
// Description is an optional free-form database description.
Description string `yaml:"description,omitempty"`
// Protocol is the database type e.g. postgres, mysql, etc.
Protocol string `yaml:"protocol"`
// URI is the database address to connect to.
URI string `yaml:"uri"`
// CACertFile is an optional path to the database CA certificate.
// Deprecated in favor of TLS.CACertFile.
CACertFile string `yaml:"ca_cert_file,omitempty"`
// TLS keeps an optional TLS configuration options.
TLS DatabaseTLS `yaml:"tls"`
// MySQL are additional database options.
MySQL DatabaseMySQL `yaml:"mysql"`
// StaticLabels is a map of database static labels.
StaticLabels map[string]string `yaml:"static_labels,omitempty"`
// DynamicLabels is a list of database dynamic labels.
DynamicLabels []CommandLabel `yaml:"dynamic_labels,omitempty"`
// AWS contains AWS specific settings for AWS-hosted databases.
AWS DatabaseAWS `yaml:"aws"`
// GCP contains GCP specific settings for Cloud SQL databases.
GCP DatabaseGCP `yaml:"gcp"`
// AD contains Active Directory database configuration.
AD DatabaseAD `yaml:"ad"`
// Azure contains Azure database configuration.
Azure DatabaseAzure `yaml:"azure"`
// AdminUser describes database privileged user for auto-provisioning.
AdminUser DatabaseAdminUser `yaml:"admin_user"`
// Oracle is Database Oracle settings
Oracle DatabaseOracle `yaml:"oracle,omitempty"`
}
// DatabaseAdminUser describes database privileged user for auto-provisioning.
type DatabaseAdminUser struct {
// Name is the database admin username (e.g. "postgres").
Name string `yaml:"name"`
// DefaultDatabase is the database that the admin user logs into by
// default.
//
// Depending on the database type, this database may be used to store
// procedures or data for managing database users.
DefaultDatabase string `yaml:"default_database"`
}
// DatabaseAD contains database Active Directory configuration.
type DatabaseAD struct {
// KeytabFile is the path to the Kerberos keytab file.
KeytabFile string `yaml:"keytab_file"`
// Krb5File is the path to the Kerberos configuration file. Defaults to /etc/krb5.conf.
Krb5File string `yaml:"krb5_file,omitempty"`
// Domain is the Active Directory domain the database resides in.
Domain string `yaml:"domain"`
// SPN is the service principal name for the database.
SPN string `yaml:"spn"`
// LDAPCert is a certificate from Windows LDAP/AD, optional; only for x509 Authentication.
LDAPCert string `yaml:"ldap_cert,omitempty"`
// KDCHostName is the host name for a KDC for x509 Authentication.
KDCHostName string `yaml:"kdc_host_name,omitempty"`
// LDAPServiceAccountName is the name of service account for performing LDAP queries. Required for x509 Auth / PKINIT.
LDAPServiceAccountName string `yaml:"ldap_service_account_name,omitempty"`
// LDAPServiceAccountSID is the SID of service account for performing LDAP queries. Required for x509 Auth / PKINIT.
LDAPServiceAccountSID string `yaml:"ldap_service_account_sid,omitempty"`
}
// DatabaseTLS keeps TLS settings used when connecting to database.
type DatabaseTLS struct {
// Mode is a TLS verification mode. Available options are 'verify-full', 'verify-ca' or 'insecure',
// 'verify-full' is the default option.
Mode string `yaml:"mode"`
// ServerName allows providing custom server name.
// This name will override DNS name when validating certificate presented by the database.
ServerName string `yaml:"server_name,omitempty"`
// CACertFile is an optional path to the database CA certificate.
CACertFile string `yaml:"ca_cert_file,omitempty"`
// TrustSystemCertPool allows Teleport to trust certificate authorities
// available on the host system.
TrustSystemCertPool bool `yaml:"trust_system_cert_pool,omitempty"`
}
// DatabaseMySQL are an additional MySQL database options.
type DatabaseMySQL struct {
// ServerVersion is the MySQL version reported by DB proxy instead of default Teleport string.
ServerVersion string `yaml:"server_version,omitempty"`
}
// DatabaseOracle are an additional Oracle database options.
type DatabaseOracle struct {
// AuditUser is the Oracle database user privilege to access internal Oracle audit trail.
AuditUser string `yaml:"audit_user,omitempty"`
}
// SecretStore contains settings for managing secrets.
type SecretStore struct {
// KeyPrefix specifies the secret key prefix.
KeyPrefix string `yaml:"key_prefix,omitempty"`
// KMSKeyID specifies the KMS key used to encrypt and decrypt the secret.
KMSKeyID string `yaml:"kms_key_id,omitempty"`
}
// DatabaseAWS contains AWS specific settings for RDS/Aurora databases.
type DatabaseAWS struct {
// Region is a cloud region for RDS/Aurora database endpoint.
Region string `yaml:"region,omitempty"`
// Redshift contains Redshift specific settings.
Redshift DatabaseAWSRedshift `yaml:"redshift"`
// RDS contains RDS specific settings.
RDS DatabaseAWSRDS `yaml:"rds"`
// ElastiCache contains ElastiCache specific settings.
ElastiCache DatabaseAWSElastiCache `yaml:"elasticache"`
// SecretStore contains settings for managing secrets.
SecretStore SecretStore `yaml:"secret_store"`
// MemoryDB contains MemoryDB specific settings.
MemoryDB DatabaseAWSMemoryDB `yaml:"memorydb"`
// AccountID is the AWS account ID.
AccountID string `yaml:"account_id,omitempty"`
// AssumeRoleARN is the AWS role to assume to before accessing the database.
AssumeRoleARN string `yaml:"assume_role_arn,omitempty"`
// ExternalID is an optional AWS external ID used to enable assuming an AWS role across accounts.
ExternalID string `yaml:"external_id,omitempty"`
// RedshiftServerless contains RedshiftServerless specific settings.
RedshiftServerless DatabaseAWSRedshiftServerless `yaml:"redshift_serverless"`
// SessionTags is a list of AWS STS session tags.
SessionTags map[string]string `yaml:"session_tags,omitempty"`
}
// DatabaseAWSRedshift contains AWS Redshift specific settings.
type DatabaseAWSRedshift struct {
// ClusterID is the Redshift cluster identifier.
ClusterID string `yaml:"cluster_id,omitempty"`
}
// DatabaseAWSRDS contains settings for RDS databases.
type DatabaseAWSRDS struct {
// InstanceID is the RDS instance identifier.
InstanceID string `yaml:"instance_id,omitempty"`
// ClusterID is the RDS cluster (Aurora) identifier.
ClusterID string `yaml:"cluster_id,omitempty"`
}
// DatabaseAWSElastiCache contains settings for ElastiCache databases.
type DatabaseAWSElastiCache struct {
// ReplicationGroupID is the ElastiCache replication group ID.
ReplicationGroupID string `yaml:"replication_group_id,omitempty"`
}
// DatabaseAWSMemoryDB contains settings for MemoryDB databases.
type DatabaseAWSMemoryDB struct {
// ClusterName is the MemoryDB cluster name.
ClusterName string `yaml:"cluster_name,omitempty"`
}
// DatabaseAWSRedshiftServerless contains AWS Redshift Serverless specific settings.
type DatabaseAWSRedshiftServerless struct {
// WorkgroupName is the Redshift Serverless workgroup name.
WorkgroupName string `yaml:"workgroup_name,omitempty"`
// EndpointName is the Redshift Serverless VPC endpoint name.
EndpointName string `yaml:"endpoint_name,omitempty"`
}
// DatabaseGCP contains GCP specific settings for Cloud SQL databases.
type DatabaseGCP struct {
// ProjectID is the GCP project ID where the database is deployed.
ProjectID string `yaml:"project_id,omitempty"`
// InstanceID is the Cloud SQL database instance ID.
InstanceID string `yaml:"instance_id,omitempty"`
}
// DatabaseAzure contains Azure database configuration.
type DatabaseAzure struct {
// ResourceID is the Azure fully qualified ID for the resource.
ResourceID string `yaml:"resource_id,omitempty"`
// IsFlexiServer is true if the database is an Azure Flexible server.
IsFlexiServer bool `yaml:"is_flexi_server,omitempty"`
}
// Apps represents the configuration for the collection of applications this
// service will start. In file configuration this would be the "app_service"
// section.
type Apps struct {
// Service contains fields common to all services like "enabled" and
// "listen_addr".
Service `yaml:",inline"`
// DebugApp turns on a header debugging application.
DebugApp bool `yaml:"debug_app"`
// Apps is a list of applications that will be run by this service.
Apps []*App `yaml:"apps"`
// ResourceMatchers match cluster application resources.
ResourceMatchers []ResourceMatcher `yaml:"resources,omitempty"`
}
// App is the specific application that will be proxied by the application
// service.
type App struct {
// Name of the application.
Name string `yaml:"name"`
// Description is an optional free-form app description.
Description string `yaml:"description,omitempty"`
// URI is the internal address of the application.
URI string `yaml:"uri"`
// Public address of the application. This is the address users will access
// the application at.
PublicAddr string `yaml:"public_addr"`
// StaticLabels is a map of static labels to apply to this application.
StaticLabels map[string]string `yaml:"labels,omitempty"`
// DynamicLabels is a list of commands that generate dynamic labels
// to apply to this application.
DynamicLabels []CommandLabel `yaml:"commands,omitempty"`
// InsecureSkipVerify is used to skip validating the servers certificate.
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
// Rewrite defines a block that is used to rewrite requests and responses.
Rewrite *Rewrite `yaml:"rewrite,omitempty"`
// AWS contains additional options for AWS applications.
AWS *AppAWS `yaml:"aws,omitempty"`
// Cloud identifies the cloud instance the app represents.
Cloud string `yaml:"cloud,omitempty"`
// RequiredApps is a list of app names that are required for this app to function. Any app listed here will
// be part of the authentication redirect flow and authenticate along side this app.
RequiredApps []string `yaml:"required_apps,omitempty"`
// UseAnyProxyPublicAddr will rebuild this app's fqdn based on the proxy public addr that the
// request originated from. This should be true if your proxy has multiple proxy public addrs and you
// want the app to be accessible from any of them. If `public_addr` is explicitly set in the app spec,
// setting this value to true will overwrite that public address in the web UI.
UseAnyProxyPublicAddr bool `yaml:"use_any_proxy_public_addr"`
// CORS defines the Cross-Origin Resource Sharing configuration for the app,
// controlling how resources are shared across different origins.
CORS *CORS `yaml:"cors,omitempty"`
// TCPPorts is a list of ports and port ranges that an app agent can forward connections to.
// Only applicable to TCP App Access.
// If this field is not empty, URI is expected to contain no port number and start with the tcp
// protocol.
TCPPorts []PortRange `yaml:"tcp_ports,omitempty"`
// MCP contains MCP server-related configurations.
MCP *MCP `yaml:"mcp,omitempty"`
}
// CORS represents the configuration for Cross-Origin Resource Sharing (CORS)
// settings that control how the app responds to requests from different origins.
type CORS struct {
// AllowedOrigins specifies the list of origins that are allowed to access the app.
// Example: "https://client.teleport.example.com:3080"
AllowedOrigins []string `yaml:"allowed_origins"`
// AllowedMethods specifies the HTTP methods that are allowed when accessing the app.
// Example: "POST", "GET", "OPTIONS", "PUT", "DELETE"
AllowedMethods []string `yaml:"allowed_methods"`
// AllowedHeaders specifies the HTTP headers that can be used when making requests to the app.
// Example: "Content-Type", "Authorization", "X-Custom-Header"
AllowedHeaders []string `yaml:"allowed_headers"`
// ExposedHeaders indicate which response headers should be made available to scripts running in
// the browser, in response to a cross-origin request.
ExposedHeaders []string `yaml:"exposed_headers"`
// AllowCredentials indicates whether credentials such as cookies or authorization headers
// are allowed to be included in the requests.
AllowCredentials bool `yaml:"allow_credentials"`
// MaxAge specifies how long (in seconds) the results of a preflight request can be cached.
// Example: 86400 (which equals 24 hours)
MaxAge uint `yaml:"max_age"`
}
// Rewrite is a list of rewriting rules to apply to requests and responses.
type Rewrite struct {
// Redirect is a list of hosts that should be rewritten to the public address.
Redirect []string `yaml:"redirect"`
// Headers is a list of extra headers to inject in the request.
Headers []string `yaml:"headers,omitempty"`
// JWTClaims configures whether roles/traits are included in the JWT token
JWTClaims string `yaml:"jwt_claims,omitempty"`
}
// AppAWS contains additional options for AWS applications.
type AppAWS struct {
// ExternalID is the AWS External ID used when assuming roles in this app.
ExternalID string `yaml:"external_id,omitempty"`
}
// PortRange describes a port range for TCP apps. The range starts with Port and ends with EndPort.
// PortRange can be used to describe a single port in which case the Port field is the port and the
// EndPort field is 0.
type PortRange struct {
// Port describes the start of the range. It must be between 1 and 65535.
Port int `yaml:"port"`
// EndPort describes the end of the range, inclusive. When describing a port range, it must be
// greater than Port and less than or equal to 65535. When describing a single port, it must be
// set to 0.
EndPort int `yaml:"end_port,omitempty"`
}
// MCP contains MCP server-related configurations.
type MCP struct {
// Command to launch stdio-based MCP servers.
Command string `yaml:"command,omitempty"`
// Args to execute with the command.
Args []string `yaml:"args,omitempty"`
// RunAsHostUser is the host user account under which the command will be
// executed. Required for stdio-based MCP servers.
RunAsHostUser string `yaml:"run_as_host_user,omitempty"`
}
// Proxy is a `proxy_service` section of the config file:
type Proxy struct {
// Service is a generic service configuration section
Service `yaml:",inline"`
// WebAddr is a web UI listen address
WebAddr string `yaml:"web_listen_addr,omitempty"`
// TunAddr is a reverse tunnel address
TunAddr string `yaml:"tunnel_listen_addr,omitempty"`
// PeerAddr is the address this proxy will be dialed at by its peers.
PeerAddr string `yaml:"peer_listen_addr,omitempty"`
// PeerPublicAddr is the hostport the proxy advertises for peer proxy
// client connections.
PeerPublicAddr string `yaml:"peer_public_addr,omitempty"`
// KeyFile is a TLS key file
KeyFile string `yaml:"https_key_file,omitempty"`
// CertFile is a TLS Certificate file
CertFile string `yaml:"https_cert_file,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
// or not.
ProxyProtocol string `yaml:"proxy_protocol,omitempty"`
// ProxyProtocolAllowDowngrade controls support for downgrading IPv6 source addresses in PROXY headers to pseudo IPv4
// addresses when connecting to an IPv4 destination
ProxyProtocolAllowDowngrade *types.BoolOption `yaml:"proxy_protocol_allow_downgrade,omitempty"`
// KubeProxy configures kubernetes protocol support of the proxy
Kube KubeProxy `yaml:"kubernetes,omitempty"`
// KubeAddr is a shorthand for enabling the Kubernetes endpoint without a
// local Kubernetes cluster.
KubeAddr string `yaml:"kube_listen_addr,omitempty"`
// KubePublicAddr is a public address of the kubernetes endpoint.
KubePublicAddr apiutils.Strings `yaml:"kube_public_addr,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 apiutils.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 apiutils.Strings `yaml:"ssh_public_addr,omitempty"`
// TunnelPublicAddr sets the hostport the proxy advertises for the tunnel
// endpoint. The hosts in PublicAddr are included in the list of host
// principals on the SSH certificate.
TunnelPublicAddr apiutils.Strings `yaml:"tunnel_public_addr,omitempty"`
// KeyPairs is a list of x509 key pairs the proxy will load.
KeyPairs []KeyPair `yaml:"https_keypairs"`
// KeyPairsReloadInterval is the interval between attempts to reload
// x509 key pairs. If set to 0, then periodic reloading is disabled.
KeyPairsReloadInterval time.Duration `yaml:"https_keypairs_reload_interval"`
// ACME configures ACME protocol support
ACME ACME `yaml:"acme"`
// MySQLAddr is MySQL proxy listen address.
MySQLAddr string `yaml:"mysql_listen_addr,omitempty"`
// MySQLPublicAddr is the hostport the proxy advertises for MySQL
// client connections.
MySQLPublicAddr apiutils.Strings `yaml:"mysql_public_addr,omitempty"`
// MySQLServerVersion allow to overwrite proxy default mysql engine version reported by Teleport proxy.
MySQLServerVersion string `yaml:"mysql_server_version,omitempty"`
// PostgresAddr is Postgres proxy listen address.
PostgresAddr string `yaml:"postgres_listen_addr,omitempty"`
// PostgresPublicAddr is the hostport the proxy advertises for Postgres
// client connections.
PostgresPublicAddr apiutils.Strings `yaml:"postgres_public_addr,omitempty"`
// MongoAddr is Mongo proxy listen address.
MongoAddr string `yaml:"mongo_listen_addr,omitempty"`
// MongoPublicAddr is the hostport the proxy advertises for Mongo
// client connections.
MongoPublicAddr apiutils.Strings `yaml:"mongo_public_addr,omitempty"`
// IdP is configuration for identity providers.
//
//nolint:revive // Because we want this to be IdP.
IdP IdP `yaml:"idp,omitempty"`
// UI provides config options for the web UI
UI *UIConfig `yaml:"ui,omitempty"`
// TrustXForwardedFor enables the service to take client source IPs from
// the "X-Forwarded-For" headers for web APIs received from layer 7 load
// balancers or reverse proxies.
TrustXForwardedFor types.Bool `yaml:"trust_x_forwarded_for,omitempty"`
// AutomaticUpgradesChannels is a map of all version channels used by the
// proxy built-in version server to retrieve target versions. This is part
// of the automatic upgrades.
AutomaticUpgradesChannels automaticupgrades.Channels `yaml:"automatic_upgrades_channels,omitempty"`
}
// UIConfig provides config options for the web UI served by the proxy service.
type UIConfig struct {
// ScrollbackLines is the max number of lines the UI terminal can display in its history
ScrollbackLines int `yaml:"scrollback_lines,omitempty"`
// ShowResources determines which resources are shown in the web UI. Default if unset is "requestable"
// which means resources the user has access to and resources they can request will be shown in the
// resources UI. If set to `accessible_only`, only resources the user already has access to will be shown.
ShowResources constants.ShowResources `yaml:"show_resources,omitempty"`
}
// ACME configures ACME protocol - automatic X.509 certificates
type ACME struct {
// EnabledFlag is whether ACME should be enabled
EnabledFlag string `yaml:"enabled,omitempty"`
// Email is the email that will receive problems with certificate renewals
Email string `yaml:"email,omitempty"`
// URI is ACME server URI
URI string `yaml:"uri,omitempty"`
}
// Parse parses ACME section values
func (a ACME) Parse() (*servicecfg.ACME, error) {
// ACME is disabled by default
out := servicecfg.ACME{}
if a.EnabledFlag == "" {
return &out, nil
}
var err error
out.Enabled, err = apiutils.ParseBool(a.EnabledFlag)
if err != nil {
return nil, trace.Wrap(err)
}
out.Email = a.Email
if a.URI != "" {
_, err := url.Parse(a.URI)
if err != nil {
return nil, trace.Wrap(err, "acme.uri should be a valid URI, for example %v", acme.LetsEncryptURL)
}
}
out.URI = a.URI
return &out, nil
}
// IdP represents the configuration for identity providers running within the
// proxy.
//
//nolint:revive // Because we want this to be IdP.
type IdP struct {
// SAMLIdP represents configuratino options for the SAML identity provider.
SAMLIdP SAMLIdP `yaml:"saml,omitempty"`
}
// SAMLIdP represents the configuration for the SAML identity provider.
type SAMLIdP struct {
// Enabled turns the SAML IdP on or off for this process.
EnabledFlag string `yaml:"enabled,omitempty"`
// BaseURL is the base URL to provide to the SAML IdP.
BaseURL string `yaml:"base_url,omitempty"`
}
// Enabled returns true if the SAML IdP is enabled or if the enabled flag is unset.
func (s *SAMLIdP) Enabled() bool {
if s.EnabledFlag == "" {
return true
}
v, err := apiutils.ParseBool(s.EnabledFlag)
if err != nil {
return false
}
return v
}
// KeyPair represents a path on disk to a private key and certificate.
type KeyPair struct {
// PrivateKey is the path on disk to a PEM encoded private key,
PrivateKey string `yaml:"key_file"`
// Certificate is the path on disk to a PEM encoded x509 certificate.
Certificate string `yaml:"cert_file"`
}
// KubeProxy is a `kubernetes` section in `proxy_service`.
type KubeProxy struct {
// Service is a generic service configuration section
Service `yaml:",inline"`
// PublicAddr is a publicly advertised address of the kubernetes proxy
PublicAddr apiutils.Strings `yaml:"public_addr,omitempty"`
// KubeconfigFile is an optional path to kubeconfig file,
// if specified, teleport will use API server address and
// trusted certificate authority information from it
KubeconfigFile string `yaml:"kubeconfig_file,omitempty"`
// ClusterName is the name of a kubernetes cluster this proxy is running
// in. If set, this proxy will handle kubernetes requests for the cluster.
ClusterName string `yaml:"cluster_name,omitempty"`
}
// Kube is a `kubernetes_service`
type Kube struct {
// Service is a generic service configuration section
Service `yaml:",inline"`
// PublicAddr is a publicly advertised address of the kubernetes service
PublicAddr apiutils.Strings `yaml:"public_addr,omitempty"`
// KubeconfigFile is an optional path to kubeconfig file,
// if specified, teleport will use API server address and
// trusted certificate authority information from it
KubeconfigFile string `yaml:"kubeconfig_file,omitempty"`
// KubeClusterName is the name of a kubernetes cluster this service is
// running in. If set, this proxy will handle kubernetes requests for the
// cluster.
KubeClusterName string `yaml:"kube_cluster_name,omitempty"`
// StaticLabels are the static labels for RBAC on kubernetes clusters.
StaticLabels map[string]string `yaml:"labels,omitempty"`
// DynamicLabels are the dynamic labels for RBAC on kubernetes clusters.
DynamicLabels []CommandLabel `yaml:"commands,omitempty"`
// ResourceMatchers match cluster kube_cluster resources.
ResourceMatchers []ResourceMatcher `yaml:"resources,omitempty"`
}
// ReverseTunnel is a SSH reverse tunnel maintained by one cluster's
// proxy to remote Teleport proxy
type ReverseTunnel struct {
DomainName string `yaml:"domain_name"`
Addresses []string `yaml:"addresses"`
}
// ConvertAndValidate returns validated services.ReverseTunnel or nil and error otherwize
func (t *ReverseTunnel) ConvertAndValidate() (types.ReverseTunnel, error) {
for i := range t.Addresses {
addr, err := utils.ParseHostPortAddr(t.Addresses[i], defaults.SSHProxyTunnelListenPort)
if err != nil {
return nil, trace.Wrap(err, "Invalid address for tunnel %v", t.DomainName)
}
t.Addresses[i] = addr.String()
}
out, err := types.NewReverseTunnel(t.DomainName, t.Addresses)
if err != nil {
return nil, trace.Wrap(err)
}
if err := services.ValidateReverseTunnel(out); err != nil {
return nil, trace.Wrap(err)
}
return out, nil
}
// ClaimMapping is OIDC claim mapping that maps
// claim name to teleport roles
type ClaimMapping struct {
// Claim is OIDC claim name
Claim string `yaml:"claim"`
// Value is claim value to match
Value string `yaml:"value"`
// Roles is a list of teleport roles to match
Roles []string `yaml:"roles,omitempty"`
}
// Metrics is a `metrics_service` section of the config file:
type Metrics struct {
// Service is a generic service configuration section
Service `yaml:",inline"`
// KeyPairs is a list of x509 serving key pairs used for securing the metrics endpoint with mTLS.
// mTLS will be enabled for the service if both 'keypairs' and 'ca_certs' fields are set.
KeyPairs []KeyPair `yaml:"keypairs,omitempty"`
// CACerts is a list of prometheus CA certificates to validate clients against.
// mTLS will be enabled for the service if both 'keypairs' and 'ca_certs' fields are set.
CACerts []string `yaml:"ca_certs,omitempty"`
// GRPCServerLatency enables histogram metrics for each gRPC endpoint on the auth server
GRPCServerLatency bool `yaml:"grpc_server_latency,omitempty"`
// GRPCServerLatency enables histogram metrics for each gRPC endpoint on the auth server
GRPCClientLatency bool `yaml:"grpc_client_latency,omitempty"`
}
// MTLSEnabled returns whether mtls is enabled or not in the metrics service config.
func (m *Metrics) MTLSEnabled() bool {
return len(m.KeyPairs) > 0 && len(m.CACerts) > 0
}
// DebugService is a `debug_service` section of the config file.
type DebugService struct {
// Service is a generic service configuration section
Service `yaml:",inline"`
}
// WindowsDesktopService contains configuration for windows_desktop_service.
type WindowsDesktopService struct {
Service `yaml:",inline"`
// Labels are the configured windows deesktops service labels.
Labels map[string]string `yaml:"labels,omitempty"`
// PublicAddr is a list of advertised public addresses of this service.
PublicAddr apiutils.Strings `yaml:"public_addr,omitempty"`
// ShowDesktopWallpaper determines whether desktop sessions will show a
// user-selected wallpaper vs a system-default, single-color wallpaper.
ShowDesktopWallpaper bool `yaml:"show_desktop_wallpaper,omitempty"`
// LDAP is the LDAP connection parameters.
LDAP LDAPConfig `yaml:"ldap"`
// PKIDomain optionally configures a separate Active Directory domain
// for PKI operations. If empty, the domain from the LDAP config is used.
// This can be useful for cases where PKI is configured in a root domain
// but Teleport is used to provide access to users and computers in a child
// domain.
PKIDomain string `yaml:"pki_domain"`
// KDCAddress optionally configures the address of the Kerberos Key Distribution Center,
// which is used to support RDP Network Level Authentication (NLA).
// If empty, the LDAP address will be used instead.
// Note: NLA is only supported in Active Directory environments - this field has
// no effect when connecting to desktops as local Windows users.
KDCAddress string `yaml:"kdc_address"`
// Discovery configures desktop discovery via LDAP.
// New usages should prever DiscoveryConfigs instead, which allows for multiple searches.
Discovery LDAPDiscoveryConfig `yaml:"discovery,omitempty"`
// DiscoveryConfigs configures desktop discovery via LDAP.
DiscoveryConfigs []LDAPDiscoveryConfig `yaml:"discovery_configs,omitempty"`
// DiscoveryInterval controls how frequently the discovery process runs.
DiscoveryInterval time.Duration `yaml:"discovery_interval"`
// ADHosts is a list of static, AD-connected Windows hosts. This gives users
// a way to specify AD-connected hosts that won't be found by the filters
// specified in `discovery` (or if `discovery` is omitted).
//
// Deprecated: prefer StaticHosts instead.
ADHosts []string `yaml:"hosts,omitempty"`
// NonADHosts is a list of standalone Windows hosts that are not
// jointed to an Active Directory domain.
//
// Deprecated: prefer StaticHosts instead.
NonADHosts []string `yaml:"non_ad_hosts,omitempty"`
// StaticHosts is a list of Windows hosts (both AD-connected and standalone).
// User can specify name for each host and labels specific to it.
StaticHosts []WindowsHost `yaml:"static_hosts,omitempty"`
// HostLabels optionally applies labels to Windows hosts for RBAC.
// A host can match multiple rules and will get a union of all
// the matched labels.
HostLabels []WindowsHostLabelRule `yaml:"host_labels,omitempty"`
// ResourceMatchers match dynamic Windows desktop resources.
ResourceMatchers []ResourceMatcher `yaml:"resources,omitempty"`
}
// Check checks whether the WindowsDesktopService is valid or not
func (wds *WindowsDesktopService) Check() error {
hasAD := len(wds.ADHosts) > 0 || slices.ContainsFunc(wds.StaticHosts, func(host WindowsHost) bool {
return host.AD
})
if hasAD && wds.LDAP.Addr == "" {
return trace.BadParameter("if Active Directory hosts are specified in the windows_desktop_service, " +
"the ldap configuration must also be specified")
}
if wds.Discovery.BaseDN != "" && wds.LDAP.Addr == "" {
return trace.BadParameter("if discovery is specified in the windows_desktop_service, " +
"ldap configuration must also be specified")
}
return nil
}
// WindowsHostLabelRule describes how a set of labels should be applied to
// a Windows host.
type WindowsHostLabelRule struct {
// Match is a regexp that is checked against the Windows host's DNS name.
// If the regexp matches, this rule's labels will be applied to the host.
Match string `yaml:"match"`
// Labels is the set of labels to apply to hosts that match this rule.
Labels map[string]string `yaml:"labels"`
}
// WindowsHost describes single host in configuration
type WindowsHost struct {
// Name of the host
Name string `yaml:"name"`
// Address of the host, with an optional port.
// 10.1.103.4 or 10.1.103.4:3389, for example.
Address string `yaml:"addr"`
// Labels is the set of labels to apply to this host
Labels map[string]string `yaml:"labels"`
// AD tells if host is part of Active Directory domain
AD bool `yaml:"ad"`
}
// LDAPConfig is the LDAP connection parameters.
type LDAPConfig struct {
// Addr is the host:port of the LDAP server (typically port 389).
Addr string `yaml:"addr"`
// Domain is the ActiveDirectory domain name.
Domain string `yaml:"domain"`
// Username for LDAP authentication.
Username string `yaml:"username"`
// SID is the Security Identifier for the service account specified by Username.
SID string `yaml:"sid"`
// InsecureSkipVerify decides whether whether we skip verifying with the LDAP server's CA when making the LDAPS connection.
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
// ServerName is the name of the LDAP server for TLS.
ServerName string `yaml:"server_name,omitempty"`
// DEREncodedCAFile is the filepath to an optional DER encoded CA cert to be used for verification (if InsecureSkipVerify is set to false).
DEREncodedCAFile string `yaml:"der_ca_file,omitempty"`
// PEMEncodedCACert is an optional PEM encoded CA cert to be used for verification (if InsecureSkipVerify is set to false).
PEMEncodedCACert string `yaml:"ldap_ca_cert,omitempty"`
}
// LDAPDiscoveryConfig is LDAP discovery configuration for windows desktop discovery service.
type LDAPDiscoveryConfig struct {
// BaseDN is the base DN to search for desktops.
// Use the value '*' to search from the root of the domain,
// or leave blank to disable desktop discovery.
BaseDN string `yaml:"base_dn"`
// Filters are additional LDAP filters to apply to the search.
// See: https://ldap.com/ldap-filters/
Filters []string `yaml:"filters"`
// LabelAttributes are LDAP attributes to apply to hosts discovered
// via LDAP. Teleport labels hosts by prefixing the attribute with
// "ldap/" - for example, a value of "location" here would result in
// discovered desktops having a label with key "ldap/location" and
// the value being the value of the "location" attribute.
LabelAttributes []string `yaml:"label_attributes"`
// RDPPort is the port to use for RDP for hosts discovered with this configuration.
// Optional, defaults to 3389 if unspecified.
RDPPort int `yaml:"rdp_port"`
}
// TracingService contains configuration for the tracing_service.
type TracingService struct {
// Enabled turns the tracing service role on or off for this process
EnabledFlag string `yaml:"enabled,omitempty"`
// ExporterURL is the OTLP exporter URL to send spans to
ExporterURL string `yaml:"exporter_url"`
// KeyPairs is a list of x509 serving key pairs used for mTLS.
KeyPairs []KeyPair `yaml:"keypairs,omitempty"`
// CACerts are the exporter ca certs to use
CACerts []string `yaml:"ca_certs,omitempty"`
// SamplingRatePerMillion is the sampling rate for the exporter.
// 1_000_000 means all spans will be sampled and 0 means none are sampled.
SamplingRatePerMillion int `yaml:"sampling_rate_per_million"`
}
func (s *TracingService) Enabled() bool {
if s.EnabledFlag == "" {
return false
}
v, err := apiutils.ParseBool(s.EnabledFlag)
if err != nil {
return false
}
return v
}
// Okta represents an okta_service section in the config file.
type Okta struct {
Service `yaml:",inline"`
// APIEndpoint is the Okta API endpoint to use.
APIEndpoint string `yaml:"api_endpoint,omitempty"`
// APITokenPath is the path to the Okta API token.
APITokenPath string `yaml:"api_token_path,omitempty"`
// SyncPeriod is the duration between synchronization calls for synchronizing Okta applications and groups..
// Deprecated. Please use sync.app_group_sync_period instead.
SyncPeriod time.Duration `yaml:"sync_period,omitempty"`
// Import is the import settings for the Okta service.
Sync OktaSync `yaml:"sync,omitempty"`
}
// OktaSync represents the import subsection of the okta_service section in the config file.
type OktaSync struct {
// AppGroupSyncPeriod is the duration between synchronization calls for synchronizing Okta applications and groups.
AppGroupSyncPeriod time.Duration `yaml:"app_group_sync_period,omitempty"`
// SyncAccessLists will enable or disable the Okta importing of access lists. Defaults to false.
SyncAccessListsFlag string `yaml:"sync_access_lists,omitempty"`
// DefaultOwners are the default owners for all imported access lists.
DefaultOwners []string `yaml:"default_owners,omitempty"`
// GroupFilters are filters for which Okta groups to synchronize as access lists.
// These are globs/regexes.
GroupFilters []string `yaml:"group_filters,omitempty"`
// AppFilters are filters for which Okta applications to synchronize as access lists.
// These are globs/regexes.
AppFilters []string `yaml:"app_filters,omitempty"`
}
func (o *OktaSync) SyncAccessLists() bool {
if o.SyncAccessListsFlag == "" {
return false
}
enabled, _ := apiutils.ParseBool(o.SyncAccessListsFlag)
return enabled
}
func (o *OktaSync) Parse() (*servicecfg.OktaSyncSettings, error) {
enabled := o.SyncAccessLists()
if enabled && len(o.DefaultOwners) == 0 {
return nil, trace.BadParameter("default owners must be set when access list import is enabled")
}
for _, filter := range o.GroupFilters {
_, err := utils.CompileExpression(filter)
if err != nil {
return nil, trace.Wrap(err, "error parsing group filter: %s", filter)
}
}
for _, filter := range o.AppFilters {
_, err := utils.CompileExpression(filter)
if err != nil {
return nil, trace.Wrap(err, "error parsing app filter: %s", filter)
}
}
return &servicecfg.OktaSyncSettings{
AppGroupSyncPeriod: o.AppGroupSyncPeriod,
SyncAccessLists: o.SyncAccessLists(),
DefaultOwners: o.DefaultOwners,
GroupFilters: o.GroupFilters,
AppFilters: o.AppFilters,
}, nil
}
// JamfService is the yaml representation of jamf_service.
// Corresponds to [types.JamfSpecV1].
type JamfService struct {
Service `yaml:",inline"`
// Name is the name of the sync device source.
Name string `yaml:"name,omitempty"`
// SyncDelay is the initial sync delay.
// Zero means "server default", negative means "immediate".
SyncDelay time.Duration `yaml:"sync_delay,omitempty"`
// ExitOnSync tells the service to exit immediately after the first sync.
ExitOnSync bool `yaml:"exit_on_sync,omitempty"`
// APIEndpoint is the Jamf Pro API endpoint.
// Example: "https://yourtenant.jamfcloud.com/api".
APIEndpoint string `yaml:"api_endpoint,omitempty"`
// Username is the Jamf Pro API username.
Username string `yaml:"username,omitempty"`
// PasswordFile is a file containing the Jamf Pro API password.
// A single trailing newline is trimmed, anything else is taken literally.
PasswordFile string `yaml:"password_file,omitempty"`
// ClientID is the Jamf API Client ID.
ClientID string `yaml:"client_id,omitempty"`
// ClientSecretFile is a file containing the Jamf API client secret.
// A single trailing newline is trimmed, anything else is taken literally.
ClientSecretFile string `yaml:"client_secret_file,omitempty"`
// Inventory are the entries for inventory sync.
Inventory []*JamfInventoryEntry `yaml:"inventory,omitempty"`
}
// JamfInventoryEntry is the yaml representation of a jamf_service.inventory
// entry.
// Corresponds to [types.JamfInventoryEntry].
type JamfInventoryEntry struct {
// FilterRSQL is a Jamf Pro API RSQL filter string.
FilterRSQL string `yaml:"filter_rsql,omitempty"`
// SyncPeriodPartial is the period for PARTIAL syncs.
// Zero means "server default", negative means "disabled".
SyncPeriodPartial time.Duration `yaml:"sync_period_partial,omitempty"`
// SyncPeriodFull is the period for FULL syncs.
// Zero means "server default", negative means "disabled".
SyncPeriodFull time.Duration `yaml:"sync_period_full,omitempty"`
// OnMissing is the trigger for devices missing from the MDM inventory view.
// See [types.JamfInventoryEntry.OnMissing].
OnMissing string `yaml:"on_missing,omitempty"`
// Custom page size for inventory queries.
// A server default is used if zeroed or negative.
PageSize int32 `yaml:"page_size,omitempty"`
}
func (j *JamfService) toJamfSpecV1() (*types.JamfSpecV1, error) {
switch {
case j == nil:
return nil, trace.BadParameter("jamf_service is nil")
case j.ListenAddress != "":
return nil, trace.BadParameter("jamf listen_addr not supported")
}
// Assemble spec.
inventory := make([]*types.JamfInventoryEntry, len(j.Inventory))
for i, e := range j.Inventory {
inventory[i] = &types.JamfInventoryEntry{
FilterRsql: e.FilterRSQL,
SyncPeriodPartial: types.Duration(e.SyncPeriodPartial),
SyncPeriodFull: types.Duration(e.SyncPeriodFull),
OnMissing: e.OnMissing,
PageSize: e.PageSize,
}
}
spec := &types.JamfSpecV1{
Enabled: j.Enabled(),
Name: j.Name,
SyncDelay: types.Duration(j.SyncDelay),
ApiEndpoint: j.APIEndpoint,
Inventory: inventory,
}
// Validate.
if err := types.ValidateJamfSpecV1(spec); err != nil {
return nil, trace.BadParameter("jamf_service %v", err)
}
return spec, nil
}
func (j *JamfService) readJamfCredentials() (*servicecfg.JamfCredentials, error) {
password, err := readJamfPasswordFile(j.PasswordFile, "password_file")
if err != nil {
return nil, trace.Wrap(err)
}
clientSecret, err := readJamfPasswordFile(j.ClientSecretFile, "client_secret_file")
if err != nil {
return nil, trace.Wrap(err)
}
creds := &servicecfg.JamfCredentials{
Username: j.Username,
Password: password,
ClientID: j.ClientID,
ClientSecret: clientSecret,
}
// Validate.
if err := servicecfg.ValidateJamfCredentials(creds); err != nil {
return nil, trace.BadParameter("jamf_service %v", err)
}
return creds, nil
}
func readJamfPasswordFile(path, key string) (string, error) {
if path == "" {
return "", nil
}
pwdBytes, err := os.ReadFile(path)
if err != nil {
return "", trace.BadParameter("jamf %v: %v", key, err)
}
pwd := string(pwdBytes)
if pwd == "" {
return "", trace.BadParameter("jamf %v is empty", key)
}
// Trim exactly one trailing \n, if present.
if l := len(pwd); pwd[l-1] == '\n' {
pwd = pwd[:l-1]
}
return pwd, nil
}