2016-02-16 21:18:58 +00:00
|
|
|
/*
|
|
|
|
Copyright 2015-16 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.
|
|
|
|
*/
|
2016-03-11 01:03:01 +00:00
|
|
|
|
2016-02-16 21:18:58 +00:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
2016-03-28 19:58:34 +00:00
|
|
|
"bytes"
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2016-03-11 01:03:01 +00:00
|
|
|
"io/ioutil"
|
2016-03-02 02:24:20 +00:00
|
|
|
"net"
|
2016-03-28 19:58:34 +00:00
|
|
|
"os"
|
2016-02-16 22:18:45 +00:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2016-06-10 02:17:07 +00:00
|
|
|
"gopkg.in/yaml.v2"
|
|
|
|
|
2016-03-11 01:03:01 +00:00
|
|
|
"github.com/gravitational/teleport"
|
2016-03-28 19:58:34 +00:00
|
|
|
"github.com/gravitational/teleport/lib/auth"
|
2016-10-24 06:42:17 +00:00
|
|
|
"github.com/gravitational/teleport/lib/backend"
|
2016-02-24 07:35:25 +00:00
|
|
|
"github.com/gravitational/teleport/lib/defaults"
|
|
|
|
"github.com/gravitational/teleport/lib/service"
|
2016-03-28 19:58:34 +00:00
|
|
|
"github.com/gravitational/teleport/lib/services"
|
|
|
|
"github.com/gravitational/teleport/lib/utils"
|
2016-03-11 01:03:01 +00:00
|
|
|
|
2016-02-16 22:18:45 +00:00
|
|
|
"github.com/gravitational/trace"
|
2016-02-16 21:18:58 +00:00
|
|
|
)
|
|
|
|
|
2016-02-17 02:19:21 +00:00
|
|
|
var (
|
|
|
|
// all possible valid YAML config keys
|
2016-02-26 23:29:49 +00:00
|
|
|
validKeys = map[string]bool{
|
2016-06-17 06:50:12 +00:00
|
|
|
"seed_config": true,
|
2016-05-13 01:51:12 +00:00
|
|
|
"cluster_name": true,
|
2016-06-02 01:56:48 +00:00
|
|
|
"trusted_clusters": true,
|
2016-04-02 00:58:41 +00:00
|
|
|
"pid_file": true,
|
2016-03-28 19:58:34 +00:00
|
|
|
"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,
|
|
|
|
"auth_token": true,
|
|
|
|
"auth_servers": true,
|
|
|
|
"domain_name": true,
|
|
|
|
"storage": true,
|
|
|
|
"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,
|
|
|
|
"peers": true,
|
|
|
|
"prefix": true,
|
|
|
|
"web_listen_addr": true,
|
2016-06-12 02:05:50 +00:00
|
|
|
"tunnel_listen_addr": true,
|
2016-03-28 19:58:34 +00:00
|
|
|
"ssh_listen_addr": true,
|
|
|
|
"listen_addr": true,
|
|
|
|
"https_key_file": true,
|
|
|
|
"https_cert_file": true,
|
|
|
|
"advertise_ip": true,
|
|
|
|
"tls_key_file": true,
|
|
|
|
"tls_cert_file": true,
|
|
|
|
"tls_ca_file": true,
|
|
|
|
"authorities": true,
|
|
|
|
"keys": true,
|
2016-04-02 19:57:44 +00:00
|
|
|
"reverse_tunnels": true,
|
2016-03-28 19:58:34 +00:00
|
|
|
"addresses": true,
|
2016-04-03 05:20:51 +00:00
|
|
|
"oidc_connectors": true,
|
|
|
|
"id": true,
|
|
|
|
"issuer_url": true,
|
|
|
|
"client_id": true,
|
|
|
|
"client_secret": true,
|
|
|
|
"redirect_url": true,
|
2016-05-12 07:44:25 +00:00
|
|
|
"tokens": true,
|
2016-10-24 06:42:17 +00:00
|
|
|
"region": true,
|
|
|
|
"table_name": true,
|
|
|
|
"access_key": true,
|
|
|
|
"secret_key": true,
|
2016-10-22 02:57:14 +00:00
|
|
|
"u2fappid": true,
|
|
|
|
"u2ftrustedfacets": true,
|
2016-02-17 02:19:21 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2016-02-16 22:18:45 +00:00
|
|
|
// 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 {
|
2016-04-02 19:57:44 +00:00
|
|
|
Global `yaml:"teleport,omitempty"`
|
|
|
|
Auth Auth `yaml:"auth_service,omitempty"`
|
|
|
|
SSH SSH `yaml:"ssh_service,omitempty"`
|
|
|
|
Proxy Proxy `yaml:"proxy_service,omitempty"`
|
2016-02-16 22:18:45 +00:00
|
|
|
}
|
|
|
|
|
2016-02-17 02:19:21 +00:00
|
|
|
type YAMLMap map[interface{}]interface{}
|
|
|
|
|
2016-02-16 22:18:45 +00:00
|
|
|
// ReadFromFile reads Teleport configuration from a file. Currently only YAML
|
|
|
|
// format is supported
|
2016-03-28 19:58:34 +00:00
|
|
|
func ReadFromFile(filePath string) (*FileConfig, error) {
|
|
|
|
ext := strings.ToLower(filepath.Ext(filePath))
|
2016-02-16 22:18:45 +00:00
|
|
|
if ext != ".yaml" && ext != ".yml" {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.BadParameter(
|
|
|
|
"'%v' invalid configuration file type: '%v'. Only .yml is supported", filePath, ext)
|
2016-02-16 22:18:45 +00:00
|
|
|
}
|
2016-03-28 19:58:34 +00:00
|
|
|
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 {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.BadParameter(
|
|
|
|
"confiugraion should be base64 encoded: %v", err)
|
2016-03-28 19:58:34 +00:00
|
|
|
}
|
|
|
|
return ReadConfig(bytes.NewBuffer(data))
|
|
|
|
}
|
2016-02-16 22:18:45 +00:00
|
|
|
|
2016-03-28 19:58:34 +00:00
|
|
|
// ReadConfig reads Teleport configuration from reader in YAML format
|
|
|
|
func ReadConfig(reader io.Reader) (*FileConfig, error) {
|
2016-02-16 22:18:45 +00:00
|
|
|
// read & parse YAML config:
|
2016-03-28 19:58:34 +00:00
|
|
|
bytes, err := ioutil.ReadAll(reader)
|
2016-02-16 22:18:45 +00:00
|
|
|
if err != nil {
|
2016-03-28 19:58:34 +00:00
|
|
|
return nil, trace.Wrap(err, "failed reading Teleport configuration")
|
2016-02-16 22:18:45 +00:00
|
|
|
}
|
2016-03-28 19:58:34 +00:00
|
|
|
var fc FileConfig
|
|
|
|
if err = yaml.Unmarshal(bytes, &fc); err != nil {
|
|
|
|
return nil, trace.Wrap(err, "failed to parse Teleport configuration")
|
2016-02-16 22:18:45 +00:00
|
|
|
}
|
2016-02-17 02:19:21 +00:00
|
|
|
// now check for unknown (misspelled) config keys:
|
|
|
|
var validateKeys func(m YAMLMap) error
|
|
|
|
validateKeys = func(m YAMLMap) error {
|
2016-02-26 23:29:49 +00:00
|
|
|
var recursive, ok bool
|
|
|
|
var key string
|
|
|
|
for k, v := range m {
|
|
|
|
if key, ok = k.(string); ok {
|
|
|
|
if recursive, ok = validKeys[key]; !ok {
|
2016-06-07 22:02:59 +00:00
|
|
|
return trace.BadParameter("unrecognized configuration key: '%v'", key)
|
2016-02-17 02:19:21 +00:00
|
|
|
}
|
2016-02-26 23:29:49 +00:00
|
|
|
if recursive {
|
|
|
|
if m2, ok := v.(YAMLMap); ok {
|
|
|
|
if err := validateKeys(m2); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2016-02-17 02:19:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2016-02-26 23:29:49 +00:00
|
|
|
// validate configuration keys:
|
|
|
|
var tmp YAMLMap
|
|
|
|
if err = yaml.Unmarshal(bytes, &tmp); err != nil {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.BadParameter("error parsing YAML config")
|
2016-02-17 02:19:21 +00:00
|
|
|
}
|
2016-02-26 23:29:49 +00:00
|
|
|
if err = validateKeys(tmp); err != nil {
|
2016-02-17 02:19:21 +00:00
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
2016-03-28 19:58:34 +00:00
|
|
|
return &fc, nil
|
2016-02-16 22:18:45 +00:00
|
|
|
}
|
|
|
|
|
2016-03-11 01:03:01 +00:00
|
|
|
// MakeSampleFileConfig returns a sample config structure populated by defaults,
|
2016-02-24 07:35:25 +00:00
|
|
|
// useful to generate sample configuration files
|
|
|
|
func MakeSampleFileConfig() (fc *FileConfig) {
|
|
|
|
conf := service.MakeDefaultConfig()
|
|
|
|
|
|
|
|
// sample global config:
|
|
|
|
var g Global
|
|
|
|
g.NodeName = conf.Hostname
|
2016-10-09 23:00:20 +00:00
|
|
|
g.AuthToken = "cluster-join-token"
|
2016-02-24 07:35:25 +00:00
|
|
|
g.Logger.Output = "stderr"
|
|
|
|
g.Logger.Severity = "INFO"
|
2016-03-11 01:03:01 +00:00
|
|
|
g.AuthServers = []string{defaults.AuthListenAddr().Addr}
|
2016-02-24 07:35:25 +00:00
|
|
|
g.Limits.MaxConnections = defaults.LimiterMaxConnections
|
|
|
|
g.Limits.MaxUsers = defaults.LimiterMaxConcurrentUsers
|
2016-06-01 00:31:33 +00:00
|
|
|
g.DataDir = defaults.DataDir
|
2016-02-24 07:35:25 +00:00
|
|
|
g.Storage.Type = conf.Auth.RecordsBackend.Type
|
2016-04-02 01:03:57 +00:00
|
|
|
g.PIDFile = "/var/run/teleport.pid"
|
2016-02-24 07:35:25 +00:00
|
|
|
|
|
|
|
// sample SSH config:
|
|
|
|
var s SSH
|
|
|
|
s.EnabledFlag = "yes"
|
|
|
|
s.ListenAddress = conf.SSH.Addr.Addr
|
|
|
|
s.Commands = []CommandLabel{
|
|
|
|
{
|
|
|
|
Name: "hostname",
|
|
|
|
Command: []string{"/usr/bin/hostname"},
|
|
|
|
Period: time.Minute,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "arch",
|
|
|
|
Command: []string{"/usr/bin/uname", "-p"},
|
|
|
|
Period: time.Hour,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
s.Labels = map[string]string{
|
|
|
|
"db_type": "postgres",
|
|
|
|
"db_role": "master",
|
|
|
|
}
|
|
|
|
|
|
|
|
// sample Auth config:
|
|
|
|
var a Auth
|
|
|
|
a.ListenAddress = conf.Auth.SSHAddr.Addr
|
|
|
|
a.EnabledFlag = "yes"
|
2016-10-23 03:43:44 +00:00
|
|
|
a.U2fAppId = conf.Auth.U2fAppId
|
|
|
|
a.U2fTrustedFacets = conf.Auth.U2fTrustedFacets
|
2016-10-09 23:00:20 +00:00
|
|
|
a.StaticTokens = []StaticToken{"proxy,node:cluster-join-token"}
|
2016-02-24 07:35:25 +00:00
|
|
|
|
|
|
|
// sample proxy config:
|
|
|
|
var p Proxy
|
|
|
|
p.EnabledFlag = "yes"
|
|
|
|
p.ListenAddress = conf.Proxy.SSHAddr.Addr
|
|
|
|
p.WebAddr = conf.Proxy.WebAddr.Addr
|
2016-06-12 02:05:50 +00:00
|
|
|
p.TunAddr = conf.Proxy.ReverseTunnelListenAddr.Addr
|
2016-02-24 07:35:25 +00:00
|
|
|
p.CertFile = "/etc/teleport/teleport.crt"
|
|
|
|
p.KeyFile = "/etc/teleport/teleport.key"
|
|
|
|
|
|
|
|
fc = &FileConfig{
|
|
|
|
Global: g,
|
|
|
|
Proxy: p,
|
|
|
|
SSH: s,
|
|
|
|
Auth: a,
|
|
|
|
}
|
|
|
|
return fc
|
|
|
|
}
|
|
|
|
|
2016-03-12 04:09:40 +00:00
|
|
|
// MakeAuthPeerFileConfig returns a sample configuration for auth
|
|
|
|
// server peer that shares etcd backend
|
|
|
|
func MakeAuthPeerFileConfig(domainName string, token string) (fc *FileConfig) {
|
|
|
|
conf := service.MakeDefaultConfig()
|
|
|
|
|
|
|
|
// sample global config:
|
|
|
|
var g Global
|
|
|
|
g.NodeName = conf.Hostname
|
|
|
|
g.AuthToken = token
|
|
|
|
g.Logger.Output = "stderr"
|
|
|
|
g.Logger.Severity = "INFO"
|
|
|
|
g.AuthServers = []string{"<insert auth server peer address here>"}
|
|
|
|
g.Limits.MaxConnections = defaults.LimiterMaxConnections
|
|
|
|
g.Limits.MaxUsers = defaults.LimiterMaxConcurrentUsers
|
|
|
|
g.Storage.Type = teleport.ETCDBackendType
|
|
|
|
g.Storage.Prefix = defaults.ETCDPrefix
|
|
|
|
g.Storage.Peers = []string{"insert ETCD peers addresses here"}
|
|
|
|
|
|
|
|
// sample Auth config:
|
|
|
|
var a Auth
|
|
|
|
a.ListenAddress = conf.Auth.SSHAddr.Addr
|
|
|
|
a.EnabledFlag = "yes"
|
|
|
|
a.DomainName = domainName
|
|
|
|
|
|
|
|
var p Proxy
|
|
|
|
p.EnabledFlag = "no"
|
|
|
|
|
|
|
|
var s SSH
|
|
|
|
s.EnabledFlag = "no"
|
|
|
|
|
|
|
|
fc = &FileConfig{
|
|
|
|
Global: g,
|
|
|
|
Auth: a,
|
|
|
|
Proxy: p,
|
|
|
|
SSH: s,
|
|
|
|
}
|
|
|
|
return fc
|
|
|
|
}
|
|
|
|
|
2016-03-11 01:03:01 +00:00
|
|
|
// DebugDumpToYAML allows for quick YAML dumping of the config
|
2016-02-16 22:18:45 +00:00
|
|
|
func (conf *FileConfig) DebugDumpToYAML() string {
|
|
|
|
bytes, err := yaml.Marshal(&conf)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return string(bytes)
|
|
|
|
}
|
|
|
|
|
2016-03-11 01:03:01 +00:00
|
|
|
// ConnectionRate configures rate limiter
|
2016-02-16 21:18:58 +00:00
|
|
|
type ConnectionRate struct {
|
2016-02-16 22:18:45 +00:00
|
|
|
Period time.Duration `yaml:"period"`
|
2016-02-21 01:17:09 +00:00
|
|
|
Average int64 `yaml:"average"`
|
|
|
|
Burst int64 `yaml:"burst"`
|
2016-02-16 21:18:58 +00:00
|
|
|
}
|
|
|
|
|
2016-03-11 01:03:01 +00:00
|
|
|
// ConnectionLimits sets up connection limiter
|
2016-02-16 21:18:58 +00:00
|
|
|
type ConnectionLimits struct {
|
2016-02-21 01:17:09 +00:00
|
|
|
MaxConnections int64 `yaml:"max_connections"`
|
2016-02-16 21:18:58 +00:00
|
|
|
MaxUsers int `yaml:"max_users"`
|
|
|
|
Rates []ConnectionRate `yaml:"rates,omitempty"`
|
|
|
|
}
|
|
|
|
|
2016-03-11 01:03:01 +00:00
|
|
|
// Log configures teleport logging
|
2016-02-16 22:18:45 +00:00
|
|
|
type Log struct {
|
2016-06-21 19:09:55 +00:00
|
|
|
// 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"
|
2016-02-16 22:18:45 +00:00
|
|
|
Severity string `yaml:"severity,omitempty"`
|
|
|
|
}
|
|
|
|
|
2016-03-11 01:03:01 +00:00
|
|
|
// Global is 'teleport' (global) section of the config file
|
2016-02-16 22:18:45 +00:00
|
|
|
type Global struct {
|
2016-02-16 21:18:58 +00:00
|
|
|
NodeName string `yaml:"nodename,omitempty"`
|
2016-04-02 01:03:57 +00:00
|
|
|
PIDFile string `yaml:"pid_file,omitempty"`
|
2016-02-17 02:19:21 +00:00
|
|
|
AuthToken string `yaml:"auth_token,omitempty"`
|
2016-03-11 01:03:01 +00:00
|
|
|
AuthServers []string `yaml:"auth_servers,omitempty"`
|
2016-02-16 22:18:45 +00:00
|
|
|
Limits ConnectionLimits `yaml:"connection_limits,omitempty"`
|
|
|
|
Logger Log `yaml:"log,omitempty"`
|
2016-10-24 06:42:17 +00:00
|
|
|
Storage backend.Config `yaml:"storage,omitempty"`
|
2016-03-12 04:09:40 +00:00
|
|
|
AdvertiseIP net.IP `yaml:"advertise_ip,omitempty"`
|
2016-06-01 00:31:33 +00:00
|
|
|
DataDir string `yaml:"data_dir,omitempty"`
|
2016-04-02 19:57:44 +00:00
|
|
|
|
|
|
|
// Keys holds the list of SSH key/cert pairs used by all services
|
|
|
|
// Each service (like proxy, auth, node) can find the key it needs
|
|
|
|
// by looking into certificate
|
|
|
|
Keys []KeyPair `yaml:"keys,omitempty"`
|
2016-06-17 06:50:12 +00:00
|
|
|
|
|
|
|
// SeedConfig [GRAVITATIONAL USE] when set to true, Teleport treats
|
|
|
|
// its configuration file simply as a seed data on initial start-up.
|
|
|
|
// For OSS Teleport it should always be 'false' by default.
|
|
|
|
SeedConfig bool `yaml:"seed_config,omitempty"`
|
2016-02-16 21:18:58 +00:00
|
|
|
}
|
|
|
|
|
2016-03-11 01:03:01 +00:00
|
|
|
// Service is a common configuration of a teleport service
|
2016-02-16 22:18:45 +00:00
|
|
|
type Service struct {
|
|
|
|
EnabledFlag string `yaml:"enabled,omitempty"`
|
|
|
|
ListenAddress string `yaml:"listen_addr,omitempty"`
|
2016-02-16 21:18:58 +00:00
|
|
|
}
|
|
|
|
|
2016-02-16 22:18:45 +00:00
|
|
|
// Configured determines if a given "_service" section has been specified
|
|
|
|
func (s *Service) Configured() bool {
|
|
|
|
return s.EnabledFlag != ""
|
|
|
|
}
|
2016-02-16 21:18:58 +00:00
|
|
|
|
2016-02-16 22:18:45 +00:00
|
|
|
// Enabled determines if a given "_service" section has been set to 'true'
|
|
|
|
func (s *Service) Enabled() bool {
|
|
|
|
switch strings.ToLower(s.EnabledFlag) {
|
|
|
|
case "", "yes", "yeah", "y", "true", "1":
|
|
|
|
return true
|
2016-02-16 21:18:58 +00:00
|
|
|
}
|
2016-02-16 22:18:45 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-02-17 02:19:21 +00:00
|
|
|
// Disabled returns 'true' if the service has been deliverately turned off
|
|
|
|
func (s *Service) Disabled() bool {
|
|
|
|
return s.Configured() && !s.Enabled()
|
|
|
|
}
|
|
|
|
|
2016-03-12 04:09:40 +00:00
|
|
|
// Auth is 'auth_service' section of the config file
|
2016-02-16 22:18:45 +00:00
|
|
|
type Auth struct {
|
|
|
|
Service `yaml:",inline"`
|
2016-05-14 23:44:41 +00:00
|
|
|
// DomainName is the name of the CA who manages this cluster
|
|
|
|
DomainName string `yaml:"cluster_name,omitempty"`
|
2016-04-02 19:57:44 +00:00
|
|
|
|
2016-06-02 01:56:48 +00:00
|
|
|
// 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
|
2016-06-11 06:02:42 +00:00
|
|
|
TrustedClusters []TrustedCluster `yaml:"trusted_clusters,omitempty"`
|
2016-06-02 01:56:48 +00:00
|
|
|
|
2016-06-07 22:02:59 +00:00
|
|
|
// FOR INTERNAL USE:
|
2016-05-11 21:33:44 +00:00
|
|
|
// Authorities : 3rd party certificate authorities (CAs) this auth service trusts.
|
2016-04-02 19:57:44 +00:00
|
|
|
Authorities []Authority `yaml:"authorities,omitempty"`
|
|
|
|
|
2016-06-07 22:02:59 +00:00
|
|
|
// FOR INTERNAL USE:
|
2016-05-11 21:33:44 +00:00
|
|
|
// ReverseTunnels is a list of SSH tunnels to 3rd party proxy services (used to talk
|
2016-04-02 19:57:44 +00:00
|
|
|
// to 3rd party auth servers we trust)
|
|
|
|
ReverseTunnels []ReverseTunnel `yaml:"reverse_tunnels,omitempty"`
|
2016-04-03 05:20:51 +00:00
|
|
|
|
|
|
|
// OIDCConnectors is a list of trusted OpenID Connect Identity providers
|
|
|
|
OIDCConnectors []OIDCConnector `yaml:"oidc_connectors"`
|
2016-05-12 07:44:25 +00:00
|
|
|
|
|
|
|
// 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"
|
2016-05-14 23:44:41 +00:00
|
|
|
StaticTokens []StaticToken `yaml:"tokens,omitempty"`
|
2016-10-14 06:51:16 +00:00
|
|
|
|
2016-10-22 02:57:14 +00:00
|
|
|
U2fAppId string `yaml:"u2fappid,omitempty"`
|
2016-10-17 04:03:09 +00:00
|
|
|
|
2016-10-22 02:57:14 +00:00
|
|
|
U2fTrustedFacets []string `yaml:"u2ftrustedfacets,omitempty"`
|
2016-02-16 22:18:45 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 06:02:42 +00:00
|
|
|
// 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
|
2016-06-12 22:20:42 +00:00
|
|
|
KeyFile string `yaml:"key_file,omitempty"`
|
2016-06-11 06:02:42 +00:00
|
|
|
// 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 addressess to
|
|
|
|
// connect to
|
|
|
|
TunnelAddr string `yaml:"tunnel_addr,omitempty"`
|
|
|
|
}
|
|
|
|
|
2016-05-12 07:44:25 +00:00
|
|
|
type StaticToken string
|
|
|
|
|
2016-03-12 04:09:40 +00:00
|
|
|
// SSH is 'ssh_service' section of the config file
|
2016-02-16 22:18:45 +00:00
|
|
|
type SSH struct {
|
2016-03-12 04:09:40 +00:00
|
|
|
Service `yaml:",inline"`
|
|
|
|
Labels map[string]string `yaml:"labels,omitempty"`
|
|
|
|
Commands []CommandLabel `yaml:"commands,omitempty"`
|
2016-02-16 22:18:45 +00:00
|
|
|
}
|
|
|
|
|
2016-03-12 04:09:40 +00:00
|
|
|
// CommandLabel is `command` section of `ssh_service` in the config file
|
2016-02-16 22:18:45 +00:00
|
|
|
type CommandLabel struct {
|
|
|
|
Name string `yaml:"name"`
|
2016-02-24 07:35:25 +00:00
|
|
|
Command []string `yaml:"command,flow"`
|
2016-02-16 22:18:45 +00:00
|
|
|
Period time.Duration `yaml:"period"`
|
|
|
|
}
|
|
|
|
|
2016-03-12 04:09:40 +00:00
|
|
|
// Proxy is `proxy_service` section of the config file:
|
2016-02-16 22:18:45 +00:00
|
|
|
type Proxy struct {
|
|
|
|
Service `yaml:",inline"`
|
|
|
|
WebAddr string `yaml:"web_listen_addr,omitempty"`
|
2016-06-12 02:05:50 +00:00
|
|
|
TunAddr string `yaml:"tunnel_listen_addr,omitempty"`
|
2016-02-16 22:18:45 +00:00
|
|
|
KeyFile string `yaml:"https_key_file,omitempty"`
|
|
|
|
CertFile string `yaml:"https_cert_file,omitempty"`
|
2016-02-16 21:18:58 +00:00
|
|
|
}
|
2016-03-28 19:58:34 +00:00
|
|
|
|
|
|
|
// ReverseTunnel is a SSH reverse tunnel mantained by one cluster's
|
|
|
|
// proxy to remote Teleport proxy
|
|
|
|
type ReverseTunnel struct {
|
|
|
|
DomainName string `yaml:"domain_name"`
|
|
|
|
Addresses []string `yaml:"addresses"`
|
|
|
|
}
|
|
|
|
|
2016-06-10 02:17:07 +00:00
|
|
|
// 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()
|
|
|
|
}
|
|
|
|
|
2016-03-28 19:58:34 +00:00
|
|
|
out := &services.ReverseTunnel{
|
|
|
|
DomainName: t.DomainName,
|
|
|
|
DialAddrs: t.Addresses,
|
|
|
|
}
|
|
|
|
if err := out.Check(); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// KeyPair is a pair of private key and certificates
|
|
|
|
type KeyPair struct {
|
|
|
|
// PrivateKeyFile is a path to file with private key
|
|
|
|
PrivateKeyFile string `yaml:"private_key_file"`
|
|
|
|
// CertFile is a path to file with OpenSSH certificate
|
|
|
|
CertFile string `yaml:"cert_file"`
|
|
|
|
// PrivateKey is PEM encoded OpenSSH private key
|
|
|
|
PrivateKey string `yaml:"private_key"`
|
|
|
|
// Cert is certificate in OpenSSH authorized keys format
|
|
|
|
Cert string `yaml:"cert"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Identity parses keypair into auth server identity
|
|
|
|
func (k *KeyPair) Identity() (*auth.Identity, error) {
|
|
|
|
var keyBytes []byte
|
|
|
|
var err error
|
|
|
|
if k.PrivateKeyFile != "" {
|
|
|
|
keyBytes, err = ioutil.ReadFile(k.PrivateKeyFile)
|
|
|
|
if err != nil {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.ConvertSystemError(err)
|
2016-03-28 19:58:34 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
keyBytes = []byte(k.PrivateKey)
|
|
|
|
}
|
|
|
|
var certBytes []byte
|
|
|
|
if k.CertFile != "" {
|
|
|
|
certBytes, err = ioutil.ReadFile(k.CertFile)
|
|
|
|
if err != nil {
|
2016-04-12 17:54:24 +00:00
|
|
|
return nil, trace.ConvertSystemError(err)
|
2016-03-28 19:58:34 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
certBytes = []byte(k.Cert)
|
|
|
|
}
|
|
|
|
return auth.ReadIdentityFromKeyPair(keyBytes, certBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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, error) {
|
|
|
|
ca := &services.CertAuthority{
|
|
|
|
AllowedLogins: a.AllowedLogins,
|
|
|
|
DomainName: a.DomainName,
|
|
|
|
Type: a.Type,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, path := range a.CheckingKeyFiles {
|
|
|
|
keyBytes, err := utils.ReadPath(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
ca.CheckingKeys = append(ca.CheckingKeys, keyBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, val := range a.CheckingKeys {
|
|
|
|
ca.CheckingKeys = append(ca.CheckingKeys, []byte(val))
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, path := range a.SigningKeyFiles {
|
|
|
|
keyBytes, err := utils.ReadPath(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
ca.SigningKeys = append(ca.SigningKeys, keyBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, val := range a.SigningKeys {
|
|
|
|
ca.SigningKeys = append(ca.SigningKeys, []byte(val))
|
|
|
|
}
|
|
|
|
|
|
|
|
return ca, nil
|
|
|
|
}
|
2016-04-03 05:20:51 +00:00
|
|
|
|
|
|
|
// 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 successfull authentication
|
|
|
|
// Should match the URL on Provider's side
|
|
|
|
RedirectURL string `yaml:"redirect_url"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse parses config struct into services connector and checks if it's valid
|
|
|
|
func (o *OIDCConnector) Parse() (*services.OIDCConnector, error) {
|
|
|
|
other := &services.OIDCConnector{
|
|
|
|
ID: o.ID,
|
|
|
|
IssuerURL: o.IssuerURL,
|
|
|
|
ClientID: o.ClientID,
|
|
|
|
ClientSecret: o.ClientSecret,
|
|
|
|
RedirectURL: o.RedirectURL,
|
|
|
|
}
|
|
|
|
if err := other.Check(); err != nil {
|
|
|
|
return nil, trace.Wrap(err)
|
|
|
|
}
|
|
|
|
return other, nil
|
|
|
|
}
|
2016-05-12 07:44:25 +00:00
|
|
|
|
|
|
|
// Parse() is applied to a string in "role,role,role:token" format. It breaks it
|
|
|
|
// apart into a slice of roles, token and optional error
|
|
|
|
func (t StaticToken) Parse() (roles teleport.Roles, token string, err error) {
|
|
|
|
parts := strings.Split(string(t), ":")
|
|
|
|
if len(parts) != 2 {
|
|
|
|
return nil, "", trace.Errorf("invalid static token spec: '%s'", t)
|
|
|
|
}
|
|
|
|
roles, err = teleport.ParseRoles(parts[0])
|
|
|
|
return roles, parts[1], trace.Wrap(err)
|
|
|
|
}
|