mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 18:23:25 +00:00
5739b63e51
* mfa: add new second_factor options "on" and "optional" "on" means that 2FA is required for all users, either TOTP or U2F. "optional" means that 2FA is supported for all users, but not required. Only users with MFA devices registered will be prompted for 2FA on login. The login with both supported methods is using the same API as the U2F login. It just now supports TOTP in addition. The API endpoints are still named after "u2f", I'll rename those in a future PR (in a backwards-compatible way). * Apply suggestions from code review Co-authored-by: Gus Luxton <gus@gravitational.com> Co-authored-by: a-palchikov <deemok@gmail.com> * Address reivew feedback Co-authored-by: Gus Luxton <gus@gravitational.com> Co-authored-by: a-palchikov <deemok@gmail.com>
1271 lines
42 KiB
Go
1271 lines
42 KiB
Go
/*
|
|
Copyright 2015-2018 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"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/acme"
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
"github.com/gravitational/teleport"
|
|
"github.com/gravitational/teleport/api/constants"
|
|
"github.com/gravitational/teleport/api/types"
|
|
"github.com/gravitational/teleport/lib/backend"
|
|
"github.com/gravitational/teleport/lib/bpf"
|
|
"github.com/gravitational/teleport/lib/defaults"
|
|
"github.com/gravitational/teleport/lib/pam"
|
|
"github.com/gravitational/teleport/lib/service"
|
|
"github.com/gravitational/teleport/lib/services"
|
|
"github.com/gravitational/teleport/lib/utils"
|
|
|
|
"github.com/gravitational/trace"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
const (
|
|
// randomTokenLenBytes is the length of random token generated for the example config
|
|
randomTokenLenBytes = 24
|
|
)
|
|
|
|
var (
|
|
// all possible valid YAML config keys
|
|
// true = has sub-keys
|
|
// false = does not have sub-keys (a leaf)
|
|
validKeys = map[string]bool{
|
|
"proxy_protocol": false,
|
|
"namespace": true,
|
|
"cluster_name": true,
|
|
"trusted_clusters": true,
|
|
"pid_file": true,
|
|
"cert_file": true,
|
|
"private_key_file": true,
|
|
"cert": true,
|
|
"private_key": true,
|
|
"checking_keys": true,
|
|
"checking_key_files": true,
|
|
"signing_keys": true,
|
|
"signing_key_files": true,
|
|
"allowed_logins": true,
|
|
"teleport": true,
|
|
"enabled": true,
|
|
"ssh_service": true,
|
|
"proxy_service": true,
|
|
"auth_service": true,
|
|
"kubernetes": true,
|
|
"kubeconfig_file": true,
|
|
"auth_token": true,
|
|
"auth_servers": true,
|
|
"domain_name": true,
|
|
"storage": false,
|
|
"nodename": true,
|
|
"log": true,
|
|
"period": true,
|
|
"connection_limits": true,
|
|
"max_connections": true,
|
|
"max_users": true,
|
|
"rates": true,
|
|
"commands": true,
|
|
"labels": false,
|
|
"output": true,
|
|
"severity": true,
|
|
"role": true,
|
|
"name": true,
|
|
"type": true,
|
|
"data_dir": true,
|
|
"web_listen_addr": true,
|
|
"tunnel_listen_addr": true,
|
|
"ssh_listen_addr": true,
|
|
"listen_addr": true,
|
|
"ca_cert_file": false,
|
|
"https_key_file": true,
|
|
"https_cert_file": true,
|
|
"advertise_ip": true,
|
|
"authorities": true,
|
|
"keys": true,
|
|
"reverse_tunnels": true,
|
|
"addresses": true,
|
|
"oidc_connectors": true,
|
|
"id": true,
|
|
"issuer_url": true,
|
|
"client_id": true,
|
|
"client_secret": true,
|
|
"redirect_url": true,
|
|
"acr_values": true,
|
|
"provider": true,
|
|
"tokens": true,
|
|
"region": true,
|
|
"table_name": true,
|
|
"access_key": true,
|
|
"secret_key": true,
|
|
"u2f": true,
|
|
"app_id": true,
|
|
"facets": true,
|
|
"authentication": true,
|
|
"second_factor": false,
|
|
"oidc": true,
|
|
"display": false,
|
|
"scope": false,
|
|
"claims_to_roles": true,
|
|
"dynamic_config": false,
|
|
"seed_config": false,
|
|
"public_addr": false,
|
|
"ssh_public_addr": false,
|
|
"tunnel_public_addr": false,
|
|
"cache": true,
|
|
"ttl": false,
|
|
"issuer": false,
|
|
"permit_user_env": false,
|
|
"ciphers": false,
|
|
"kex_algos": false,
|
|
"mac_algos": false,
|
|
"ca_signature_algo": false,
|
|
"connector_name": false,
|
|
"session_recording": false,
|
|
"read_capacity_units": false,
|
|
"write_capacity_units": false,
|
|
"license_file": false,
|
|
"proxy_checks_host_keys": false,
|
|
"audit_table_name": false,
|
|
"audit_sessions_uri": false,
|
|
"audit_events_uri": false,
|
|
"pam": true,
|
|
"use_pam_auth": false,
|
|
"service_name": false,
|
|
"client_idle_timeout": false,
|
|
"session_control_timeout": false,
|
|
"disconnect_expired_cert": false,
|
|
"ciphersuites": false,
|
|
"ca_pin": false,
|
|
"keep_alive_interval": false,
|
|
"keep_alive_count_max": false,
|
|
"local_auth": false,
|
|
"enhanced_recording": false,
|
|
"command_buffer_size": false,
|
|
"disk_buffer_size": false,
|
|
"network_buffer_size": false,
|
|
"cgroup_path": false,
|
|
"kubernetes_service": true,
|
|
"kube_cluster_name": false,
|
|
"kube_listen_addr": false,
|
|
"app_service": true,
|
|
"db_service": true,
|
|
"protocol": false,
|
|
"uri": false,
|
|
"apps": false,
|
|
"databases": false,
|
|
"https_keypairs": true,
|
|
"key_file": false,
|
|
"insecure_skip_verify": false,
|
|
"rewrite": false,
|
|
"redirect": false,
|
|
"debug_app": false,
|
|
"acme": true,
|
|
"email": false,
|
|
"mysql_listen_addr": false,
|
|
}
|
|
)
|
|
|
|
var validCASigAlgos = []string{
|
|
ssh.SigAlgoRSA,
|
|
ssh.SigAlgoRSASHA2256,
|
|
ssh.SigAlgoRSASHA2512,
|
|
}
|
|
|
|
// FileConfig structre 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 {
|
|
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 defined database access configuration.
|
|
Databases Databases `yaml:"db_service,omitempty"`
|
|
}
|
|
|
|
type YAMLMap map[interface{}]interface{}
|
|
|
|
// 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 {
|
|
return nil, trace.Wrap(err, fmt.Sprintf("failed to open file: %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 := ioutil.ReadAll(reader)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err, "failed reading Teleport configuration")
|
|
}
|
|
var fc FileConfig
|
|
|
|
// New validation in 6.0:
|
|
//
|
|
// Try strict unmarshal first (fails if any yaml entry doesn't map to a
|
|
// FileConfig field).
|
|
//
|
|
// If strict unmarshal failed, there may be some innocent mis-placed config
|
|
// fields. Fall back to the old validation first.
|
|
//
|
|
// If the old validation fails too, then we'll report the above error
|
|
// because the config is definitely invalid.
|
|
//
|
|
// If the old validation succeeds, we'll log the above error, but won't
|
|
// enforce it yet to let users fix the problem
|
|
strictUnmarshalErr := yaml.UnmarshalStrict(bytes, &fc)
|
|
if strictUnmarshalErr == nil {
|
|
// don't start Teleport with invalid ciphers, kex algorithms, or mac algorithms.
|
|
if err = fc.CheckAndSetDefaults(); err != nil {
|
|
return nil, trace.BadParameter("failed to parse Teleport configuration: %v", err)
|
|
}
|
|
return &fc, nil
|
|
}
|
|
// Remove all newlines in the YAML error, to avoid escaping when printing.
|
|
strictUnmarshalErr = errors.New(strings.Replace(strictUnmarshalErr.Error(), "\n", "", -1))
|
|
// DELETE IN 7.0: during 6.0, users should notice any issues that passed
|
|
// old validation but not the new strict one. With 7.0, we should always
|
|
// enforce the strict validation.
|
|
if err = yaml.Unmarshal(bytes, &fc); err != nil {
|
|
return nil, trace.BadParameter("failed to parse Teleport configuration: %v", strictUnmarshalErr)
|
|
}
|
|
// don't start Teleport with invalid ciphers, kex algorithms, or mac algorithms.
|
|
err = fc.CheckAndSetDefaults()
|
|
if err != nil {
|
|
return nil, trace.BadParameter("failed to parse Teleport configuration: %v", err)
|
|
}
|
|
// now check for unknown (misspelled) config keys:
|
|
var validateKeys func(m YAMLMap) error
|
|
validateKeys = func(m YAMLMap) error {
|
|
var recursive, ok bool
|
|
var key string
|
|
for k, v := range m {
|
|
if key, ok = k.(string); ok {
|
|
if recursive, ok = validKeys[key]; !ok {
|
|
return trace.BadParameter("unrecognized configuration key: '%v'", key)
|
|
}
|
|
if recursive {
|
|
if m2, ok := v.(YAMLMap); ok {
|
|
if err := validateKeys(m2); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
// validate configuration keys:
|
|
var tmp YAMLMap
|
|
if err = yaml.Unmarshal(bytes, &tmp); err != nil {
|
|
return nil, trace.BadParameter("error parsing YAML config: %v", err)
|
|
}
|
|
if err = validateKeys(tmp); err != nil {
|
|
// Both old an new validations failed. Report the new strict validation
|
|
// error.
|
|
return nil, trace.Wrap(strictUnmarshalErr)
|
|
}
|
|
// New strict validation failed but old one succeeded. There's something
|
|
// wrong with the config, but don't prevent it from starting up.
|
|
logrus.Errorf("Teleport configuration is invalid: %v.", strictUnmarshalErr)
|
|
logrus.Error("This error will be enforced in the next Teleport release.")
|
|
// Also add a short but noticeable sleep, to nudge users to pay attention
|
|
// to logs.
|
|
time.Sleep(5 * time.Second)
|
|
return &fc, nil
|
|
}
|
|
|
|
// MakeSampleFileConfig returns a sample config structure populated by defaults,
|
|
// useful to generate sample configuration files
|
|
func MakeSampleFileConfig() (fc *FileConfig, err error) {
|
|
conf := service.MakeDefaultConfig()
|
|
|
|
// generate a secure random token
|
|
randomJoinToken, err := utils.CryptoRandomHex(randomTokenLenBytes)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
// sample global config:
|
|
var g Global
|
|
g.NodeName = conf.Hostname
|
|
g.AuthToken = randomJoinToken
|
|
g.CAPin = "sha256:ca-pin-hash-goes-here"
|
|
g.Logger.Output = "stderr"
|
|
g.Logger.Severity = "INFO"
|
|
g.AuthServers = []string{fmt.Sprintf("%s:%d", defaults.Localhost, defaults.AuthListenPort)}
|
|
g.DataDir = defaults.DataDir
|
|
|
|
// sample SSH config:
|
|
var s SSH
|
|
s.EnabledFlag = "yes"
|
|
s.ListenAddress = conf.SSH.Addr.Addr
|
|
s.Commands = []CommandLabel{
|
|
{
|
|
Name: "hostname",
|
|
Command: []string{"hostname"},
|
|
Period: time.Minute,
|
|
},
|
|
{
|
|
Name: "arch",
|
|
Command: []string{"uname", "-p"},
|
|
Period: time.Hour,
|
|
},
|
|
}
|
|
s.Labels = map[string]string{
|
|
"env": "staging",
|
|
}
|
|
|
|
// sample Auth config:
|
|
var a Auth
|
|
a.ListenAddress = conf.Auth.SSHAddr.Addr
|
|
a.EnabledFlag = "yes"
|
|
a.StaticTokens = []StaticToken{StaticToken(fmt.Sprintf("proxy,node:%s", randomJoinToken))}
|
|
a.LicenseFile = "/path/to/license-if-using-teleport-enterprise.pem"
|
|
|
|
// sample proxy config:
|
|
var p Proxy
|
|
p.EnabledFlag = "yes"
|
|
p.ListenAddress = conf.Proxy.SSHAddr.Addr
|
|
p.WebAddr = conf.Proxy.WebAddr.Addr
|
|
p.TunAddr = conf.Proxy.ReverseTunnelListenAddr.Addr
|
|
|
|
fc = &FileConfig{
|
|
Global: g,
|
|
Proxy: p,
|
|
SSH: s,
|
|
Auth: a,
|
|
}
|
|
return fc, nil
|
|
}
|
|
|
|
// 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
|
|
|
|
var sc ssh.Config
|
|
sc.SetDefaults()
|
|
|
|
for _, c := range conf.Ciphers {
|
|
if !utils.SliceContainsStr(sc.Ciphers, c) {
|
|
return trace.BadParameter("cipher algorithm %q is not supported; supported algorithms: %q", c, sc.Ciphers)
|
|
}
|
|
}
|
|
for _, k := range conf.KEXAlgorithms {
|
|
if !utils.SliceContainsStr(sc.KeyExchanges, k) {
|
|
return trace.BadParameter("KEX algorithm %q is not supported; supported algorithms: %q", k, sc.KeyExchanges)
|
|
}
|
|
}
|
|
for _, m := range conf.MACAlgorithms {
|
|
if !utils.SliceContainsStr(sc.MACs, m) {
|
|
return trace.BadParameter("MAC algorithm %q is not supported; supported algorithms: %q", m, sc.MACs)
|
|
}
|
|
}
|
|
if conf.CASignatureAlgorithm != nil && !utils.SliceContainsStr(validCASigAlgos, *conf.CASignatureAlgorithm) {
|
|
return trace.BadParameter("CA signature algorithm %q is not supported; supported algorithms: %q", *conf.CASignatureAlgorithm, validCASigAlgos)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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"`
|
|
}
|
|
|
|
// 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 valus are "error", "info", "warn"
|
|
Severity string `yaml:"severity,omitempty"`
|
|
}
|
|
|
|
// 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"`
|
|
AuthToken string `yaml:"auth_token,omitempty"`
|
|
AuthServers []string `yaml:"auth_servers,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"`
|
|
SeedConfig *bool `yaml:"seed_config,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 an SSH Certificate Authority (CA) signature
|
|
// algorithm that the server uses for signing user and host certificates.
|
|
// If omitted, the default will be used.
|
|
CASignatureAlgorithm *string `yaml:"ca_signature_algo,omitempty"`
|
|
|
|
// CAPin is the SKPI hash of the CA used to verify the Auth Server.
|
|
CAPin string `yaml:"ca_pin"`
|
|
}
|
|
|
|
// 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"`
|
|
}
|
|
|
|
func isNever(v string) bool {
|
|
switch v {
|
|
case "never", "no", "0":
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Enabled determines if a given "_service" section has been set to 'true'
|
|
func (c *CachePolicy) Enabled() bool {
|
|
if c.EnabledFlag == "" {
|
|
return true
|
|
}
|
|
enabled, _ := utils.ParseBool(c.EnabledFlag)
|
|
return enabled
|
|
}
|
|
|
|
// NeverExpires returns if cache never expires by itself
|
|
func (c *CachePolicy) NeverExpires() bool {
|
|
return isNever(c.TTL)
|
|
}
|
|
|
|
// Parse parses cache policy from Teleport config
|
|
func (c *CachePolicy) Parse() (*service.CachePolicy, error) {
|
|
out := service.CachePolicy{
|
|
Type: c.Type,
|
|
Enabled: c.Enabled(),
|
|
NeverExpires: c.NeverExpires(),
|
|
}
|
|
if !out.NeverExpires {
|
|
var err error
|
|
if c.TTL != "" {
|
|
out.TTL, err = time.ParseDuration(c.TTL)
|
|
if err != nil {
|
|
return nil, trace.BadParameter("cache.ttl invalid duration: %v, accepted format '10h'", c.TTL)
|
|
}
|
|
}
|
|
}
|
|
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 := utils.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 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"`
|
|
|
|
// 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 exmple: "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, proxy, 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 string `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:
|
|
// Authorities : 3rd party certificate authorities (CAs) this auth service trusts.
|
|
Authorities []Authority `yaml:"authorities,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"`
|
|
|
|
// TrustedClustersFile is a file path to a file containing public CA keys
|
|
// of clusters we trust. One key per line, those starting with '#' are comments
|
|
// Deprecated: Remove in Teleport 2.4.1.
|
|
TrustedClusters []TrustedCluster `yaml:"trusted_clusters,omitempty"`
|
|
|
|
// OIDCConnectors is a list of trusted OpenID Connect Identity providers
|
|
// Deprecated: Remove in Teleport 2.4.1.
|
|
OIDCConnectors []OIDCConnector `yaml:"oidc_connectors,omitempty"`
|
|
|
|
// Configuration for "universal 2nd factor"
|
|
// Deprecated: Remove in Teleport 2.4.1.
|
|
U2F U2F `yaml:"u2f,omitempty"`
|
|
|
|
// DynamicConfig determines when file configuration is pushed to the backend. Setting
|
|
// it here overrides defaults.
|
|
// Deprecated: Remove in Teleport 2.4.1.
|
|
DynamicConfig *bool `yaml:"dynamic_config,omitempty"`
|
|
|
|
// PublicAddr sets SSH host principals and TLS DNS names to auth
|
|
// server certificates
|
|
PublicAddr utils.Strings `yaml:"public_addr,omitempty"`
|
|
|
|
// ClientIdleTimeout sets global cluster default setting for client idle timeouts
|
|
ClientIdleTimeout services.Duration `yaml:"client_idle_timeout,omitempty"`
|
|
|
|
// DisconnectExpiredCert provides disconnect expired certificate setting -
|
|
// if true, connections with expired client certificates will get disconnected
|
|
DisconnectExpiredCert services.Bool `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 services.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 services.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"`
|
|
}
|
|
|
|
// 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() (services.ClusterName, error) {
|
|
if string(c) == "" {
|
|
return nil, nil
|
|
}
|
|
return services.NewClusterName(services.ClusterNameSpecV2{
|
|
ClusterName: string(c),
|
|
})
|
|
}
|
|
|
|
type StaticTokens []StaticToken
|
|
|
|
func (t StaticTokens) Parse() (services.StaticTokens, error) {
|
|
staticTokens := []services.ProvisionTokenV1{}
|
|
|
|
for _, token := range t {
|
|
st, err := token.Parse()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
staticTokens = append(staticTokens, *st)
|
|
}
|
|
|
|
return services.NewStaticTokens(services.StaticTokensSpecV2{
|
|
StaticTokens: staticTokens,
|
|
})
|
|
}
|
|
|
|
type StaticToken string
|
|
|
|
// Parse is applied to a string in "role,role,role:token" format. It breaks it
|
|
// apart and constructs a services.ProvisionToken which contains the token,
|
|
// role, and expiry (infinite).
|
|
func (t StaticToken) Parse() (*services.ProvisionTokenV1, error) {
|
|
parts := strings.Split(string(t), ":")
|
|
if len(parts) != 2 {
|
|
return nil, trace.BadParameter("invalid static token spec: %q", t)
|
|
}
|
|
|
|
roles, err := teleport.ParseRoles(parts[0])
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
token, err := utils.ReadToken(parts[1])
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return &services.ProvisionTokenV1{
|
|
Token: token,
|
|
Roles: roles,
|
|
Expires: time.Unix(0, 0).UTC(),
|
|
}, 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"`
|
|
|
|
// LocalAuth controls if local authentication is allowed.
|
|
LocalAuth *services.Bool `yaml:"local_auth"`
|
|
}
|
|
|
|
// Parse returns a services.AuthPreference (type, second factor, U2F).
|
|
func (a *AuthenticationConfig) Parse() (services.AuthPreference, error) {
|
|
var err error
|
|
|
|
var u services.U2F
|
|
if a.U2F != nil {
|
|
u = a.U2F.Parse()
|
|
}
|
|
|
|
ap, err := services.NewAuthPreference(services.AuthPreferenceSpecV2{
|
|
Type: a.Type,
|
|
SecondFactor: a.SecondFactor,
|
|
ConnectorName: a.ConnectorName,
|
|
U2F: &u,
|
|
})
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
// check to make sure the configuration is valid
|
|
err = ap.CheckAndSetDefaults()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return ap, nil
|
|
}
|
|
|
|
type UniversalSecondFactor struct {
|
|
AppID string `yaml:"app_id"`
|
|
Facets []string `yaml:"facets"`
|
|
}
|
|
|
|
func (u *UniversalSecondFactor) Parse() services.U2F {
|
|
return services.U2F{
|
|
AppID: u.AppID,
|
|
Facets: u.Facets,
|
|
}
|
|
}
|
|
|
|
// 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 utils.Strings `yaml:"public_addr,omitempty"`
|
|
|
|
// BPF is used to configure BPF-based auditing for this node.
|
|
BPF *BPF `yaml:"enhanced_recording,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"`
|
|
}
|
|
|
|
// Parse returns a parsed pam.Config.
|
|
func (p *PAM) Parse() *pam.Config {
|
|
serviceName := p.ServiceName
|
|
if serviceName == "" {
|
|
serviceName = defaults.ServiceName
|
|
}
|
|
enabled, _ := utils.ParseBool(p.Enabled)
|
|
return &pam.Config{
|
|
Enabled: enabled,
|
|
ServiceName: serviceName,
|
|
UsePAMAuth: p.UsePAMAuth,
|
|
}
|
|
}
|
|
|
|
// 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() *bpf.Config {
|
|
enabled, _ := utils.ParseBool(b.Enabled)
|
|
return &bpf.Config{
|
|
Enabled: enabled,
|
|
CommandBufferSize: b.CommandBufferSize,
|
|
DiskBufferSize: b.DiskBufferSize,
|
|
NetworkBufferSize: b.NetworkBufferSize,
|
|
CgroupPath: b.CgroupPath,
|
|
}
|
|
}
|
|
|
|
// 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"`
|
|
}
|
|
|
|
// 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.
|
|
CACertFile string `yaml:"ca_cert_file,omitempty"`
|
|
// 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 RDS/Aurora databases.
|
|
AWS DatabaseAWS `yaml:"aws"`
|
|
}
|
|
|
|
// 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"`
|
|
}
|
|
|
|
// 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"`
|
|
}
|
|
|
|
// App is the specific application that will be proxied by the application
|
|
// service.
|
|
type App struct {
|
|
// Name of the application.
|
|
Name string `yaml:"name"`
|
|
|
|
// 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"`
|
|
|
|
// Labels is a map of static labels to apply to this application.
|
|
StaticLabels map[string]string `yaml:"labels,omitempty"`
|
|
|
|
// Commands is a list of 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"`
|
|
}
|
|
|
|
// 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"`
|
|
}
|
|
|
|
// 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"`
|
|
// 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"`
|
|
|
|
// 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 utils.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 utils.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 utils.Strings `yaml:"tunnel_public_addr,omitempty"`
|
|
|
|
// KeyPairs is a list of x509 key pairs the proxy will load.
|
|
KeyPairs []KeyPair `yaml:"https_keypairs"`
|
|
|
|
// ACME configures ACME protocol support
|
|
ACME ACME `yaml:"acme"`
|
|
|
|
// MySQLAddr is MySQL proxy listen address.
|
|
MySQLAddr string `yaml:"mysql_listen_addr,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() (*service.ACME, error) {
|
|
// ACME is disabled by default
|
|
out := service.ACME{}
|
|
if a.EnabledFlag == "" {
|
|
return &out, nil
|
|
}
|
|
|
|
var err error
|
|
out.Enabled, err = utils.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
|
|
}
|
|
|
|
// 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 utils.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 utils.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"`
|
|
// Static and dynamic labels for RBAC on kubernetes clusters.
|
|
StaticLabels map[string]string `yaml:"labels,omitempty"`
|
|
DynamicLabels []CommandLabel `yaml:"commands,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() (services.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 := services.NewReverseTunnel(t.DomainName, t.Addresses)
|
|
if err := services.ValidateReverseTunnel(out); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// Authority is a host or user certificate authority that
|
|
// can check and if it has private key stored as well, sign it too
|
|
type Authority struct {
|
|
// Type is either user or host certificate authority
|
|
Type services.CertAuthType `yaml:"type"`
|
|
// DomainName identifies domain name this authority serves,
|
|
// for host authorities that means base hostname of all servers,
|
|
// for user authorities that means organization name
|
|
DomainName string `yaml:"domain_name"`
|
|
// Checkers is a list of SSH public keys that can be used to check
|
|
// certificate signatures in OpenSSH authorized keys format
|
|
CheckingKeys []string `yaml:"checking_keys"`
|
|
// CheckingKeyFiles is a list of files
|
|
CheckingKeyFiles []string `yaml:"checking_key_files"`
|
|
// SigningKeys is a list of PEM-encoded private keys used for signing
|
|
SigningKeys []string `yaml:"signing_keys"`
|
|
// SigningKeyFiles is a list of paths to PEM encoded private keys used for signing
|
|
SigningKeyFiles []string `yaml:"signing_key_files"`
|
|
// AllowedLogins is a list of allowed logins for users within
|
|
// this certificate authority
|
|
AllowedLogins []string `yaml:"allowed_logins"`
|
|
}
|
|
|
|
// Parse reads values and returns parsed CertAuthority
|
|
func (a *Authority) Parse() (services.CertAuthority, services.Role, error) {
|
|
ca := types.NewCertAuthority(types.CertAuthoritySpecV2{
|
|
Type: a.Type,
|
|
ClusterName: a.DomainName,
|
|
})
|
|
|
|
// transform old allowed logins into roles
|
|
role := services.RoleForCertAuthority(ca)
|
|
role.SetLogins(services.Allow, a.AllowedLogins)
|
|
ca.AddRole(role.GetName())
|
|
|
|
for _, path := range a.CheckingKeyFiles {
|
|
keyBytes, err := utils.ReadPath(path)
|
|
if err != nil {
|
|
return nil, nil, trace.Wrap(err)
|
|
}
|
|
ca.SetCheckingKeys(append(ca.GetCheckingKeys(), keyBytes))
|
|
}
|
|
|
|
for _, val := range a.CheckingKeys {
|
|
ca.SetCheckingKeys(append(ca.GetCheckingKeys(), []byte(val)))
|
|
}
|
|
|
|
for _, path := range a.SigningKeyFiles {
|
|
keyBytes, err := utils.ReadPath(path)
|
|
if err != nil {
|
|
return nil, nil, trace.Wrap(err)
|
|
}
|
|
ca.SetSigningKeys(append(ca.GetSigningKeys(), keyBytes))
|
|
}
|
|
|
|
for _, val := range a.SigningKeys {
|
|
ca.SetSigningKeys(append(ca.GetSigningKeys(), []byte(val)))
|
|
}
|
|
|
|
return ca, role, 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"`
|
|
}
|
|
|
|
// OIDCConnector specifies configuration fo Open ID Connect compatible external
|
|
// identity provider, e.g. google in some organisation
|
|
type OIDCConnector struct {
|
|
// ID is a provider id, 'e.g.' google, used internally
|
|
ID string `yaml:"id"`
|
|
// Issuer URL is the endpoint of the provider, e.g. https://accounts.google.com
|
|
IssuerURL string `yaml:"issuer_url"`
|
|
// ClientID is id for authentication client (in our case it's our Auth server)
|
|
ClientID string `yaml:"client_id"`
|
|
// ClientSecret is used to authenticate our client and should not
|
|
// be visible to end user
|
|
ClientSecret string `yaml:"client_secret"`
|
|
// RedirectURL - Identity provider will use this URL to redirect
|
|
// client's browser back to it after successful authentication
|
|
// Should match the URL on Provider's side
|
|
RedirectURL string `yaml:"redirect_url"`
|
|
// ACR is the acr_values parameter to be sent with an authorization request.
|
|
ACR string `yaml:"acr_values,omitempty"`
|
|
// Provider is the identity provider we connect to. This field is
|
|
// only required if using acr_values.
|
|
Provider string `yaml:"provider,omitempty"`
|
|
// Display controls how this connector is displayed
|
|
Display string `yaml:"display"`
|
|
// Scope is a list of additional scopes to request from OIDC
|
|
// note that oidc and email scopes are always requested
|
|
Scope []string `yaml:"scope"`
|
|
// ClaimsToRoles is a list of mappings of claims to roles
|
|
ClaimsToRoles []ClaimMapping `yaml:"claims_to_roles"`
|
|
}
|
|
|
|
// Parse parses config struct into services connector and checks if it's valid
|
|
func (o *OIDCConnector) Parse() (services.OIDCConnector, error) {
|
|
if o.Display == "" {
|
|
o.Display = o.ID
|
|
}
|
|
|
|
var mappings []services.ClaimMapping
|
|
for _, c := range o.ClaimsToRoles {
|
|
var roles []string
|
|
if len(c.Roles) > 0 {
|
|
roles = append(roles, c.Roles...)
|
|
}
|
|
|
|
mappings = append(mappings, services.ClaimMapping{
|
|
Claim: c.Claim,
|
|
Value: c.Value,
|
|
Roles: roles,
|
|
})
|
|
}
|
|
|
|
v2 := services.NewOIDCConnector(o.ID, services.OIDCConnectorSpecV2{
|
|
IssuerURL: o.IssuerURL,
|
|
ClientID: o.ClientID,
|
|
ClientSecret: o.ClientSecret,
|
|
RedirectURL: o.RedirectURL,
|
|
Display: o.Display,
|
|
Scope: o.Scope,
|
|
ClaimsToRoles: mappings,
|
|
})
|
|
|
|
v2.SetACR(o.ACR)
|
|
v2.SetProvider(o.Provider)
|
|
if err := v2.Check(); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
return v2, nil
|
|
}
|
|
|
|
type U2F struct {
|
|
AppID string `yaml:"app_id,omitempty"`
|
|
Facets []string `yaml:"facets,omitempty"`
|
|
}
|
|
|
|
// Parse parses values in the U2F configuration section and validates its content.
|
|
func (u *U2F) Parse() (*services.U2F, error) {
|
|
// If no appID specified, default to hostname
|
|
appID := u.AppID
|
|
if appID == "" {
|
|
hostname, err := os.Hostname()
|
|
if err != nil {
|
|
return nil, trace.Wrap(err, "failed to automatically determine U2F AppID from hostname")
|
|
}
|
|
appID = fmt.Sprintf("https://%s:%d", strings.ToLower(hostname), defaults.HTTPListenPort)
|
|
}
|
|
|
|
// If no facets specified, default to AppID
|
|
facets := u.Facets
|
|
if len(facets) == 0 {
|
|
facets = []string{appID}
|
|
}
|
|
|
|
return &services.U2F{
|
|
AppID: appID,
|
|
Facets: facets,
|
|
}, nil
|
|
}
|