mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 02:03:24 +00:00
Improves teleport configure command.
Fixes #5559 Configure with -o file create file /etc/teleport.yaml. This commit optimizes configure for getting users started instead of generating sample files. ```bash teleport configure -o file --cluster-name=example.com --acme --acme-email=alice@example.com ```
This commit is contained in:
parent
c841f2e8dd
commit
71e6b1451d
|
@ -132,7 +132,7 @@ func ReadConfigFile(cliConfigPath string) (*FileConfig, error) {
|
|||
if cliConfigPath != "" {
|
||||
configFilePath = cliConfigPath
|
||||
if !fileExists(configFilePath) {
|
||||
return nil, trace.Errorf("file not found: %s", configFilePath)
|
||||
return nil, trace.NotFound("file %s is not found", configFilePath)
|
||||
}
|
||||
}
|
||||
// default config doesn't exist? quietly return:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2015 Gravitational, Inc.
|
||||
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.
|
||||
|
@ -32,18 +32,15 @@ import (
|
|||
"github.com/gravitational/teleport/lib/backend/lite"
|
||||
"github.com/gravitational/teleport/lib/backend/memory"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/fixtures"
|
||||
"github.com/gravitational/teleport/lib/service"
|
||||
"github.com/gravitational/teleport/lib/services"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/gravitational/trace"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"gopkg.in/check.v1"
|
||||
|
||||
"github.com/gravitational/trace"
|
||||
)
|
||||
|
||||
type testConfigFiles struct {
|
||||
|
@ -104,45 +101,40 @@ func TestMain(m *testing.M) {
|
|||
os.Exit(res)
|
||||
}
|
||||
|
||||
// bootstrap check
|
||||
func TestConfig(t *testing.T) { check.TestingT(t) }
|
||||
|
||||
// register test suite
|
||||
type ConfigTestSuite struct {
|
||||
testConfigFiles
|
||||
}
|
||||
|
||||
var _ = check.Suite(&ConfigTestSuite{})
|
||||
|
||||
func (s *ConfigTestSuite) SetUpSuite(c *check.C) {
|
||||
func TestConfig(t *testing.T) {
|
||||
utils.InitLoggerForTests(testing.Verbose())
|
||||
s.testConfigFiles = testConfigs
|
||||
}
|
||||
|
||||
func (s *ConfigTestSuite) TestSampleConfig(c *check.C) {
|
||||
// generate sample config and write it into a temp file:
|
||||
sfc, err := MakeSampleFileConfig()
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(sfc, check.NotNil)
|
||||
fn := filepath.Join(c.MkDir(), "default-config.yaml")
|
||||
err = ioutil.WriteFile(fn, []byte(sfc.DebugDumpToYAML()), 0660)
|
||||
c.Assert(err, check.IsNil)
|
||||
t.Run("SampleConfig", func(t *testing.T) {
|
||||
// generate sample config and write it into a temp file:
|
||||
sfc, err := MakeSampleFileConfig(SampleFlags{
|
||||
ClusterName: "cookie.localhost",
|
||||
ACMEEnabled: true,
|
||||
ACMEEmail: "alice@example.com",
|
||||
LicensePath: "/tmp/license.pem",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sfc)
|
||||
fn := filepath.Join(t.TempDir(), "default-config.yaml")
|
||||
err = ioutil.WriteFile(fn, []byte(sfc.DebugDumpToYAML()), 0660)
|
||||
require.NoError(t, err)
|
||||
|
||||
// make sure it could be parsed:
|
||||
fc, err := ReadFromFile(fn)
|
||||
c.Assert(err, check.IsNil)
|
||||
// make sure it could be parsed:
|
||||
fc, err := ReadFromFile(fn)
|
||||
require.NoError(t, err)
|
||||
|
||||
// validate a couple of values:
|
||||
c.Assert(fc.AuthServers, check.DeepEquals, []string{fmt.Sprintf("%s:%d", defaults.Localhost, defaults.AuthListenPort)})
|
||||
c.Assert(fc.Global.DataDir, check.Equals, defaults.DataDir)
|
||||
c.Assert(fc.Logger.Severity, check.Equals, "INFO")
|
||||
// validate a couple of values:
|
||||
require.Equal(t, defaults.DataDir, fc.Global.DataDir)
|
||||
require.Equal(t, "INFO", fc.Logger.Severity)
|
||||
require.Equal(t, fc.Auth.ClusterName, ClusterName("cookie.localhost"))
|
||||
require.Equal(t, fc.Auth.LicenseFile, "/tmp/license.pem")
|
||||
|
||||
c.Assert(lib.IsInsecureDevMode(), check.Equals, false)
|
||||
require.False(t, lib.IsInsecureDevMode())
|
||||
})
|
||||
}
|
||||
|
||||
// TestBooleanParsing tests that boolean options
|
||||
// are parsed properly
|
||||
func (s *ConfigTestSuite) TestBooleanParsing(c *check.C) {
|
||||
func TestBooleanParsing(t *testing.T) {
|
||||
testCases := []struct {
|
||||
s string
|
||||
b bool
|
||||
|
@ -157,7 +149,7 @@ func (s *ConfigTestSuite) TestBooleanParsing(c *check.C) {
|
|||
{s: "0", b: false},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
comment := check.Commentf("test case %v", i)
|
||||
msg := fmt.Sprintf("test case %v", i)
|
||||
conf, err := ReadFromString(base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`
|
||||
teleport:
|
||||
advertise_ip: 10.10.10.1
|
||||
|
@ -165,14 +157,14 @@ auth_service:
|
|||
enabled: yes
|
||||
disconnect_expired_cert: %v
|
||||
`, tc.s))))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(conf.Auth.DisconnectExpiredCert.Value(), check.Equals, tc.b, comment)
|
||||
require.NoError(t, err, msg)
|
||||
require.Equal(t, tc.b, conf.Auth.DisconnectExpiredCert.Value(), msg)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDurationParsing tests that duration options
|
||||
// are parsed properly
|
||||
func (s *ConfigTestSuite) TestDuration(c *check.C) {
|
||||
func TestDuration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
s string
|
||||
d time.Duration
|
||||
|
@ -182,7 +174,7 @@ func (s *ConfigTestSuite) TestDuration(c *check.C) {
|
|||
{s: "'1m'", d: time.Minute},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
comment := check.Commentf("test case %v", i)
|
||||
comment := fmt.Sprintf("test case %v", i)
|
||||
conf, err := ReadFromString(base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`
|
||||
teleport:
|
||||
advertise_ip: 10.10.10.1
|
||||
|
@ -190,8 +182,8 @@ auth_service:
|
|||
enabled: yes
|
||||
client_idle_timeout: %v
|
||||
`, tc.s))))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(conf.Auth.ClientIdleTimeout.Value(), check.Equals, tc.d, comment)
|
||||
require.NoError(t, err, comment)
|
||||
require.Equal(t, tc.d, conf.Auth.ClientIdleTimeout.Value(), comment)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,43 +325,43 @@ func TestConfigReading(t *testing.T) {
|
|||
checkStaticConfig(t, conf)
|
||||
}
|
||||
|
||||
func (s *ConfigTestSuite) TestLabelParsing(c *check.C) {
|
||||
func TestLabelParsing(t *testing.T) {
|
||||
var conf service.SSHConfig
|
||||
var err error
|
||||
// empty spec. no errors, no labels
|
||||
err = parseLabelsApply("", &conf)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(conf.CmdLabels, check.IsNil)
|
||||
c.Assert(conf.Labels, check.IsNil)
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, conf.CmdLabels)
|
||||
require.Nil(t, conf.Labels)
|
||||
|
||||
// simple static labels
|
||||
err = parseLabelsApply(`key=value,more="much better"`, &conf)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(conf.CmdLabels, check.NotNil)
|
||||
c.Assert(conf.CmdLabels, check.HasLen, 0)
|
||||
c.Assert(conf.Labels, check.DeepEquals, map[string]string{
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, conf.CmdLabels)
|
||||
require.Len(t, conf.CmdLabels, 0)
|
||||
require.Equal(t, map[string]string{
|
||||
"key": "value",
|
||||
"more": "much better",
|
||||
})
|
||||
}, conf.Labels)
|
||||
|
||||
// static labels + command labels
|
||||
err = parseLabelsApply(`key=value,more="much better",arch=[5m2s:/bin/uname -m "p1 p2"]`, &conf)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(conf.Labels, check.DeepEquals, map[string]string{
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, map[string]string{
|
||||
"key": "value",
|
||||
"more": "much better",
|
||||
})
|
||||
c.Assert(conf.CmdLabels, check.DeepEquals, services.CommandLabels{
|
||||
}, conf.Labels)
|
||||
require.Equal(t, services.CommandLabels{
|
||||
"arch": &services.CommandLabelV2{
|
||||
Period: services.NewDuration(time.Minute*5 + time.Second*2),
|
||||
Command: []string{"/bin/uname", "-m", `"p1 p2"`},
|
||||
},
|
||||
})
|
||||
}, conf.CmdLabels)
|
||||
}
|
||||
|
||||
func (s *ConfigTestSuite) TestTrustedClusters(c *check.C) {
|
||||
func TestTrustedClusters(t *testing.T) {
|
||||
err := readTrustedClusters(nil, nil)
|
||||
c.Assert(err, check.IsNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
var conf service.Config
|
||||
err = readTrustedClusters([]TrustedCluster{
|
||||
|
@ -379,24 +371,24 @@ func (s *ConfigTestSuite) TestTrustedClusters(c *check.C) {
|
|||
TunnelAddr: "one,two",
|
||||
},
|
||||
}, &conf)
|
||||
c.Assert(err, check.IsNil)
|
||||
require.NoError(t, err)
|
||||
authorities := conf.Auth.Authorities
|
||||
c.Assert(len(authorities), check.Equals, 2)
|
||||
c.Assert(authorities[0].GetClusterName(), check.Equals, "cluster-a")
|
||||
c.Assert(authorities[0].GetType(), check.Equals, services.HostCA)
|
||||
c.Assert(len(authorities[0].GetCheckingKeys()), check.Equals, 1)
|
||||
c.Assert(authorities[1].GetClusterName(), check.Equals, "cluster-a")
|
||||
c.Assert(authorities[1].GetType(), check.Equals, services.UserCA)
|
||||
c.Assert(len(authorities[1].GetCheckingKeys()), check.Equals, 1)
|
||||
require.Len(t, authorities, 2)
|
||||
require.Equal(t, "cluster-a", authorities[0].GetClusterName())
|
||||
require.Equal(t, services.HostCA, authorities[0].GetType())
|
||||
require.Len(t, authorities[0].GetCheckingKeys(), 1)
|
||||
require.Equal(t, "cluster-a", authorities[1].GetClusterName())
|
||||
require.Equal(t, services.UserCA, authorities[1].GetType())
|
||||
require.Len(t, authorities[1].GetCheckingKeys(), 1)
|
||||
_, _, _, _, err = ssh.ParseAuthorizedKey(authorities[1].GetCheckingKeys()[0])
|
||||
c.Assert(err, check.IsNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
tunnels := conf.ReverseTunnels
|
||||
c.Assert(len(tunnels), check.Equals, 1)
|
||||
c.Assert(tunnels[0].GetClusterName(), check.Equals, "cluster-a")
|
||||
c.Assert(len(tunnels[0].GetDialAddrs()), check.Equals, 2)
|
||||
c.Assert(tunnels[0].GetDialAddrs()[0], check.Equals, "tcp://one:3024")
|
||||
c.Assert(tunnels[0].GetDialAddrs()[1], check.Equals, "tcp://two:3024")
|
||||
require.Len(t, tunnels, 1)
|
||||
require.Equal(t, "cluster-a", tunnels[0].GetClusterName())
|
||||
require.Len(t, tunnels[0].GetDialAddrs(), 2)
|
||||
require.Equal(t, "tcp://one:3024", tunnels[0].GetDialAddrs()[0])
|
||||
require.Equal(t, "tcp://two:3024", tunnels[0].GetDialAddrs()[1])
|
||||
|
||||
// invalid data:
|
||||
err = readTrustedClusters([]TrustedCluster{
|
||||
|
@ -406,15 +398,16 @@ func (s *ConfigTestSuite) TestTrustedClusters(c *check.C) {
|
|||
TunnelAddr: "one,two",
|
||||
},
|
||||
}, &conf)
|
||||
c.Assert(err, check.NotNil)
|
||||
c.Assert(err, check.ErrorMatches, "^.*reading trusted cluster keys.*$")
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "reading trusted cluster keys")
|
||||
err = readTrustedClusters([]TrustedCluster{
|
||||
{
|
||||
KeyFile: "../../fixtures/trusted_clusters/cluster-a",
|
||||
TunnelAddr: "one,two",
|
||||
},
|
||||
}, &conf)
|
||||
c.Assert(err, check.ErrorMatches, ".*needs allow_logins parameter")
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "needs allow_logins parameter")
|
||||
conf.ReverseTunnels = nil
|
||||
err = readTrustedClusters([]TrustedCluster{
|
||||
{
|
||||
|
@ -423,12 +416,12 @@ func (s *ConfigTestSuite) TestTrustedClusters(c *check.C) {
|
|||
TunnelAddr: "",
|
||||
},
|
||||
}, &conf)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(len(conf.ReverseTunnels), check.Equals, 0)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, conf.ReverseTunnels, 0)
|
||||
}
|
||||
|
||||
// TestFileConfigCheck makes sure we don't start with invalid settings.
|
||||
func (s *ConfigTestSuite) TestFileConfigCheck(c *check.C) {
|
||||
func TestFileConfigCheck(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
inConfig string
|
||||
|
@ -472,33 +465,34 @@ teleport:
|
|||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
comment := check.Commentf(tt.desc)
|
||||
comment := fmt.Sprintf(tt.desc)
|
||||
|
||||
_, err := ReadConfig(bytes.NewBufferString(tt.inConfig))
|
||||
if tt.outError {
|
||||
c.Assert(err, check.NotNil, comment)
|
||||
require.Error(t, err, comment)
|
||||
} else {
|
||||
c.Assert(err, check.IsNil, comment)
|
||||
require.NoError(t, err, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ConfigTestSuite) TestApplyConfig(c *check.C) {
|
||||
tokenPath := filepath.Join(s.tempDir, "small-config-token")
|
||||
func TestApplyConfig(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
tokenPath := filepath.Join(tempDir, "small-config-token")
|
||||
err := ioutil.WriteFile(tokenPath, []byte("join-token"), 0644)
|
||||
c.Assert(err, check.IsNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
conf, err := ReadConfig(bytes.NewBufferString(fmt.Sprintf(SmallConfigString, tokenPath)))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(conf, check.NotNil)
|
||||
c.Assert(conf.Proxy.PublicAddr, check.DeepEquals, utils.Strings{"web3:443"})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, conf)
|
||||
require.Equal(t, utils.Strings{"web3:443"}, conf.Proxy.PublicAddr)
|
||||
|
||||
cfg := service.MakeDefaultConfig()
|
||||
err = ApplyFileConfig(conf, cfg)
|
||||
c.Assert(err, check.IsNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
c.Assert(cfg.Token, check.Equals, "join-token")
|
||||
c.Assert(cfg.Auth.StaticTokens.GetStaticTokens(), check.DeepEquals, services.ProvisionTokensFromV1([]services.ProvisionTokenV1{
|
||||
require.Equal(t, "join-token", cfg.Token)
|
||||
require.Equal(t, services.ProvisionTokensFromV1([]services.ProvisionTokenV1{
|
||||
{
|
||||
Token: "xxx",
|
||||
Roles: teleport.Roles([]teleport.Role{"Proxy", "Node"}),
|
||||
|
@ -509,47 +503,46 @@ func (s *ConfigTestSuite) TestApplyConfig(c *check.C) {
|
|||
Roles: teleport.Roles([]teleport.Role{"Auth"}),
|
||||
Expires: time.Unix(0, 0).UTC(),
|
||||
},
|
||||
}))
|
||||
c.Assert(cfg.Auth.ClusterName.GetClusterName(), check.Equals, "magadan")
|
||||
c.Assert(cfg.Auth.ClusterConfig.GetLocalAuth(), check.Equals, true)
|
||||
c.Assert(cfg.AdvertiseIP, check.Equals, "10.10.10.1")
|
||||
}), cfg.Auth.StaticTokens.GetStaticTokens())
|
||||
require.Equal(t, "magadan", cfg.Auth.ClusterName.GetClusterName())
|
||||
require.True(t, cfg.Auth.ClusterConfig.GetLocalAuth())
|
||||
require.Equal(t, "10.10.10.1", cfg.AdvertiseIP)
|
||||
|
||||
c.Assert(cfg.Proxy.Enabled, check.Equals, true)
|
||||
c.Assert(cfg.Proxy.WebAddr.FullAddress(), check.Equals, "tcp://webhost:3080")
|
||||
c.Assert(cfg.Proxy.ReverseTunnelListenAddr.FullAddress(), check.Equals, "tcp://tunnelhost:1001")
|
||||
require.True(t, cfg.Proxy.Enabled)
|
||||
require.Equal(t, "tcp://webhost:3080", cfg.Proxy.WebAddr.FullAddress())
|
||||
require.Equal(t, "tcp://tunnelhost:1001", cfg.Proxy.ReverseTunnelListenAddr.FullAddress())
|
||||
}
|
||||
|
||||
// TestApplyConfigNoneEnabled makes sure that if a section is not enabled,
|
||||
// it's fields are not read in.
|
||||
func (s *ConfigTestSuite) TestApplyConfigNoneEnabled(c *check.C) {
|
||||
func TestApplyConfigNoneEnabled(t *testing.T) {
|
||||
conf, err := ReadConfig(bytes.NewBufferString(NoServicesConfigString))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(conf, check.NotNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := service.MakeDefaultConfig()
|
||||
err = ApplyFileConfig(conf, cfg)
|
||||
c.Assert(err, check.IsNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
c.Assert(cfg.Auth.Enabled, check.Equals, false)
|
||||
c.Assert(cfg.Auth.PublicAddrs, check.HasLen, 0)
|
||||
c.Assert(cfg.Proxy.Enabled, check.Equals, false)
|
||||
c.Assert(cfg.Proxy.PublicAddrs, check.HasLen, 0)
|
||||
c.Assert(cfg.SSH.Enabled, check.Equals, false)
|
||||
c.Assert(cfg.SSH.PublicAddrs, check.HasLen, 0)
|
||||
c.Assert(cfg.Apps.Enabled, check.Equals, false)
|
||||
c.Assert(cfg.Databases.Enabled, check.Equals, false)
|
||||
require.False(t, cfg.Auth.Enabled)
|
||||
require.Len(t, cfg.Auth.PublicAddrs, 0)
|
||||
require.False(t, cfg.Proxy.Enabled)
|
||||
require.Len(t, cfg.Proxy.PublicAddrs, 0)
|
||||
require.False(t, cfg.SSH.Enabled)
|
||||
require.Len(t, cfg.SSH.PublicAddrs, 0)
|
||||
require.False(t, cfg.Apps.Enabled)
|
||||
require.False(t, cfg.Databases.Enabled)
|
||||
}
|
||||
|
||||
func (s *ConfigTestSuite) TestBackendDefaults(c *check.C) {
|
||||
func TestBackendDefaults(t *testing.T) {
|
||||
read := func(val string) *service.Config {
|
||||
// Default value is lite backend.
|
||||
conf, err := ReadConfig(bytes.NewBufferString(val))
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(conf, check.NotNil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, conf)
|
||||
|
||||
cfg := service.MakeDefaultConfig()
|
||||
err = ApplyFileConfig(conf, cfg)
|
||||
c.Assert(err, check.IsNil)
|
||||
require.NoError(t, err)
|
||||
return cfg
|
||||
}
|
||||
|
||||
|
@ -557,8 +550,8 @@ func (s *ConfigTestSuite) TestBackendDefaults(c *check.C) {
|
|||
cfg := read(`teleport:
|
||||
data_dir: /var/lib/teleport
|
||||
`)
|
||||
c.Assert(cfg.Auth.StorageConfig.Type, check.Equals, lite.GetName())
|
||||
c.Assert(cfg.Auth.StorageConfig.Params[defaults.BackendPath], check.Equals, filepath.Join("/var/lib/teleport", defaults.BackendDir))
|
||||
require.Equal(t, cfg.Auth.StorageConfig.Type, lite.GetName())
|
||||
require.Equal(t, cfg.Auth.StorageConfig.Params[defaults.BackendPath], filepath.Join("/var/lib/teleport", defaults.BackendDir))
|
||||
|
||||
// If no path is specified, the default is picked. In addition, internally
|
||||
// dir gets converted into lite.
|
||||
|
@ -567,8 +560,8 @@ func (s *ConfigTestSuite) TestBackendDefaults(c *check.C) {
|
|||
storage:
|
||||
type: dir
|
||||
`)
|
||||
c.Assert(cfg.Auth.StorageConfig.Type, check.Equals, lite.GetName())
|
||||
c.Assert(cfg.Auth.StorageConfig.Params[defaults.BackendPath], check.Equals, filepath.Join("/var/lib/teleport", defaults.BackendDir))
|
||||
require.Equal(t, cfg.Auth.StorageConfig.Type, lite.GetName())
|
||||
require.Equal(t, cfg.Auth.StorageConfig.Params[defaults.BackendPath], filepath.Join("/var/lib/teleport", defaults.BackendDir))
|
||||
|
||||
// Support custom paths for dir/lite backends.
|
||||
cfg = read(`teleport:
|
||||
|
@ -577,19 +570,19 @@ func (s *ConfigTestSuite) TestBackendDefaults(c *check.C) {
|
|||
type: dir
|
||||
path: /var/lib/teleport/mybackend
|
||||
`)
|
||||
c.Assert(cfg.Auth.StorageConfig.Type, check.Equals, lite.GetName())
|
||||
c.Assert(cfg.Auth.StorageConfig.Params[defaults.BackendPath], check.Equals, "/var/lib/teleport/mybackend")
|
||||
require.Equal(t, cfg.Auth.StorageConfig.Type, lite.GetName())
|
||||
require.Equal(t, cfg.Auth.StorageConfig.Params[defaults.BackendPath], "/var/lib/teleport/mybackend")
|
||||
|
||||
// Kubernetes proxy is disabled by default.
|
||||
cfg = read(`teleport:
|
||||
data_dir: /var/lib/teleport
|
||||
`)
|
||||
c.Assert(cfg.Proxy.Kube.Enabled, check.Equals, false)
|
||||
require.False(t, cfg.Proxy.Kube.Enabled)
|
||||
}
|
||||
|
||||
// TestParseKey ensures that keys are parsed correctly if they are in
|
||||
// authorized_keys format or known_hosts format.
|
||||
func (s *ConfigTestSuite) TestParseKey(c *check.C) {
|
||||
func TestParseKey(t *testing.T) {
|
||||
tests := []struct {
|
||||
inCABytes []byte
|
||||
outType services.CertAuthType
|
||||
|
@ -617,16 +610,16 @@ func (s *ConfigTestSuite) TestParseKey(c *check.C) {
|
|||
|
||||
// run tests
|
||||
for i, tt := range tests {
|
||||
comment := check.Commentf("Test %v", i)
|
||||
comment := fmt.Sprintf("Test %v", i)
|
||||
|
||||
ca, _, err := parseCAKey(tt.inCABytes, []string{"foo"})
|
||||
c.Assert(err, check.IsNil, comment)
|
||||
c.Assert(ca.GetType(), check.Equals, tt.outType)
|
||||
c.Assert(ca.GetClusterName(), check.Equals, tt.outClusterName)
|
||||
require.NoError(t, err, comment)
|
||||
require.Equal(t, ca.GetType(), tt.outType)
|
||||
require.Equal(t, ca.GetClusterName(), tt.outClusterName)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ConfigTestSuite) TestParseCachePolicy(c *check.C) {
|
||||
func TestParseCachePolicy(t *testing.T) {
|
||||
tcs := []struct {
|
||||
in *CachePolicy
|
||||
out *service.CachePolicy
|
||||
|
@ -641,13 +634,13 @@ func (s *ConfigTestSuite) TestParseCachePolicy(c *check.C) {
|
|||
{in: &CachePolicy{Type: "memsql"}, err: trace.BadParameter("unsupported backend")},
|
||||
}
|
||||
for i, tc := range tcs {
|
||||
comment := check.Commentf("test case #%v", i)
|
||||
comment := fmt.Sprintf("test case #%v", i)
|
||||
out, err := tc.in.Parse()
|
||||
if tc.err != nil {
|
||||
c.Assert(err, check.FitsTypeOf, err, comment)
|
||||
require.IsType(t, tc.err, err, comment)
|
||||
} else {
|
||||
c.Assert(err, check.IsNil, comment)
|
||||
fixtures.DeepCompare(c, out, tc.out)
|
||||
require.NoError(t, err, comment)
|
||||
require.Equal(t, out, tc.out, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -860,7 +853,7 @@ func makeConfigFixture() string {
|
|||
return conf.DebugDumpToYAML()
|
||||
}
|
||||
|
||||
func (s *ConfigTestSuite) TestPermitUserEnvironment(c *check.C) {
|
||||
func TestPermitUserEnvironment(t *testing.T) {
|
||||
tests := []struct {
|
||||
inConfigString string
|
||||
inPermitUserEnvironment bool
|
||||
|
@ -891,7 +884,7 @@ ssh_service:
|
|||
|
||||
// run tests
|
||||
for i, tt := range tests {
|
||||
comment := check.Commentf("Test %v", i)
|
||||
comment := fmt.Sprintf("Test %v", i)
|
||||
|
||||
clf := CommandLineFlags{
|
||||
ConfigString: base64.StdEncoding.EncodeToString([]byte(tt.inConfigString)),
|
||||
|
@ -900,25 +893,24 @@ ssh_service:
|
|||
cfg := service.MakeDefaultConfig()
|
||||
|
||||
err := Configure(&clf, cfg)
|
||||
c.Assert(err, check.IsNil, comment)
|
||||
|
||||
c.Assert(cfg.SSH.PermitUserEnvironment, check.Equals, tt.outPermitUserEnvironment, comment)
|
||||
require.NoError(t, err, comment)
|
||||
require.Equal(t, tt.outPermitUserEnvironment, cfg.SSH.PermitUserEnvironment, comment)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDebugFlag ensures that the debug command-line flag is correctly set in the config.
|
||||
func (s *ConfigTestSuite) TestDebugFlag(c *check.C) {
|
||||
func TestDebugFlag(t *testing.T) {
|
||||
clf := CommandLineFlags{
|
||||
Debug: true,
|
||||
}
|
||||
cfg := service.MakeDefaultConfig()
|
||||
c.Assert(cfg.Debug, check.Equals, false)
|
||||
require.False(t, cfg.Debug)
|
||||
err := Configure(&clf, cfg)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(cfg.Debug, check.Equals, true)
|
||||
require.NoError(t, err)
|
||||
require.True(t, cfg.Debug)
|
||||
}
|
||||
|
||||
func (s *ConfigTestSuite) TestLicenseFile(c *check.C) {
|
||||
func TestLicenseFile(t *testing.T) {
|
||||
testCases := []struct {
|
||||
path string
|
||||
result string
|
||||
|
@ -941,22 +933,21 @@ func (s *ConfigTestSuite) TestLicenseFile(c *check.C) {
|
|||
}
|
||||
|
||||
cfg := service.MakeDefaultConfig()
|
||||
c.Assert(cfg.Auth.LicenseFile, check.Equals,
|
||||
filepath.Join(defaults.DataDir, defaults.LicenseFile))
|
||||
require.Equal(t, filepath.Join(defaults.DataDir, defaults.LicenseFile), cfg.Auth.LicenseFile)
|
||||
|
||||
for _, tc := range testCases {
|
||||
fc := new(FileConfig)
|
||||
c.Assert(fc.CheckAndSetDefaults(), check.IsNil)
|
||||
require.NoError(t, fc.CheckAndSetDefaults())
|
||||
fc.Auth.LicenseFile = tc.path
|
||||
err := ApplyFileConfig(fc, cfg)
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(cfg.Auth.LicenseFile, check.Equals, tc.result)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.result, cfg.Auth.LicenseFile)
|
||||
}
|
||||
}
|
||||
|
||||
// TestFIPS makes sure configuration is correctly updated/enforced when in
|
||||
// FedRAMP/FIPS 140-2 mode.
|
||||
func (s *ConfigTestSuite) TestFIPS(c *check.C) {
|
||||
func TestFIPS(t *testing.T) {
|
||||
tests := []struct {
|
||||
inConfigString string
|
||||
inFIPSMode bool
|
||||
|
@ -985,7 +976,7 @@ func (s *ConfigTestSuite) TestFIPS(c *check.C) {
|
|||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
comment := check.Commentf("Test %v", i)
|
||||
comment := fmt.Sprintf("Test %v", i)
|
||||
|
||||
clf := CommandLineFlags{
|
||||
ConfigString: base64.StdEncoding.EncodeToString([]byte(tt.inConfigString)),
|
||||
|
@ -998,9 +989,9 @@ func (s *ConfigTestSuite) TestFIPS(c *check.C) {
|
|||
|
||||
err := Configure(&clf, cfg)
|
||||
if tt.outError {
|
||||
c.Assert(err, check.NotNil, comment)
|
||||
require.Error(t, err, comment)
|
||||
} else {
|
||||
c.Assert(err, check.IsNil, comment)
|
||||
require.NoError(t, err, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1091,10 +1082,10 @@ func TestProxyKube(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *ConfigTestSuite) TestApps(c *check.C) {
|
||||
func TestApps(t *testing.T) {
|
||||
tests := []struct {
|
||||
inConfigString string
|
||||
inComment check.CommentInterface
|
||||
inComment string
|
||||
outError bool
|
||||
}{
|
||||
{
|
||||
|
@ -1107,7 +1098,7 @@ app_service:
|
|||
public_addr: "foo.example.com"
|
||||
uri: "http://127.0.0.1:8080"
|
||||
`,
|
||||
inComment: check.Commentf("config is valid"),
|
||||
inComment: "config is valid",
|
||||
outError: false,
|
||||
},
|
||||
{
|
||||
|
@ -1119,7 +1110,7 @@ app_service:
|
|||
public_addr: "foo.example.com"
|
||||
uri: "http://127.0.0.1:8080"
|
||||
`,
|
||||
inComment: check.Commentf("config is missing name"),
|
||||
inComment: "config is missing name",
|
||||
outError: true,
|
||||
},
|
||||
{
|
||||
|
@ -1131,7 +1122,7 @@ app_service:
|
|||
name: foo
|
||||
uri: "http://127.0.0.1:8080"
|
||||
`,
|
||||
inComment: check.Commentf("config is valid"),
|
||||
inComment: "config is valid",
|
||||
outError: false,
|
||||
},
|
||||
{
|
||||
|
@ -1143,7 +1134,7 @@ app_service:
|
|||
name: foo
|
||||
public_addr: "foo.example.com"
|
||||
`,
|
||||
inComment: check.Commentf("config is missing internal address"),
|
||||
inComment: "config is missing internal address",
|
||||
outError: true,
|
||||
},
|
||||
}
|
||||
|
@ -1155,55 +1146,55 @@ app_service:
|
|||
cfg := service.MakeDefaultConfig()
|
||||
|
||||
err := Configure(&clf, cfg)
|
||||
c.Assert(err != nil, check.Equals, tt.outError, tt.inComment)
|
||||
require.Equal(t, err != nil, tt.outError, tt.inComment)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAppsCLF checks that validation runs on application configuration passed
|
||||
// in on the command line.
|
||||
func (s *ConfigTestSuite) TestAppsCLF(c *check.C) {
|
||||
func TestAppsCLF(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc check.CommentInterface
|
||||
desc string
|
||||
inRoles string
|
||||
inAppName string
|
||||
inAppURI string
|
||||
outError error
|
||||
}{
|
||||
{
|
||||
desc: check.Commentf("role provided, valid name and uri"),
|
||||
desc: "role provided, valid name and uri",
|
||||
inRoles: defaults.RoleApp,
|
||||
inAppName: "foo",
|
||||
inAppURI: "http://localhost:8080",
|
||||
outError: nil,
|
||||
},
|
||||
{
|
||||
desc: check.Commentf("role provided, name not provided"),
|
||||
desc: "role provided, name not provided",
|
||||
inRoles: defaults.RoleApp,
|
||||
inAppName: "",
|
||||
inAppURI: "http://localhost:8080",
|
||||
outError: trace.BadParameter(""),
|
||||
},
|
||||
{
|
||||
desc: check.Commentf("role provided, uri not provided"),
|
||||
desc: "role provided, uri not provided",
|
||||
inRoles: defaults.RoleApp,
|
||||
inAppName: "foo",
|
||||
inAppURI: "",
|
||||
outError: trace.BadParameter(""),
|
||||
},
|
||||
{
|
||||
desc: check.Commentf("valid name and uri"),
|
||||
desc: "valid name and uri",
|
||||
inAppName: "foo",
|
||||
inAppURI: "http://localhost:8080",
|
||||
outError: nil,
|
||||
},
|
||||
{
|
||||
desc: check.Commentf("invalid name"),
|
||||
desc: "invalid name",
|
||||
inAppName: "-foo",
|
||||
inAppURI: "http://localhost:8080",
|
||||
outError: trace.BadParameter(""),
|
||||
},
|
||||
{
|
||||
desc: check.Commentf("missing uri"),
|
||||
desc: "missing uri",
|
||||
inAppName: "foo",
|
||||
outError: trace.BadParameter(""),
|
||||
},
|
||||
|
@ -1218,15 +1209,15 @@ func (s *ConfigTestSuite) TestAppsCLF(c *check.C) {
|
|||
cfg := service.MakeDefaultConfig()
|
||||
err := Configure(&clf, cfg)
|
||||
if err != nil {
|
||||
c.Assert(err, check.FitsTypeOf, tt.outError)
|
||||
require.IsType(t, err, tt.outError, tt.desc)
|
||||
} else {
|
||||
c.Assert(err, check.IsNil)
|
||||
require.NoError(t, err, tt.desc)
|
||||
}
|
||||
if tt.outError != nil {
|
||||
continue
|
||||
}
|
||||
c.Assert(cfg.Apps.Enabled, check.Equals, true)
|
||||
c.Assert(cfg.Apps.Apps, check.HasLen, 1)
|
||||
require.True(t, cfg.Apps.Enabled, tt.desc)
|
||||
require.Len(t, cfg.Apps.Apps, 1, tt.desc)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2015-2018 Gravitational, Inc.
|
||||
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.
|
||||
|
@ -48,11 +48,6 @@ import (
|
|||
"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
|
||||
|
@ -325,28 +320,30 @@ func ReadConfig(reader io.Reader) (*FileConfig, error) {
|
|||
return &fc, nil
|
||||
}
|
||||
|
||||
// MakeSampleFileConfig returns a sample config structure populated by defaults,
|
||||
// useful to generate sample configuration files
|
||||
func MakeSampleFileConfig() (fc *FileConfig, err error) {
|
||||
// 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
|
||||
}
|
||||
|
||||
// MakeSampleFileConfig returns a sample config to start
|
||||
// a standalone server
|
||||
func MakeSampleFileConfig(flags SampleFlags) (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:
|
||||
// SSH config:
|
||||
var s SSH
|
||||
s.EnabledFlag = "yes"
|
||||
s.ListenAddress = conf.SSH.Addr.Addr
|
||||
|
@ -356,29 +353,29 @@ func MakeSampleFileConfig() (fc *FileConfig, err error) {
|
|||
Command: []string{"hostname"},
|
||||
Period: time.Minute,
|
||||
},
|
||||
{
|
||||
Name: "arch",
|
||||
Command: []string{"uname", "-p"},
|
||||
Period: time.Hour,
|
||||
},
|
||||
}
|
||||
s.Labels = map[string]string{
|
||||
"env": "staging",
|
||||
"env": "example",
|
||||
}
|
||||
|
||||
// sample Auth config:
|
||||
// Auth config:
|
||||
var a Auth
|
||||
a.ListenAddress = conf.Auth.SSHAddr.Addr
|
||||
a.ClusterName = ClusterName(flags.ClusterName)
|
||||
a.EnabledFlag = "yes"
|
||||
a.StaticTokens = []StaticToken{StaticToken(fmt.Sprintf("proxy,node:%s", randomJoinToken))}
|
||||
a.LicenseFile = "/path/to/license-if-using-teleport-enterprise.pem"
|
||||
|
||||
if flags.LicensePath != "" {
|
||||
a.LicenseFile = flags.LicensePath
|
||||
}
|
||||
|
||||
// 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
|
||||
if flags.ACMEEnabled {
|
||||
p.ACME.EnabledFlag = "yes"
|
||||
p.ACME.Email = flags.ACMEEmail
|
||||
}
|
||||
|
||||
fc = &FileConfig{
|
||||
Global: g,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016-2019 Gravitational, Inc.
|
||||
Copyright 2016-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.
|
||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -179,18 +180,39 @@ func UserMessageFromError(err error) string {
|
|||
return trace.DebugReport(err)
|
||||
}
|
||||
if err != nil {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintln(&buf, Color(Red, "ERROR:"))
|
||||
// If the error is a trace error, check if it has a user message embedded in
|
||||
// it, if it does, print it, otherwise escape and print the original error.
|
||||
if er, ok := err.(*trace.TraceErr); ok {
|
||||
if er.Message != "" {
|
||||
return fmt.Sprintf("error: %v", EscapeControl(er.Message))
|
||||
for _, message := range er.Messages {
|
||||
fmt.Fprintln(&buf, "\t"+EscapeControl(message))
|
||||
}
|
||||
fmt.Fprintln(&buf, "\t"+EscapeControl(trace.Unwrap(er).Error()))
|
||||
} else {
|
||||
fmt.Fprintln(&buf, EscapeControl(err.Error()))
|
||||
}
|
||||
return fmt.Sprintf("error: %v", EscapeControl(err.Error()))
|
||||
return buf.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
const (
|
||||
// Red is an escape code for red terminal color
|
||||
Red = 31
|
||||
// Yellow is an escape code for yellow terminal color
|
||||
Yellow = 33
|
||||
// Blue is an escape code for blue terminal color
|
||||
Blue = 36
|
||||
// Gray is an escape code for gray terminal color
|
||||
Gray = 37
|
||||
)
|
||||
|
||||
// Color formats the string in a terminal escape color
|
||||
func Color(color int, v interface{}) string {
|
||||
return fmt.Sprintf("\x1b[%dm%v\x1b[0m", color, v)
|
||||
}
|
||||
|
||||
// Consolef prints the same message to a 'ui console' (if defined) and also to
|
||||
// the logger with INFO priority
|
||||
func Consolef(w io.Writer, log log.FieldLogger, component, msg string, params ...interface{}) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2015-2017 Gravitational, Inc.
|
||||
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.
|
||||
|
@ -19,6 +19,7 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
|
@ -28,6 +29,7 @@ import (
|
|||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/lib/config"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/modules"
|
||||
"github.com/gravitational/teleport/lib/service"
|
||||
"github.com/gravitational/teleport/lib/srv"
|
||||
"github.com/gravitational/teleport/lib/sshutils/scp"
|
||||
|
@ -64,11 +66,12 @@ func Run(options Options) (executedCommand string, conf *service.Config) {
|
|||
// define global flags:
|
||||
var ccf config.CommandLineFlags
|
||||
var scpFlags scp.Flags
|
||||
var dumpFlags dumpFlags
|
||||
|
||||
// define commands:
|
||||
start := app.Command("start", "Starts the Teleport service.")
|
||||
status := app.Command("status", "Print the status of the current SSH session.")
|
||||
dump := app.Command("configure", "Print the sample config file into stdout.")
|
||||
dump := app.Command("configure", "Generate a simple config file to get started.")
|
||||
ver := app.Command("version", "Print the version.")
|
||||
scpc := app.Command("scp", "Server-side implementation of SCP.").Hidden()
|
||||
exec := app.Command("exec", "Used internally by Teleport to re-exec itself to run a command.").Hidden()
|
||||
|
@ -165,6 +168,17 @@ func Run(options Options) (executedCommand string, conf *service.Config) {
|
|||
scpc.Flag("local-addr", "local address which accepted the request").StringVar(&scpFlags.LocalAddr)
|
||||
scpc.Arg("target", "").StringsVar(&scpFlags.Target)
|
||||
|
||||
// dump flags
|
||||
dump.Flag("cluster-name",
|
||||
"Unique cluster name, e.g. example.com.").StringVar(&dumpFlags.ClusterName)
|
||||
dump.Flag("output",
|
||||
"Write to stdout with -o=stdout, default config file with -o=file or custom path with -o=file:///path").Short('o').Default(
|
||||
teleport.SchemeStdout).StringVar(&dumpFlags.output)
|
||||
dump.Flag("acme",
|
||||
"Get automatic certificate from Letsencrypt.org using ACME.").BoolVar(&dumpFlags.ACMEEnabled)
|
||||
dump.Flag("acme-email",
|
||||
"Email to receive updates from Letsencrypt.org.").StringVar(&dumpFlags.ACMEEmail)
|
||||
|
||||
// parse CLI commands+flags:
|
||||
command, err := app.Parse(options.Args)
|
||||
if err != nil {
|
||||
|
@ -194,7 +208,7 @@ func Run(options Options) (executedCommand string, conf *service.Config) {
|
|||
case status.FullCommand():
|
||||
err = onStatus()
|
||||
case dump.FullCommand():
|
||||
err = onConfigDump()
|
||||
err = onConfigDump(dumpFlags)
|
||||
case exec.FullCommand():
|
||||
err = onExec()
|
||||
case forward.FullCommand():
|
||||
|
@ -237,13 +251,74 @@ func onStatus() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type dumpFlags struct {
|
||||
config.SampleFlags
|
||||
output string
|
||||
}
|
||||
|
||||
func (flags *dumpFlags) CheckAndSetDefaults() error {
|
||||
if flags.output == "" || flags.output == teleport.SchemeFile {
|
||||
flags.output = teleport.SchemeFile + "://" + defaults.ConfigFilePath
|
||||
} else if flags.output == teleport.SchemeStdout {
|
||||
flags.output = teleport.SchemeStdout + "://"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// onConfigDump is the handler for "configure" CLI command
|
||||
func onConfigDump() error {
|
||||
sfc, err := config.MakeSampleFileConfig()
|
||||
func onConfigDump(flags dumpFlags) error {
|
||||
if err := flags.CheckAndSetDefaults(); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
uri, err := url.Parse(flags.output)
|
||||
if err != nil {
|
||||
return trace.BadParameter("could not parse output value %q, use --output=%q",
|
||||
flags.output, defaults.ConfigFilePath)
|
||||
}
|
||||
|
||||
if modules.GetModules().BuildType() != modules.BuildOSS {
|
||||
flags.LicensePath = filepath.Join(defaults.DataDir, "license.pem")
|
||||
}
|
||||
|
||||
sfc, err := config.MakeSampleFileConfig(flags.SampleFlags)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
fmt.Printf("%s\n%s\n", sampleConfComment, sfc.DebugDumpToYAML())
|
||||
switch uri.Scheme {
|
||||
case teleport.SchemeStdout:
|
||||
fmt.Printf("%s\n%s\n", sampleConfComment, sfc.DebugDumpToYAML())
|
||||
case teleport.SchemeFile, "":
|
||||
if uri.Path == "" {
|
||||
return trace.BadParameter("missing path in --output=%q", uri)
|
||||
}
|
||||
if !filepath.IsAbs(uri.Path) {
|
||||
return trace.BadParameter("please use absolute path for file %v", uri.Path)
|
||||
}
|
||||
f, err := os.OpenFile(uri.Path, os.O_RDWR|os.O_CREATE|os.O_EXCL, teleport.FileMaskOwnerOnly)
|
||||
err = trace.ConvertSystemError(err)
|
||||
if err != nil {
|
||||
if trace.IsAlreadyExists(err) {
|
||||
return trace.AlreadyExists("will not overwrite existing file %v, rm -f %v and try again", uri.Path, uri.Path)
|
||||
}
|
||||
return trace.Wrap(err, "could not write to config file, missing sudo?")
|
||||
}
|
||||
if _, err := f.Write([]byte(sfc.DebugDumpToYAML())); err != nil {
|
||||
f.Close()
|
||||
return trace.Wrap(trace.ConvertSystemError(err), "could not write to config file, missing sudo?")
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return trace.Wrap(err, "could not close file %v", uri.Path)
|
||||
}
|
||||
if modules.GetModules().BuildType() == modules.BuildOSS {
|
||||
fmt.Printf("Wrote config to file %q. Now you can start the server. Happy Teleporting!\n", uri.Path)
|
||||
} else {
|
||||
fmt.Printf("Wrote config to file %q. Add your license file to %v and start the server. Happy Teleporting!\n", uri.Path, flags.LicensePath)
|
||||
}
|
||||
default:
|
||||
return trace.BadParameter(
|
||||
"unsupported --output=%v, use path for example --output=%v", uri.Scheme, defaults.ConfigFilePath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2016 Gravitational, Inc.
|
||||
Copyright 2016-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.
|
||||
|
@ -22,107 +22,121 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gravitational/teleport/lib/config"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"gopkg.in/check.v1"
|
||||
"github.com/gravitational/trace"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// bootstrap check
|
||||
func TestTeleportMain(t *testing.T) { check.TestingT(t) }
|
||||
|
||||
// register test suite
|
||||
type MainTestSuite struct {
|
||||
hostname string
|
||||
configFile string
|
||||
}
|
||||
|
||||
var _ = check.Suite(&MainTestSuite{})
|
||||
|
||||
func (s *MainTestSuite) SetUpTest(c *check.C) {
|
||||
func TestTeleportMain(t *testing.T) {
|
||||
utils.InitLoggerForTests(testing.Verbose())
|
||||
}
|
||||
|
||||
func (s *MainTestSuite) SetUpSuite(c *check.C) {
|
||||
var err error
|
||||
// get the hostname
|
||||
s.hostname, err = os.Hostname()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// generate the fixture config file
|
||||
dirname, err := ioutil.TempDir("", "teleport")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s.configFile = filepath.Join(dirname, "teleport.yaml")
|
||||
err = ioutil.WriteFile(s.configFile, []byte(YAMLConfig), 0770)
|
||||
c.Assert(err, check.IsNil)
|
||||
hostname, err := os.Hostname()
|
||||
require.NoError(t, err)
|
||||
|
||||
// set imprtant defaults to test-mode (non-existing files&locations)
|
||||
// generate the fixture config file
|
||||
configFile := filepath.Join(t.TempDir(), "teleport.yaml")
|
||||
err = ioutil.WriteFile(configFile, []byte(YAMLConfig), 0660)
|
||||
require.NoError(t, err)
|
||||
|
||||
// set defaults to test-mode (non-existing files&locations)
|
||||
defaults.ConfigFilePath = "/tmp/teleport/etc/teleport.yaml"
|
||||
defaults.DataDir = "/tmp/teleport/var/lib/teleport"
|
||||
|
||||
t.Run("Default", func(t *testing.T) {
|
||||
cmd, conf := Run(Options{
|
||||
Args: []string{"start"},
|
||||
InitOnly: true,
|
||||
})
|
||||
require.Equal(t, "start", cmd)
|
||||
require.Equal(t, hostname, conf.Hostname)
|
||||
require.Equal(t, "/tmp/teleport/var/lib/teleport", conf.DataDir)
|
||||
require.True(t, conf.Auth.Enabled)
|
||||
require.True(t, conf.SSH.Enabled)
|
||||
require.True(t, conf.Proxy.Enabled)
|
||||
require.Equal(t, os.Stdout, conf.Console)
|
||||
require.Equal(t, log.ErrorLevel, log.GetLevel())
|
||||
})
|
||||
|
||||
t.Run("RolesFlag", func(t *testing.T) {
|
||||
cmd, conf := Run(Options{
|
||||
Args: []string{"start", "--roles=node"},
|
||||
InitOnly: true,
|
||||
})
|
||||
require.True(t, conf.SSH.Enabled)
|
||||
require.False(t, conf.Auth.Enabled)
|
||||
require.False(t, conf.Proxy.Enabled)
|
||||
require.Equal(t, "start", cmd)
|
||||
|
||||
cmd, conf = Run(Options{
|
||||
Args: []string{"start", "--roles=proxy"},
|
||||
InitOnly: true,
|
||||
})
|
||||
require.False(t, conf.SSH.Enabled)
|
||||
require.False(t, conf.Auth.Enabled)
|
||||
require.True(t, conf.Proxy.Enabled)
|
||||
require.Equal(t, "start", cmd)
|
||||
|
||||
cmd, conf = Run(Options{
|
||||
Args: []string{"start", "--roles=auth"},
|
||||
InitOnly: true,
|
||||
})
|
||||
require.False(t, conf.SSH.Enabled)
|
||||
require.True(t, conf.Auth.Enabled)
|
||||
require.False(t, conf.Proxy.Enabled)
|
||||
require.Equal(t, "start", cmd)
|
||||
})
|
||||
|
||||
t.Run("ConfigFile", func(t *testing.T) {
|
||||
cmd, conf := Run(Options{
|
||||
Args: []string{"start", "--roles=node", "--labels=a=a1,b=b1", "--config=" + configFile},
|
||||
InitOnly: true,
|
||||
})
|
||||
require.Equal(t, "start", cmd)
|
||||
require.True(t, conf.SSH.Enabled)
|
||||
require.False(t, conf.Auth.Enabled)
|
||||
require.False(t, conf.Proxy.Enabled)
|
||||
require.Equal(t, log.DebugLevel, conf.Log.GetLevel())
|
||||
require.Equal(t, "hvostongo.example.org", conf.Hostname)
|
||||
require.Equal(t, "xxxyyy", conf.Token)
|
||||
require.Equal(t, "10.5.5.5", conf.AdvertiseIP)
|
||||
require.Equal(t, map[string]string{"a": "a1", "b": "b1"}, conf.SSH.Labels)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *MainTestSuite) TestDefault(c *check.C) {
|
||||
cmd, conf := Run(Options{
|
||||
Args: []string{"start"},
|
||||
InitOnly: true,
|
||||
})
|
||||
c.Assert(cmd, check.Equals, "start")
|
||||
c.Assert(conf.Hostname, check.Equals, s.hostname)
|
||||
c.Assert(conf.DataDir, check.Equals, "/tmp/teleport/var/lib/teleport")
|
||||
c.Assert(conf.Auth.Enabled, check.Equals, true)
|
||||
c.Assert(conf.SSH.Enabled, check.Equals, true)
|
||||
c.Assert(conf.Proxy.Enabled, check.Equals, true)
|
||||
c.Assert(conf.Console, check.Equals, os.Stdout)
|
||||
c.Assert(log.GetLevel(), check.Equals, log.ErrorLevel)
|
||||
}
|
||||
func TestConfigure(t *testing.T) {
|
||||
t.Run("Dump", func(t *testing.T) {
|
||||
err := onConfigDump(dumpFlags{
|
||||
// typo
|
||||
output: "sddout",
|
||||
})
|
||||
require.IsType(t, trace.BadParameter(""), err)
|
||||
|
||||
func (s *MainTestSuite) TestRolesFlag(c *check.C) {
|
||||
cmd, conf := Run(Options{
|
||||
Args: []string{"start", "--roles=node"},
|
||||
InitOnly: true,
|
||||
})
|
||||
c.Assert(conf.SSH.Enabled, check.Equals, true)
|
||||
c.Assert(conf.Auth.Enabled, check.Equals, false)
|
||||
c.Assert(conf.Proxy.Enabled, check.Equals, false)
|
||||
c.Assert(cmd, check.Equals, "start")
|
||||
err = onConfigDump(dumpFlags{
|
||||
output: "file://" + filepath.Join(t.TempDir(), "test"),
|
||||
SampleFlags: config.SampleFlags{
|
||||
ClusterName: "example.com",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
cmd, conf = Run(Options{
|
||||
Args: []string{"start", "--roles=proxy"},
|
||||
InitOnly: true,
|
||||
// stdout
|
||||
err = onConfigDump(dumpFlags{
|
||||
output: "stdout",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
c.Assert(conf.SSH.Enabled, check.Equals, false)
|
||||
c.Assert(conf.Auth.Enabled, check.Equals, false)
|
||||
c.Assert(conf.Proxy.Enabled, check.Equals, true)
|
||||
c.Assert(cmd, check.Equals, "start")
|
||||
|
||||
cmd, conf = Run(Options{
|
||||
Args: []string{"start", "--roles=auth"},
|
||||
InitOnly: true,
|
||||
t.Run("Defaults", func(t *testing.T) {
|
||||
flags := dumpFlags{}
|
||||
err := flags.CheckAndSetDefaults()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
c.Assert(conf.SSH.Enabled, check.Equals, false)
|
||||
c.Assert(conf.Auth.Enabled, check.Equals, true)
|
||||
c.Assert(conf.Proxy.Enabled, check.Equals, false)
|
||||
c.Assert(cmd, check.Equals, "start")
|
||||
}
|
||||
|
||||
func (s *MainTestSuite) TestConfigFile(c *check.C) {
|
||||
cmd, conf := Run(Options{
|
||||
Args: []string{"start", "--roles=node", "--labels=a=a1,b=b1", "--config=" + s.configFile},
|
||||
InitOnly: true,
|
||||
})
|
||||
c.Assert(cmd, check.Equals, "start")
|
||||
c.Assert(conf.SSH.Enabled, check.Equals, true)
|
||||
c.Assert(conf.Auth.Enabled, check.Equals, false)
|
||||
c.Assert(conf.Proxy.Enabled, check.Equals, false)
|
||||
c.Assert(conf.Log.GetLevel(), check.Equals, log.DebugLevel)
|
||||
c.Assert(conf.Hostname, check.Equals, "hvostongo.example.org")
|
||||
c.Assert(conf.Token, check.Equals, "xxxyyy")
|
||||
c.Assert(conf.AdvertiseIP, check.DeepEquals, "10.5.5.5")
|
||||
c.Assert(conf.SSH.Labels, check.DeepEquals, map[string]string{"a": "a1", "b": "b1"})
|
||||
}
|
||||
|
||||
const YAMLConfig = `
|
||||
|
|
|
@ -53,13 +53,11 @@ Examples:
|
|||
`
|
||||
|
||||
sampleConfComment = `#
|
||||
# Sample Teleport configuration file
|
||||
# A Sample Teleport configuration file.
|
||||
# Creates a single proxy, auth and node server.
|
||||
#
|
||||
# Things to update:
|
||||
# 1. ca_pin: Obtain the CA pin hash for joining more nodes by running 'tctl status'
|
||||
# on the auth server once Teleport is running.
|
||||
# 2. license-if-using-teleport-enterprise.pem: If you are an Enterprise customer,
|
||||
# obtain this from https://dashboard.gravitational.com/web/
|
||||
# 1. license.pem: You only need a license from https://dashboard.goteleport.com
|
||||
# if you are an Enterprise customer.
|
||||
#`
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue