Files
teleport/lib/config/fileconf.go
T
Zac Bergquist 2ae9d770fc Refactor tctl's dependencies (#22693)
* Move configuration from lib/service to lib/service/servicecfg

The new servicecfg package will hold only configuration for services.
This will allow other packages (like tctl and tsh) to depend on
servicecfg without pulling in all of lib/service (which has a number
of platform-specific details).

This is the first step towards being able to build tctl for Windows.

* Move PAM and BPF config into servicecfg

This breaks a compile-time dependency on BPF/PAM for tctl.
2023-03-09 17:48:36 +00:00

2289 lines
80 KiB
Go

/*
Copyright 2015-2021 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"bytes"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"io"
"io/fs"
"net"
"net/url"
"os"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/coreos/go-oidc/oauth2"
"github.com/gravitational/trace"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/acme"
"golang.org/x/crypto/ssh"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"gopkg.in/yaml.v2"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/installers"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/tlsutils"
"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"`
// 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"`
}
// ReadFromFile reads Teleport configuration from a file. Currently only YAML
// format is supported
func ReadFromFile(filePath string) (*FileConfig, error) {
f, err := os.Open(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.Replace(err.Error(), "\n", "", -1))
}
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
}
// 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
}
joinMethod := flags.JoinMethod
if joinMethod == "" && flags.AuthToken != "" {
joinMethod = string(types.JoinMethodToken)
}
g.JoinParams = JoinParams{
TokenName: flags.AuthToken,
Method: types.JoinMethod(joinMethod),
}
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 makeSampleSSHConfig(conf *servicecfg.Config, flags SampleFlags, enabled bool) (SSH, error) {
var s SSH
if enabled {
s.EnabledFlag = "yes"
s.ListenAddress = conf.SSH.Addr.Addr
s.Commands = []CommandLabel{
{
Name: "hostname",
Command: []string{"hostname"},
Period: time.Minute,
},
}
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
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)
}
}
if err := checkAndSetDefaultsForAWSMatchers(conf.Discovery.AWSMatchers); err != nil {
return trace.Wrap(err)
}
if err := checkAndSetDefaultsForAzureMatchers(conf.Discovery.AzureMatchers); err != nil {
return trace.Wrap(err)
}
if err := checkAndSetDefaultsForGCPMatchers(conf.Discovery.GCPMatchers); err != nil {
return trace.Wrap(err)
}
return nil
}
// awsRegions returns the list of all regions available on every aws partition.
// It is used to validate the AWSMatcher.Regions values.
func awsRegions() []string {
var regions []string
partitions := endpoints.DefaultPartitions()
for _, partition := range partitions {
regions = append(regions, maps.Keys(partition.Regions())...)
}
return regions
}
// checkAndSetDefaultsForAWSMatchers sets the default values for discovery AWS matchers
// and validates the provided types.
func checkAndSetDefaultsForAWSMatchers(matcherInput []AWSMatcher) error {
regions := awsRegions()
for i := range matcherInput {
matcher := &matcherInput[i]
for _, matcherType := range matcher.Types {
if !slices.Contains(services.SupportedAWSMatchers, matcherType) {
return trace.BadParameter("discovery service type does not support %q, supported resource types are: %v",
matcherType, services.SupportedAWSMatchers)
}
}
if len(matcher.Regions) == 0 {
return trace.BadParameter("discovery service requires at least one region; supported regions are: %v",
regions)
}
for _, region := range matcher.Regions {
if !slices.Contains(regions, region) {
return trace.BadParameter("discovery service does not support region %q; supported regions are: %v",
region, regions)
}
}
if matcher.Tags == nil || len(matcher.Tags) == 0 {
matcher.Tags = map[string]apiutils.Strings{types.Wildcard: {types.Wildcard}}
}
var installParams services.InstallerParams
var err error
if matcher.InstallParams == nil {
matcher.InstallParams = &InstallParams{
JoinParams: JoinParams{
TokenName: defaults.IAMInviteTokenName,
Method: types.JoinMethodIAM,
},
ScriptName: installers.InstallerScriptName,
InstallTeleport: "",
SSHDConfig: defaults.SSHDConfigPath,
}
installParams, err = matcher.InstallParams.Parse()
if err != nil {
return trace.Wrap(err)
}
} else {
if method := matcher.InstallParams.JoinParams.Method; method == "" {
matcher.InstallParams.JoinParams.Method = types.JoinMethodIAM
} else if method != types.JoinMethodIAM {
return trace.BadParameter("only IAM joining is supported for EC2 auto-discovery")
}
if matcher.InstallParams.JoinParams.TokenName == "" {
matcher.InstallParams.JoinParams.TokenName = defaults.IAMInviteTokenName
}
if matcher.InstallParams.SSHDConfig == "" {
matcher.InstallParams.SSHDConfig = defaults.SSHDConfigPath
}
installParams, err = matcher.InstallParams.Parse()
if err != nil {
return trace.Wrap(err)
}
if installer := matcher.InstallParams.ScriptName; installer == "" {
if installParams.InstallTeleport {
matcher.InstallParams.ScriptName = installers.InstallerScriptName
} else {
matcher.InstallParams.ScriptName = installers.InstallerScriptNameAgentless
}
}
}
if matcher.SSM.DocumentName == "" {
if installParams.InstallTeleport {
matcher.SSM.DocumentName = defaults.AWSInstallerDocument
} else {
matcher.SSM.DocumentName = defaults.AWSAgentlessInstallerDocument
}
}
}
return nil
}
// checkAndSetDefaultsForAzureMatchers sets the default values for discovery Azure matchers
// and validates the provided types.
func checkAndSetDefaultsForAzureMatchers(matcherInput []AzureMatcher) error {
for i := range matcherInput {
matcher := &matcherInput[i]
if len(matcher.Types) == 0 {
return trace.BadParameter("At least one Azure discovery service type must be specified, the supported resource types are: %v",
services.SupportedAzureMatchers)
}
for _, matcherType := range matcher.Types {
if !slices.Contains(services.SupportedAzureMatchers, matcherType) {
return trace.BadParameter("Azure discovery service type does not support %q resource type; supported resource types are: %v",
matcherType, services.SupportedAzureMatchers)
}
}
if slices.Contains(matcher.Types, services.AzureMatcherVM) {
if err := checkAndSetDefaultsForAzureInstaller(matcher); err != nil {
return trace.Wrap(err)
}
}
if slices.Contains(matcher.Regions, types.Wildcard) || len(matcher.Regions) == 0 {
matcher.Regions = []string{types.Wildcard}
}
if slices.Contains(matcher.Subscriptions, types.Wildcard) || len(matcher.Subscriptions) == 0 {
matcher.Subscriptions = []string{types.Wildcard}
}
if slices.Contains(matcher.ResourceGroups, types.Wildcard) || len(matcher.ResourceGroups) == 0 {
matcher.ResourceGroups = []string{types.Wildcard}
}
if len(matcher.ResourceTags) == 0 {
matcher.ResourceTags = map[string]apiutils.Strings{
types.Wildcard: {types.Wildcard},
}
}
}
return nil
}
func checkAndSetDefaultsForAzureInstaller(matcher *AzureMatcher) error {
if matcher.InstallParams == nil {
matcher.InstallParams = &InstallParams{
JoinParams: JoinParams{
TokenName: defaults.AzureInviteTokenName,
Method: types.JoinMethodAzure,
},
ScriptName: installers.InstallerScriptName,
}
return nil
}
switch matcher.InstallParams.JoinParams.Method {
case types.JoinMethodAzure, "":
matcher.InstallParams.JoinParams.Method = types.JoinMethodAzure
default:
return trace.BadParameter("only Azure joining is supported for Azure auto-discovery")
}
if token := matcher.InstallParams.JoinParams.TokenName; token == "" {
matcher.InstallParams.JoinParams.TokenName = defaults.AzureInviteTokenName
}
if installer := matcher.InstallParams.ScriptName; installer == "" {
matcher.InstallParams.ScriptName = installers.InstallerScriptName
}
return nil
}
// checkAndSetDefaultsForGCPMatchers sets the default values for GCP matchers
// and validates the provided types.
func checkAndSetDefaultsForGCPMatchers(matcherInput []GCPMatcher) error {
for i := range matcherInput {
matcher := &matcherInput[i]
if len(matcher.Types) == 0 {
return trace.BadParameter("At least one GCP discovery service type must be specified, the supported resource types are: %v",
services.SupportedGCPMatchers)
}
for _, matcherType := range matcher.Types {
if !slices.Contains(services.SupportedGCPMatchers, matcherType) {
return trace.BadParameter("GCP discovery service type does not support %q resource type; supported resource types are: %v",
matcherType, services.SupportedGCPMatchers)
}
}
if slices.Contains(matcher.Locations, types.Wildcard) || len(matcher.Locations) == 0 {
matcher.Locations = []string{types.Wildcard}
}
if slices.Contains(matcher.ProjectIDs, types.Wildcard) {
return trace.BadParameter("GCP discovery service project_ids does not support wildcards; please specify at least one value in project_ids.")
}
if len(matcher.ProjectIDs) == 0 {
return trace.BadParameter("GCP discovery service project_ids does cannot be empty; please specify at least one value in project_ids.")
}
if len(matcher.Tags) == 0 {
matcher.Tags = map[string]apiutils.Strings{
types.Wildcard: {types.Wildcard},
}
}
}
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"`
MaxUsers int `yaml:"max_users"`
Rates []ConnectionRate `yaml:"rates,omitempty"`
}
// 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 {
if _, ok := err.(*yaml.TypeError); !ok {
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"`
}
// 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(),
}
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 enables support for HAProxy proxy protocol version 1 when it is turned 'on'.
// Verify whether the service is in front of a trusted load balancer.
// The default value is 'on'.
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"`
// 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"`
}
// 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
}
// 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"`
}
// 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"`
}
// 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"`
}
// 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"`
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"`
}
// 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)
}
}
return types.NewAuthPreferenceFromConfigFile(types.AuthPreferenceSpecV2{
Type: a.Type,
SecondFactor: a.SecondFactor,
ConnectorName: a.ConnectorName,
U2F: u,
Webauthn: w,
RequireMFAType: a.RequireMFAType,
LockingMode: a.LockingMode,
AllowLocalAuth: a.LocalAuth,
AllowPasswordless: a.Passwordless,
AllowHeadless: a.Headless,
DeviceTrust: dt,
})
}
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 := getAttestationPEMs(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 := getAttestationPEMs(w.AttestationAllowedCAs)
if err != nil {
return nil, trace.BadParameter("webauthn.attestation_allowed_cas: %v", err)
}
deniedCAs, err := getAttestationPEMs(w.AttestationDeniedCAs)
if err != nil {
return nil, trace.BadParameter("webauthn.attestation_denied_cas: %v", err)
}
if w.Disabled {
log.Warnf(`` +
`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/access-controls/guides/webauthn/`)
}
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 getAttestationPEMs(certOrPaths []string) ([]string, error) {
res := make([]string, len(certOrPaths))
for i, certOrPath := range certOrPaths {
pem, err := getAttestationPEM(certOrPath)
if err != nil {
return nil, err
}
res[i] = pem
}
return res, nil
}
func getAttestationPEM(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 (%v) and can't be read as a file (%v)", 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: %v", 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"`
}
func (dt *DeviceTrust) Parse() (*types.DeviceTrust, error) {
return &types.DeviceTrust{
Mode: dt.Mode,
}, 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 {
return out, trace.BadParameter("when plugin runtime is enabled, at least one plugin provider must be specified")
}
slack, err := p.Slack.Parse()
if err != nil {
return out, trace.Wrap(err)
}
out.Slack = 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() (*oauth2.ClientCredentials, 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 &oauth2.ClientCredentials{
ID: clientID,
Secret: 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"`
}
// 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"`
}
// GCPMatcher matches GCP resources.
type GCPMatcher struct {
// Types are GKE resource types to match: "gke".
Types []string `yaml:"types,omitempty"`
// Locations are GKE locations to search resources for.
Locations []string `yaml:"locations,omitempty"`
// Tags are GCP labels to match.
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"`
}
// 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"`
}
// 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,
}
}
// 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"`
}
// Parse will parse the enhanced session recording configuration.
func (r *RestrictedSession) Parse() (*servicecfg.RestrictedSessionConfig, error) {
enabled, err := apiutils.ParseBool(r.Enabled)
if err != nil {
return nil, trace.Wrap(err)
}
return &servicecfg.RestrictedSessionConfig{
Enabled: enabled,
EventsBufferSize: r.EventsBufferSize,
}, nil
}
// 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"`
}
// 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"`
// 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"`
}
// 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. Used ony in Azure.
PublicProxyAddr string `yaml:"public_proxy_addr,omitempty"`
}
func (ip *InstallParams) Parse() (services.InstallerParams, error) {
install := services.InstallerParams{
JoinMethod: ip.JoinParams.Method,
JoinToken: ip.JoinParams.TokenName,
ScriptName: ip.ScriptName,
InstallTeleport: true,
SSHDConfig: ip.SSHDConfig,
}
if ip.InstallTeleport == "" {
return install, nil
}
var err error
install.InstallTeleport, err = apiutils.ParseBool(ip.InstallTeleport)
if err != nil {
return services.InstallerParams{}, 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"`
}
// 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"`
}
// 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"`
}
// 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"`
}
// 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"`
}
// 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"`
}
// 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"`
// 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"`
}
// 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"`
}
// 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"`
}
// 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"`
}
// 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"`
// 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"`
// 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"`
}
// 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"`
}
// 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
}
// 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"`
// Discovery configures desktop discovery via LDAP.
Discovery LDAPDiscoveryConfig `yaml:"discovery,omitempty"`
// Hosts 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).
Hosts []string `yaml:"hosts,omitempty"`
// NonADHosts is a list of standalone Windows hosts that are not
// jointed to an Active Directory domain.
NonADHosts []string `yaml:"non_ad_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"`
}
// Check checks whether the WindowsDesktopService is valid or not
func (wds *WindowsDesktopService) Check() error {
if len(wds.Hosts) > 0 && wds.LDAP.Addr == "" {
return trace.BadParameter("if hosts are specified in the windows_desktop_service, " +
"the ldap configuration for their corresponding Active Directory domain controller 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 a 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"`
}
// 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"`
}
// 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
}