Refactor api package and docs to use pkg.go.dev effectively. (#6388)

This commit is contained in:
Brian Joerger
2021-04-20 16:44:17 -07:00
committed by GitHub
parent f2bf2b9356
commit d830ed6db7
41 changed files with 965 additions and 1644 deletions
+10
View File
@@ -0,0 +1,10 @@
This package is documented using a combination of [pkg.go.dev](https://pkg.go.dev/github.com/gravitational/teleport/api/client) and Teleport [Docs](https://goteleport.com/docs/).
## Reference
- [Introduction](https:/goteleport.com/docs/reference/api/introduction/)
- [Getting Started](https:/goteleport.com/docs/reference/api/getting-started/)
- [Architecture](https:/goteleport.com/docs/reference/api/architecture/)
- [pkg.go.dev](https://pkg.go.dev/github.com/gravitational/teleport/api/client/)
- [Using the client](https://pkg.go.dev/github.com/gravitational/teleport/api/client#Client/)
- [Working with credentials](https://pkg.go.dev/github.com/gravitational/teleport/api/client#Credentials/)
+23 -25
View File
@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// Package client holds the implementation of the Teleport gRPC api client.
package client
import (
@@ -28,10 +27,12 @@ import (
"time"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/events"
"github.com/gravitational/teleport/api/utils"
"github.com/golang/protobuf/ptypes/empty"
"github.com/gravitational/trace"
@@ -50,7 +51,12 @@ func init() {
}
}
// Client is a gRPC Client that connects to a teleport server through TLS.
// Client is a gRPC Client that connects to a Teleport Auth server either
// locally or over ssh through a Teleport web proxy or tunnel proxy.
//
// This client can be used to cover a variety of Teleport use cases,
// such as programmatically handling access requests, integrating
// with external tools, or dynamically configuring Teleport.
type Client struct {
// c contains configuration values for the client.
c Config
@@ -71,30 +77,22 @@ type Client struct {
// New creates a new API client with an open connection to a Teleport server.
//
// The server can be an auth server, web proxy, or tunnel proxy. Multiple server
// addresses can be specified in cfg.
// New will try to open a connection with all combinations of addresses and credentials.
// The first successful connection to a server will be used, or an aggregated error will
// be returned if all combinations fail.
//
// TLS credentials are required in cfg, and can be generated by a number of
// convenience functions that pull certificates that can be easily generated with tsh.
// SSH credentials are required to connect to web/tunnel proxies and can only be
// provided through the identity file credential loader.
// cfg.Credentials must be non-empty. One of cfg.Addrs and cfg.Dialer must be non-empty,
// unless LoadProfile is used to fetch Credentials and load a web proxy dialer.
//
// LoadIdentityFile("identity-file-path")
//
// New will try to open a connection with all combinations of addresses,
// server types, and credentials. The first successful connection to a server
// will be used, or an aggregated error will be returned if all combinations fail.
//
// If cfg.DialInBackground is true, New will only use the first credentials listed.
// A predefined dialer must be provided in cfg, or the first addr must be to an auth server.
// The connection will be dialed in the background, so the connection is not guaranteed
// to be open. This option is primarily meant for internal use where the client has
// direct access to server values that guarantee a successful connection.
// See the example below for usage.
func New(ctx context.Context, cfg Config) (clt *Client, err error) {
if err = cfg.CheckAndSetDefaults(); err != nil {
return nil, trace.Wrap(err)
}
// If cfg.DialInBackground is true, only a single connection is attempted.
// This option is primarily meant for internal use where the client has
// direct access to server values that guarantee a successful connection.
if cfg.DialInBackground {
return connectInBackground(ctx, cfg)
}
@@ -127,7 +125,7 @@ func connectInBackground(ctx context.Context, cfg Config) (*Client, error) {
return nil, trace.BadParameter("must have a Dialer or Addrs in config")
}
if dialer == nil {
dialer = NewDialer(cfg.KeepAlivePeriod, cfg.DialTimeout)
dialer = NewDirectDialer(cfg.KeepAlivePeriod, cfg.DialTimeout)
addr = cfg.Addrs[0]
}
@@ -215,7 +213,7 @@ func connect(ctx context.Context, cfg Config) (*Client, error) {
for _, addr := range cfg.Addrs {
// Connect to auth.
dialer := NewDialer(cfg.KeepAlivePeriod, cfg.DialTimeout)
dialer := NewDirectDialer(cfg.KeepAlivePeriod, cfg.DialTimeout)
clt := newClient(cfg, dialer, tlsConfig)
syncConnect(clt, addr)
@@ -225,10 +223,10 @@ func connect(ctx context.Context, cfg Config) (*Client, error) {
go func(addr string) {
defer wg.Done()
// Try connecting to web proxy to retrieve tunnel address.
if pr, err := Find(ctx, addr, cfg.InsecureAddressDiscovery, nil); err == nil {
if pr, err := webclient.Find(ctx, addr, cfg.InsecureAddressDiscovery, nil); err == nil {
addr = pr.Proxy.SSH.TunnelPublicAddr
}
dialer := NewTunnelDialer(*sshConfig, cfg.KeepAlivePeriod, cfg.DialTimeout)
dialer := newTunnelDialer(*sshConfig, cfg.KeepAlivePeriod, cfg.DialTimeout)
clt := newClient(cfg, dialer, tlsConfig)
syncConnect(clt, addr)
}(addr)
@@ -310,7 +308,7 @@ type Config struct {
// Addrs is a list of teleport auth/proxy server addresses to dial.
Addrs []string
// Credentials are a list of credentials to use when attempting
// to form a connection from client to server.
// to connect to the server.
Credentials []Credentials
// Dialer is a custom dialer used to dial a server. If set, Dialer
// takes precedence over all other connection options.
@@ -605,7 +603,7 @@ func (c *Client) SetAccessRequestState(ctx context.Context, params types.AccessR
Annotations: params.Annotations,
Roles: params.Roles,
}
if d := GetDelegator(ctx); d != "" {
if d := utils.GetDelegator(ctx); d != "" {
setter.Delegator = d
}
_, err := c.grpc.SetAccessRequestState(ctx, &setter, c.callOpts...)
+26 -6
View File
@@ -21,6 +21,7 @@ import (
"net"
"time"
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/utils/sshutils"
@@ -34,7 +35,7 @@ type ContextDialer interface {
DialContext(ctx context.Context, network, addr string) (net.Conn, error)
}
// ContextDialerFunc is a function wrapper that implements the ContextDialer interface
// ContextDialerFunc is a function wrapper that implements the ContextDialer interface.
type ContextDialerFunc func(ctx context.Context, network, addr string) (net.Conn, error)
// DialContext is a function that dials to the specified address
@@ -42,17 +43,36 @@ func (f ContextDialerFunc) DialContext(ctx context.Context, network, addr string
return f(ctx, network, addr)
}
// NewDialer makes a new dialer.
func NewDialer(keepAlivePeriod, dialTimeout time.Duration) ContextDialer {
// NewDirectDialer makes a new dialer to connect directly to an Auth server.
func NewDirectDialer(keepAlivePeriod, dialTimeout time.Duration) ContextDialer {
return &net.Dialer{
Timeout: dialTimeout,
KeepAlive: keepAlivePeriod,
}
}
// NewTunnelDialer make a new ssh tunnel dialer.
func NewTunnelDialer(ssh ssh.ClientConfig, keepAlivePeriod, dialTimeout time.Duration) ContextDialer {
dialer := NewDialer(keepAlivePeriod, dialTimeout)
// NewProxyDialer makes a dialer to connect to an Auth server through the SSH reverse tunnel on the proxy.
// The dialer will ping the web client to discover the tunnel proxy address on each dial.
func NewProxyDialer(ssh ssh.ClientConfig, keepAlivePeriod, dialTimeout time.Duration, discoveryAddr string, insecure bool) ContextDialer {
dialer := newTunnelDialer(ssh, keepAlivePeriod, dialTimeout)
return ContextDialerFunc(func(ctx context.Context, network, _ string) (conn net.Conn, err error) {
// Ping web proxy to retrieve tunnel proxy address.
pr, err := webclient.Find(ctx, discoveryAddr, insecure, nil)
if err != nil {
return nil, trace.Wrap(err)
}
conn, err = dialer.DialContext(ctx, network, pr.Proxy.SSH.TunnelPublicAddr)
if err != nil {
return nil, trace.Wrap(err)
}
return conn, nil
})
}
// newTunnelDialer makes a dialer to connect to an Auth server through the SSH reverse tunnel on the proxy.
func newTunnelDialer(ssh ssh.ClientConfig, keepAlivePeriod, dialTimeout time.Duration) ContextDialer {
dialer := NewDirectDialer(keepAlivePeriod, dialTimeout)
return ContextDialerFunc(func(ctx context.Context, network, addr string) (conn net.Conn, err error) {
conn, err = dialer.DialContext(ctx, network, addr)
if err != nil {
+108 -82
View File
@@ -17,84 +17,98 @@ limitations under the License.
package client
import (
"context"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/identityfile"
"github.com/gravitational/teleport/api/profile"
"github.com/gravitational/trace"
"golang.org/x/crypto/ssh"
"golang.org/x/net/http2"
)
// Credentials are used to authenticate to Auth.
// Credentials are used to authenticate the API auth client. Some Credentials
// also provide other functionality, such as automatic address discovery and
// ssh connectivity.
//
// See the examples below for an example of each loader.
type Credentials interface {
// Dialer is used to create a dialer used to connect to Auth.
// Dialer is used to create a dialer used to connect to the Auth server.
Dialer(cfg Config) (ContextDialer, error)
// TLSConfig returns TLS configuration used to connect to Auth.
// TLSConfig returns TLS configuration used to authenticate the client.
TLSConfig() (*tls.Config, error)
// SSHClientConfig returns SSH configuration used to connect to Proxy through tunnel.
// SSHClientConfig returns SSH configuration used to connect to the
// Auth server through a reverse tunnel.
SSHClientConfig() (*ssh.ClientConfig, error)
}
// LoadTLS is used to load credentials directly from another *tls.Config.
func LoadTLS(tlsConfig *tls.Config) *TLSConfigCreds {
return &TLSConfigCreds{
// LoadTLS is used to load Credentials directly from a *tls.Config.
//
// TLS creds can only be used to connect directly to a Teleport Auth server.
func LoadTLS(tlsConfig *tls.Config) Credentials {
return &tlsConfigCreds{
tlsConfig: tlsConfig,
}
}
// TLSConfigCreds are used to authenticate the client
// with a predefined *tls.Config.
type TLSConfigCreds struct {
// tlsConfigCreds use a defined *tls.Config to provide client credentials.
type tlsConfigCreds struct {
tlsConfig *tls.Config
}
// Dialer is used to dial a connection to Auth.
func (c *TLSConfigCreds) Dialer(cfg Config) (ContextDialer, error) {
// Dialer is used to dial a connection to an Auth server.
func (c *tlsConfigCreds) Dialer(cfg Config) (ContextDialer, error) {
return nil, trace.NotImplemented("no dialer")
}
// TLSConfig returns TLS configuration used to connect to Auth.
func (c *TLSConfigCreds) TLSConfig() (*tls.Config, error) {
// TLSConfig returns TLS configuration.
func (c *tlsConfigCreds) TLSConfig() (*tls.Config, error) {
if c.tlsConfig == nil {
return nil, trace.BadParameter("tls config is nil")
}
return configure(c.tlsConfig), nil
return configureTLS(c.tlsConfig), nil
}
// SSHClientConfig returns SSH configuration used to connect to Proxy.
func (c *TLSConfigCreds) SSHClientConfig() (*ssh.ClientConfig, error) {
// SSHClientConfig returns SSH configuration.
func (c *tlsConfigCreds) SSHClientConfig() (*ssh.ClientConfig, error) {
return nil, trace.NotImplemented("no ssh config")
}
// LoadKeyPair is used to load credentials from files on disk.
func LoadKeyPair(certFile string, keyFile string, caFile string) *KeyPairCreds {
return &KeyPairCreds{
// LoadKeyPair is used to load Credentials from a certicate keypair on disk.
//
// KeyPair Credentials can only be used to connect directly to a Teleport Auth server.
//
// New KeyPair files can be generated with tsh or tctl.
// $ tctl auth sign --format=tls --user=api-user --out=path/to/certs
//
// The certificates' time to live can be specified with --ttl.
//
// See the example below for usage.
func LoadKeyPair(certFile, keyFile, caFile string) Credentials {
return &keypairCreds{
certFile: certFile,
keyFile: keyFile,
caFile: caFile,
}
}
// KeyPairCreds are used to authenticate the client
// with certificates generated in the given file paths.
type KeyPairCreds struct {
// keypairCreds use keypair certificates to provide client credentials.
type keypairCreds struct {
certFile string
keyFile string
caFile string
}
// Dialer is used to dial a connection to Auth.
func (c *KeyPairCreds) Dialer(cfg Config) (ContextDialer, error) {
// Dialer is used to dial a connection to an Auth server.
func (c *keypairCreds) Dialer(cfg Config) (ContextDialer, error) {
return nil, trace.NotImplemented("no dialer")
}
// TLSConfig returns TLS configuration used to connect to Auth.
func (c *KeyPairCreds) TLSConfig() (*tls.Config, error) {
// TLSConfig returns TLS configuration.
func (c *keypairCreds) TLSConfig() (*tls.Config, error) {
cert, err := tls.LoadX509KeyPair(c.certFile, c.keyFile)
if err != nil {
return nil, trace.Wrap(err)
@@ -110,38 +124,48 @@ func (c *KeyPairCreds) TLSConfig() (*tls.Config, error) {
return nil, trace.BadParameter("invalid TLS CA cert PEM")
}
return configure(&tls.Config{
return configureTLS(&tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: pool,
}), nil
}
// SSHClientConfig returns SSH configuration used to connect to Proxy.
func (c *KeyPairCreds) SSHClientConfig() (*ssh.ClientConfig, error) {
// SSHClientConfig returns SSH configuration.
func (c *keypairCreds) SSHClientConfig() (*ssh.ClientConfig, error) {
return nil, trace.NotImplemented("no ssh config")
}
// LoadIdentityFile is used to load credentials from an identity file on disk.
func LoadIdentityFile(path string) *IdentityCreds {
return &IdentityCreds{
// LoadIdentityFile is used to load Credentials from an identity file on disk.
//
// Identity Credentials can be used to connect to an auth server directly
// or through a reverse tunnel.
//
// A new identity file can be generated with tsh or tctl.
// $ tsh login --user=api-user --out=identity-file-path
// $ tctl auth sign --user=api-user --out=identity-file-path
//
// The identity file's time to live can be specified with --ttl.
//
// See the example below for usage.
func LoadIdentityFile(path string) Credentials {
return &identityCreds{
path: path,
}
}
// IdentityCreds are used to authenticate the client
// with an identity file generated in the given file path.
type IdentityCreds struct {
// identityCreds use an identity file to provide client credentials.
type identityCreds struct {
path string
identityFile *IdentityFile
identityFile *identityfile.IdentityFile
}
// Dialer is used to dial a connection to Auth.
func (c *IdentityCreds) Dialer(cfg Config) (ContextDialer, error) {
// Dialer is used to dial a connection to an Auth server.
func (c *identityCreds) Dialer(cfg Config) (ContextDialer, error) {
return nil, trace.NotImplemented("no dialer")
}
// TLSConfig returns TLS configuration used to connect to Auth.
func (c *IdentityCreds) TLSConfig() (*tls.Config, error) {
// TLSConfig returns TLS configuration.
func (c *identityCreds) TLSConfig() (*tls.Config, error) {
if err := c.load(); err != nil {
return nil, trace.Wrap(err)
}
@@ -151,11 +175,11 @@ func (c *IdentityCreds) TLSConfig() (*tls.Config, error) {
return nil, trace.Wrap(err)
}
return configure(tlsConfig), nil
return configureTLS(tlsConfig), nil
}
// SSHClientConfig returns SSH configuration used to connect to Proxy.
func (c *IdentityCreds) SSHClientConfig() (*ssh.ClientConfig, error) {
// SSHClientConfig returns SSH configuration.
func (c *identityCreds) SSHClientConfig() (*ssh.ClientConfig, error) {
if err := c.load(); err != nil {
return nil, trace.Wrap(err)
}
@@ -170,61 +194,63 @@ func (c *IdentityCreds) SSHClientConfig() (*ssh.ClientConfig, error) {
// load is used to lazy load the identity file from persistent storage.
// This allows LoadIdentity to avoid possible errors for UX purposes.
func (c *IdentityCreds) load() error {
func (c *identityCreds) load() error {
if c.identityFile != nil {
return nil
}
var err error
if c.identityFile, err = ReadIdentityFile(c.path); err != nil {
if c.identityFile, err = identityfile.Read(c.path); err != nil {
return trace.BadParameter("identity file could not be decoded: %v", err)
}
return nil
}
// LoadProfile is used to load credentials from a tsh Profile.
// If dir is not specified, the default profile path will be used.
// If name is not specified, the current profile name will be used.
func LoadProfile(dir, name string) *ProfileCreds {
return &ProfileCreds{
// LoadProfile is used to load Credentials from a tsh profile on disk.
//
// dir is the profile directory. It will defaults to "~/.tsh".
//
// name is the profile name. It will default to the currently active tsh profile.
//
// Profile Credentials can be used to connect to an auth server directly
// or through a reverse tunnel.
//
// Profile Credentials will automatically attempt to find your reverse
// tunnel address and make a connection through it.
//
// A new profile can be generated with tsh.
// $ tsh login --user=api-user
func LoadProfile(dir, name string) Credentials {
return &profileCreds{
dir: dir,
name: name,
}
}
// ProfileCreds are used to authenticate the client
// with a tsh profile with the given directory and name.
type ProfileCreds struct {
// profileCreds use a tsh profile to provide client credentials.
type profileCreds struct {
dir string
name string
profile *Profile
profile *profile.Profile
}
// Dialer is used to dial a connection to Auth.
func (c *ProfileCreds) Dialer(cfg Config) (ContextDialer, error) {
// Dialer is used to dial a connection to an Auth server.
func (c *profileCreds) Dialer(cfg Config) (ContextDialer, error) {
sshConfig, err := c.SSHClientConfig()
if err != nil {
return nil, trace.Wrap(err)
}
dialer := NewTunnelDialer(*sshConfig, cfg.KeepAlivePeriod, cfg.DialTimeout)
return ContextDialerFunc(func(ctx context.Context, network, _ string) (conn net.Conn, err error) {
// Ping web proxy to retrieve tunnel proxy address.
pr, err := Find(ctx, c.profile.WebProxyAddr, cfg.InsecureAddressDiscovery, nil)
if err != nil {
return nil, trace.Wrap(err)
}
conn, err = dialer.DialContext(ctx, network, pr.Proxy.SSH.TunnelPublicAddr)
if err != nil {
// not wrapping on purpose to preserve the original error
return nil, err
}
return conn, nil
}), nil
return NewProxyDialer(
*sshConfig,
cfg.KeepAlivePeriod,
cfg.DialTimeout,
c.profile.WebProxyAddr,
cfg.InsecureAddressDiscovery,
), nil
}
// TLSConfig returns TLS configuration used to connect to Auth.
func (c *ProfileCreds) TLSConfig() (*tls.Config, error) {
// TLSConfig returns TLS configuration.
func (c *profileCreds) TLSConfig() (*tls.Config, error) {
if err := c.load(); err != nil {
return nil, trace.Wrap(err)
}
@@ -234,11 +260,11 @@ func (c *ProfileCreds) TLSConfig() (*tls.Config, error) {
return nil, trace.Wrap(err)
}
return configure(tlsConfig), nil
return configureTLS(tlsConfig), nil
}
// SSHClientConfig returns SSH configuration used to connect to Proxy.
func (c *ProfileCreds) SSHClientConfig() (*ssh.ClientConfig, error) {
// SSHClientConfig returns SSH configuration.
func (c *profileCreds) SSHClientConfig() (*ssh.ClientConfig, error) {
if err := c.load(); err != nil {
return nil, trace.Wrap(err)
}
@@ -253,18 +279,18 @@ func (c *ProfileCreds) SSHClientConfig() (*ssh.ClientConfig, error) {
// load is used to lazy load the profile from persistent storage.
// This allows LoadProfile to avoid possible errors for UX purposes.
func (c *ProfileCreds) load() error {
func (c *profileCreds) load() error {
if c.profile != nil {
return nil
}
var err error
if c.profile, err = ProfileFromDir(c.dir, c.name); err != nil {
if c.profile, err = profile.FromDir(c.dir, c.name); err != nil {
return trace.BadParameter("profile could not be decoded: %v", err)
}
return nil
}
func configure(c *tls.Config) *tls.Config {
func configureTLS(c *tls.Config) *tls.Config {
tlsConfig := c.Clone()
tlsConfig.NextProtos = []string{http2.NextProtoTLS}
+16 -14
View File
@@ -26,6 +26,8 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/gravitational/teleport/api/identityfile"
"github.com/gravitational/teleport/api/profile"
"github.com/gravitational/teleport/api/utils/sshutils"
"github.com/stretchr/testify/require"
@@ -61,18 +63,18 @@ func TestLoadIdentityFile(t *testing.T) {
// Write identity file to disk.
path := filepath.Join(t.TempDir(), "file")
idFile := &IdentityFile{
idFile := &identityfile.IdentityFile{
PrivateKey: keyPEM,
Certs: Certs{
Certs: identityfile.Certs{
TLS: tlsCert,
SSH: sshCert,
},
CACerts: CACerts{
CACerts: identityfile.CACerts{
TLS: [][]byte{tlsCACert},
SSH: [][]byte{sshCACert},
},
}
err := WriteIdentityFile(idFile, path)
err := identityfile.Write(idFile, path)
require.NoError(t, err)
// Load identity file from disk.
@@ -134,7 +136,7 @@ func TestLoadProfile(t *testing.T) {
// Write identity file to disk.
dir := t.TempDir()
name := "proxy.example.com"
p := &Profile{
p := &profile.Profile{
WebProxyAddr: "proxy.example.com:3080",
SiteName: "example.com",
Username: "testUser",
@@ -143,14 +145,14 @@ func TestLoadProfile(t *testing.T) {
// Save profile and keys to disk.
require.NoError(t, p.SaveToDir(dir, true))
require.NoError(t, os.MkdirAll(p.keyDir(), 0700))
require.NoError(t, os.MkdirAll(p.userKeyDir(), 0700))
require.NoError(t, os.MkdirAll(p.sshDir(), 0700))
require.NoError(t, ioutil.WriteFile(p.keyPath(), keyPEM, 0600))
require.NoError(t, ioutil.WriteFile(p.tlsCertPath(), tlsCert, 0600))
require.NoError(t, ioutil.WriteFile(p.tlsCasPath(), tlsCACert, 0600))
require.NoError(t, ioutil.WriteFile(p.sshCertPath(), sshCert, 0600))
require.NoError(t, ioutil.WriteFile(p.sshCasPath(), sshCACert, 0600))
require.NoError(t, os.MkdirAll(p.KeyDir(), 0700))
require.NoError(t, os.MkdirAll(p.UserKeyDir(), 0700))
require.NoError(t, os.MkdirAll(p.SSHDir(), 0700))
require.NoError(t, ioutil.WriteFile(p.KeyPath(), keyPEM, 0600))
require.NoError(t, ioutil.WriteFile(p.TLSCertPath(), tlsCert, 0600))
require.NoError(t, ioutil.WriteFile(p.TLSCAsPath(), tlsCACert, 0600))
require.NoError(t, ioutil.WriteFile(p.SSHCertPath(), sshCert, 0600))
require.NoError(t, ioutil.WriteFile(p.SSHCAsPath(), sshCACert, 0600))
// Load profile from disk.
creds := LoadProfile(dir, name)
@@ -183,7 +185,7 @@ func getExpectedTLSConfig(t *testing.T) *tls.Config {
pool := x509.NewCertPool()
require.True(t, pool.AppendCertsFromPEM(tlsCACert))
return configure(&tls.Config{
return configureTLS(&tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: pool,
})
+3
View File
@@ -0,0 +1,3 @@
// Package client provides a gRPC implementation of the Teleport Auth client.
// This client can be used to programatically interact with a Teleport Auth server.
package client
+160
View File
@@ -0,0 +1,160 @@
package client_test
// this package adds godoc examples for several Client types and functions
// See https://pkg.go.dev/github.com/fluhus/godoc-tricks#Examples
import (
"context"
"log"
"time"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/types"
)
// Below is an example of creating a new Teleport Auth client with Profile credentials,
// and using that client to create, get, and delete a Role resource object.
//
// Make sure to look at the Getting Started guide before attempting to run this example.
func ExampleClient_roleCRUD() {
ctx := context.Background()
// Create a new client in your go file.
clt, err := client.New(ctx, client.Config{
Credentials: []client.Credentials{
client.LoadProfile("", ""),
},
// set to true if your Teleport web proxy doesn't have HTTP/TLS certificate
// configured yet (never use this in production).
InsecureAddressDiscovery: false,
})
if err != nil {
log.Fatalf("failed to create client: %v", err)
}
defer clt.Close()
// Resource Spec structs reflect their Resource's yaml definition.
roleSpec := types.RoleSpecV3{
Options: types.RoleOptions{
MaxSessionTTL: types.Duration(time.Hour),
},
Allow: types.RoleConditions{
Logins: []string{"role1"},
Rules: []types.Rule{
types.NewRule(types.KindAccessRequest, []string{types.VerbList, types.VerbRead}),
},
},
Deny: types.RoleConditions{
NodeLabels: types.Labels{"*": []string{"*"}},
},
}
// There are helper functions for creating Teleport resources.
role, err := types.NewRole("role1", roleSpec)
if err != nil {
log.Fatalf("failed to get role: %v", err)
}
// Getters and setters can be used to alter specs.
role.SetLogins(types.Allow, []string{"root"})
// Upsert overwrites the resource if it exists. Use this to create/update resources.
// Equivalent to `tctl create -f role1.yaml`.
err = clt.UpsertRole(ctx, role)
if err != nil {
log.Fatalf("failed to create role: %v", err)
}
// Equivalent to `tctl get role/role1`.
role, err = clt.GetRole(ctx, "role1")
if err != nil {
log.Fatalf("failed to get role: %v", err)
}
// Equivalent to `tctl rm role/role1`.
err = clt.DeleteRole(ctx, "role1")
if err != nil {
log.Fatalf("failed to delete role: %v", err)
}
}
func ExampleNew() {
ctx := context.Background()
clt, err := client.New(ctx, client.Config{
// Multiple Addresses can be provided to attempt to
// connect to the auth server. At least one address
// must be provided, except when using the ProfileCreds.
Addrs: []string{
// The Auth server address can be provided to connect locally.
"auth.example.com:3025",
// The tunnel proxy address can be provided
// to connect to the Auth server over SSH.
"proxy.example.com:3024",
// The web proxy address can be provided to automatically
// find the tunnel proxy address and connect using it.
"proxy.example.com:3080",
},
// Multiple Credentials can be provided to attempt to authenticate
// the client. At least one Credentials object must be provided.
Credentials: []client.Credentials{
client.LoadProfile("", ""),
client.LoadIdentityFile("identity-path"),
client.LoadKeyPair("cert.crt", "cert.key", "cert.cas"),
},
// set to true if your web proxy doesn't have HTTP/TLS certificate
// configured yet (never use this in production).
InsecureAddressDiscovery: false,
})
if err != nil {
log.Fatal(err)
}
defer clt.Close()
clt.Ping(ctx)
}
// Generate tsh profile with tsh.
// $ tsh login --user=api-user
// Load credentials from the default directory and current profile, or specify the directory and profile.
func ExampleCredentials_loadProfile() {
client.LoadProfile("", "")
client.LoadProfile("profile-directory", "api-user")
}
// Load credentials from the default directory and current profile, or specify the directory and profile.
func ExampleLoadProfile() {
client.LoadProfile("", "")
client.LoadProfile("profile-directory", "api-user")
}
// Generate identity file with tsh or tctl.
// $ tsh login --user=api-user --out=identity-file-path
// $ tctl auth sign --user=api-user --out=identity-file-path
// Load credentials from the specified identity file.
func ExampleCredentials_loadIdentity() {
client.LoadIdentityFile("identity-file-path")
}
// Load credentials from the specified identity file.
func ExampleLoadIdentityFile() {
client.LoadIdentityFile("identity-file-path")
}
// Generate certificate key pair with tctl.
// $ tctl auth sign --format=tls --user=api-user --out=path/to/certs
// Load credentials from the specified certificate files.
func ExampleCredentials_loadKeyPair() {
client.LoadKeyPair(
"path/to/certs.crt",
"path/to/certs.key",
"path/to/certs.cas",
)
}
// Load credentials from the specified certificate files.
func ExampleLoadKeyPair() {
client.LoadKeyPair(
"path/to/certs.crt",
"path/to/certs.key",
"path/to/certs.cas",
)
}
-217
View File
@@ -1,217 +0,0 @@
/*
Copyright 2020 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 client
import (
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/trace"
)
// EventToGRPC converts a types.Event to an proto.Event
func EventToGRPC(in types.Event) (*proto.Event, error) {
eventType, err := eventTypeToGRPC(in.Type)
if err != nil {
return nil, trace.Wrap(err)
}
out := proto.Event{
Type: eventType,
}
if in.Type == types.OpInit {
return &out, nil
}
switch r := in.Resource.(type) {
case *types.ResourceHeader:
out.Resource = &proto.Event_ResourceHeader{
ResourceHeader: r,
}
case *types.CertAuthorityV2:
out.Resource = &proto.Event_CertAuthority{
CertAuthority: r,
}
case *types.StaticTokensV2:
out.Resource = &proto.Event_StaticTokens{
StaticTokens: r,
}
case *types.ProvisionTokenV2:
out.Resource = &proto.Event_ProvisionToken{
ProvisionToken: r,
}
case *types.ClusterNameV2:
out.Resource = &proto.Event_ClusterName{
ClusterName: r,
}
case *types.ClusterConfigV3:
out.Resource = &proto.Event_ClusterConfig{
ClusterConfig: r,
}
case *types.UserV2:
out.Resource = &proto.Event_User{
User: r,
}
case *types.RoleV3:
out.Resource = &proto.Event_Role{
Role: r,
}
case *types.Namespace:
out.Resource = &proto.Event_Namespace{
Namespace: r,
}
case *types.ServerV2:
out.Resource = &proto.Event_Server{
Server: r,
}
case *types.ReverseTunnelV2:
out.Resource = &proto.Event_ReverseTunnel{
ReverseTunnel: r,
}
case *types.TunnelConnectionV2:
out.Resource = &proto.Event_TunnelConnection{
TunnelConnection: r,
}
case *types.AccessRequestV3:
out.Resource = &proto.Event_AccessRequest{
AccessRequest: r,
}
case *types.WebSessionV2:
switch r.GetSubKind() {
case types.KindAppSession:
out.Resource = &proto.Event_AppSession{
AppSession: r,
}
case types.KindWebSession:
out.Resource = &proto.Event_WebSession{
WebSession: r,
}
default:
return nil, trace.BadParameter("only %q supported", types.WebSessionSubKinds)
}
case *types.WebTokenV3:
out.Resource = &proto.Event_WebToken{
WebToken: r,
}
case *types.RemoteClusterV3:
out.Resource = &proto.Event_RemoteCluster{
RemoteCluster: r,
}
case *types.DatabaseServerV3:
out.Resource = &proto.Event_DatabaseServer{
DatabaseServer: r,
}
default:
return nil, trace.BadParameter("resource type %T is not supported", in.Resource)
}
return &out, nil
}
func eventTypeToGRPC(in types.OpType) (proto.Operation, error) {
switch in {
case types.OpInit:
return proto.Operation_INIT, nil
case types.OpPut:
return proto.Operation_PUT, nil
case types.OpDelete:
return proto.Operation_DELETE, nil
default:
return -1, trace.BadParameter("event type %v is not supported", in)
}
}
// EventFromGRPC converts an proto.Event to a types.Event
func EventFromGRPC(in proto.Event) (*types.Event, error) {
eventType, err := eventTypeFromGRPC(in.Type)
if err != nil {
return nil, trace.Wrap(err)
}
out := types.Event{
Type: eventType,
}
if eventType == types.OpInit {
return &out, nil
}
if r := in.GetResourceHeader(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetCertAuthority(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetStaticTokens(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetProvisionToken(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetClusterName(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetClusterConfig(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetUser(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetRole(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetNamespace(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetServer(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetReverseTunnel(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetTunnelConnection(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetAccessRequest(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetAppSession(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetWebSession(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetWebToken(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetRemoteCluster(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetDatabaseServer(); r != nil {
out.Resource = r
return &out, nil
} else {
return nil, trace.BadParameter("received unsupported resource %T", in.Resource)
}
}
func eventTypeFromGRPC(in proto.Operation) (types.OpType, error) {
switch in {
case proto.Operation_INIT:
return types.OpInit, nil
case proto.Operation_PUT:
return types.OpPut, nil
case proto.Operation_DELETE:
return types.OpDelete, nil
default:
return types.OpInvalid, trace.BadParameter("unsupported operation type: %v", in)
}
}
+85 -1
View File
@@ -87,7 +87,7 @@ func (w *streamWatcher) receiveEvents() {
w.closeWithError(trail.FromGRPC(err))
return
}
out, err := EventFromGRPC(*event)
out, err := eventFromGRPC(*event)
if err != nil {
w.closeWithError(trail.FromGRPC(err))
return
@@ -100,6 +100,90 @@ func (w *streamWatcher) receiveEvents() {
}
}
// eventFromGRPC converts an proto.Event to a types.Event
func eventFromGRPC(in proto.Event) (*types.Event, error) {
eventType, err := eventTypeFromGRPC(in.Type)
if err != nil {
return nil, trace.Wrap(err)
}
out := types.Event{
Type: eventType,
}
if eventType == types.OpInit {
return &out, nil
}
if r := in.GetResourceHeader(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetCertAuthority(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetStaticTokens(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetProvisionToken(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetClusterName(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetClusterConfig(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetUser(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetRole(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetNamespace(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetServer(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetReverseTunnel(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetTunnelConnection(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetAccessRequest(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetAppSession(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetWebSession(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetWebToken(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetRemoteCluster(); r != nil {
out.Resource = r
return &out, nil
} else if r := in.GetDatabaseServer(); r != nil {
out.Resource = r
return &out, nil
} else {
return nil, trace.BadParameter("received unsupported resource %T", in.Resource)
}
}
func eventTypeFromGRPC(in proto.Operation) (types.OpType, error) {
switch in {
case proto.Operation_INIT:
return types.OpInit, nil
case proto.Operation_PUT:
return types.OpPut, nil
case proto.Operation_DELETE:
return types.OpDelete, nil
default:
return types.OpInvalid, trace.BadParameter("unsupported operation type: %v", in)
}
}
// Done returns a channel that closes once the streamWatcher is Closed
func (w *streamWatcher) Done() <-chan struct{} {
return w.ctx.Done()
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package client
// Package webclient provides a client for the Teleport Proxy API endpoints.
package webclient
import (
"context"
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package client
// Package identityfile implements parsing and serialization of Teleport identity files.
package identityfile
import (
"bufio"
@@ -27,15 +28,15 @@ import (
"os"
"github.com/gravitational/teleport/api/constants"
sshutils "github.com/gravitational/teleport/api/utils/sshutils"
"github.com/gravitational/teleport/api/utils/sshutils"
"github.com/gravitational/trace"
"golang.org/x/crypto/ssh"
)
const (
// IdentityFilePermissions defines file permissions for identity files.
IdentityFilePermissions = 0600
// FilePermissions defines file permissions for identity files.
FilePermissions = 0600
)
// IdentityFile represents the basic components of an identity file.
@@ -94,20 +95,20 @@ func (i *IdentityFile) SSHClientConfig() (*ssh.ClientConfig, error) {
return ssh, nil
}
// WriteIdentityFile writes the given identityFile to the specified path.
func WriteIdentityFile(idFile *IdentityFile, path string) error {
// Write writes the given identityFile to the specified path.
func Write(idFile *IdentityFile, path string) error {
buf := new(bytes.Buffer)
if err := encodeIdentityFile(buf, idFile); err != nil {
return trace.Wrap(err)
}
if err := ioutil.WriteFile(path, buf.Bytes(), IdentityFilePermissions); err != nil {
if err := ioutil.WriteFile(path, buf.Bytes(), FilePermissions); err != nil {
return trace.ConvertSystemError(err)
}
return nil
}
// ReadIdentityFile reads an identity file from the given path.
func ReadIdentityFile(path string) (*IdentityFile, error) {
// Read reads an identity file from the given path.
func Read(path string) (*IdentityFile, error) {
r, err := os.Open(path)
if err != nil {
return nil, trace.Wrap(err)
@@ -15,12 +15,13 @@ limitations under the License.
*/
package client
package identityfile_test
import (
"path/filepath"
"testing"
"github.com/gravitational/teleport/api/identityfile"
"github.com/stretchr/testify/require"
)
@@ -29,24 +30,24 @@ import (
func TestIdentityFileBasics(t *testing.T) {
t.Parallel()
path := filepath.Join(t.TempDir(), "file")
writeIDFile := &IdentityFile{
writeIDFile := &identityfile.IdentityFile{
PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----\nkey\n-----END RSA PRIVATE KEY-----\n"),
Certs: Certs{
Certs: identityfile.Certs{
SSH: []byte("ssh ssh-cert"),
TLS: []byte("-----BEGIN CERTIFICATE-----\ntls-cert\n-----END CERTIFICATE-----\n"),
},
CACerts: CACerts{
CACerts: identityfile.CACerts{
SSH: [][]byte{[]byte("@cert-authority ssh-cacerts")},
TLS: [][]byte{[]byte("-----BEGIN CERTIFICATE-----\ntls-cacerts\n-----END CERTIFICATE-----\n")},
},
}
// Write identity file
err := WriteIdentityFile(writeIDFile, path)
err := identityfile.Write(writeIDFile, path)
require.NoError(t, err)
// Read identity file
readIDFile, err := ReadIdentityFile(path)
readIDFile, err := identityfile.Read(path)
require.NoError(t, err)
// Check that read and write values are equal
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package client
// Package profile handles management of the Teleport profile directory (~/.tsh).
package profile
import (
"crypto/tls"
@@ -93,12 +94,12 @@ func (p *Profile) Name() string {
// TLSConfig returns the profile's associated TLSConfig.
func (p *Profile) TLSConfig() (*tls.Config, error) {
cert, err := tls.LoadX509KeyPair(p.tlsCertPath(), p.keyPath())
cert, err := tls.LoadX509KeyPair(p.TLSCertPath(), p.KeyPath())
if err != nil {
return nil, trace.Wrap(err)
}
caCerts, err := ioutil.ReadFile(p.tlsCasPath())
caCerts, err := ioutil.ReadFile(p.TLSCAsPath())
if err != nil {
return nil, trace.Wrap(err)
}
@@ -116,17 +117,17 @@ func (p *Profile) TLSConfig() (*tls.Config, error) {
// SSHClientConfig returns the profile's associated SSHClientConfig.
func (p *Profile) SSHClientConfig() (*ssh.ClientConfig, error) {
cert, err := ioutil.ReadFile(p.sshCertPath())
cert, err := ioutil.ReadFile(p.SSHCertPath())
if err != nil {
return nil, trace.Wrap(err)
}
key, err := ioutil.ReadFile(p.keyPath())
key, err := ioutil.ReadFile(p.KeyPath())
if err != nil {
return nil, trace.Wrap(err)
}
caCerts, err := ioutil.ReadFile(p.sshCasPath())
caCerts, err := ioutil.ReadFile(p.SSHCAsPath())
if err != nil {
return nil, trace.Wrap(err)
}
@@ -216,11 +217,10 @@ func defaultProfilePath() string {
return filepath.Join(home, profileDir)
}
// ProfileFromDir reads the user (yaml) profile from a given directory. If
// dir is empty, this function defaults to the default tsh profile directory.
// If name is empty, this function defaults to loading the currently active
// profile (if any).
func ProfileFromDir(dir string, name string) (*Profile, error) {
// FromDir reads the user profile from a given directory. If dir is empty,
// this function defaults to the default tsh profile directory. If name is empty,
// this function defaults to loading the currently active profile (if any).
func FromDir(dir string, name string) (*Profile, error) {
dir = FullProfilePath(dir)
var err error
if name == "" {
@@ -284,34 +284,42 @@ func (p *Profile) saveToFile(filepath string) error {
return nil
}
func (p *Profile) keyDir() string {
// KeyDir returns the path to the profile's directory.
func (p *Profile) KeyDir() string {
return filepath.Join(p.Dir, constants.SessionKeyDir)
}
func (p *Profile) userKeyDir() string {
return filepath.Join(p.keyDir(), p.Name())
// UserKeyDir returns the path to the profile's key directory.
func (p *Profile) UserKeyDir() string {
return filepath.Join(p.KeyDir(), p.Name())
}
func (p *Profile) keyPath() string {
return filepath.Join(p.userKeyDir(), p.Username)
// KeyPath returns the path to the profile's private key.
func (p *Profile) KeyPath() string {
return filepath.Join(p.UserKeyDir(), p.Username)
}
func (p *Profile) tlsCertPath() string {
return filepath.Join(p.userKeyDir(), p.Username+constants.FileExtTLSCert)
// TLSCertPath returns the path to the profile's TLS certificate.
func (p *Profile) TLSCertPath() string {
return filepath.Join(p.UserKeyDir(), p.Username+constants.FileExtTLSCert)
}
func (p *Profile) tlsCasPath() string {
return filepath.Join(p.userKeyDir(), constants.FileNameTLSCerts)
// TLSCAsPath returns the path to the profile's TLS certificate authorities.
func (p *Profile) TLSCAsPath() string {
return filepath.Join(p.UserKeyDir(), constants.FileNameTLSCerts)
}
func (p *Profile) sshDir() string {
return filepath.Join(p.userKeyDir(), p.Username+constants.SSHDirSuffix)
// SSHDir returns the path to the profile's ssh directory.
func (p *Profile) SSHDir() string {
return filepath.Join(p.UserKeyDir(), p.Username+constants.SSHDirSuffix)
}
func (p *Profile) sshCertPath() string {
return filepath.Join(p.sshDir(), p.SiteName+constants.FileExtSSHCert)
// SSHCertPath returns the path to the profile's ssh certificate.
func (p *Profile) SSHCertPath() string {
return filepath.Join(p.SSHDir(), p.SiteName+constants.FileExtSSHCert)
}
func (p *Profile) sshCasPath() string {
// SSHCAsPath returns the path to the profile's ssh certificate authorities.
func (p *Profile) SSHCAsPath() string {
return filepath.Join(p.Dir, constants.FileNameKnownHosts)
}
@@ -15,7 +15,7 @@ limitations under the License.
*/
package client
package profile_test
import (
"io/ioutil"
@@ -23,6 +23,7 @@ import (
"path/filepath"
"testing"
"github.com/gravitational/teleport/api/profile"
"github.com/gravitational/trace"
"github.com/stretchr/testify/require"
@@ -37,7 +38,7 @@ func TestProfileBasics(t *testing.T) {
require.NoError(t, err)
defer os.RemoveAll(dir)
p := &Profile{
p := &profile.Profile{
WebProxyAddr: "proxy:3088",
SSHProxyAddr: "proxy:3023",
Username: "testuser",
@@ -63,7 +64,7 @@ func TestProfileBasics(t *testing.T) {
require.Error(t, err)
// make sure current profile was not set
_, err = GetCurrentProfileName(dir)
_, err = profile.GetCurrentProfileName(dir)
require.True(t, trace.IsNotFound(err))
// save again, this time also making current
@@ -71,17 +72,17 @@ func TestProfileBasics(t *testing.T) {
require.NoError(t, err)
// verify that current profile is set and matches this profile
name, err := GetCurrentProfileName(dir)
name, err := profile.GetCurrentProfileName(dir)
require.NoError(t, err)
require.Equal(t, p.Name(), name)
// load and verify current profile
clone, err := ProfileFromDir(dir, "")
clone, err := profile.FromDir(dir, "")
require.NoError(t, err)
require.Equal(t, *p, *clone)
// load and verify directly
clone, err = ProfileFromDir(dir, p.Name())
clone, err = profile.FromDir(dir, p.Name())
require.NoError(t, err)
require.Equal(t, *p, *clone)
}
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package client
package utils
import "context"
+10 -2
View File
@@ -171,8 +171,16 @@
"entries": [
{ "title": "YAML", "slug": "/config-reference/" },
{ "title": "CLI", "slug": "/cli-docs/" },
{ "title": "API", "slug": "/api-reference/" },
{ "title": "Metrics", "slug": "/metrics-logs-reference/" }
{ "title": "Metrics", "slug": "/metrics-logs-reference/" },
{
"title": "API",
"slug": "/reference/api/",
"entries": [
{"title": "Introduction", "slug": "/reference/api/introduction/"},
{"title": "Getting Started", "slug": "/reference/api/getting-started/"},
{"title": "Architecture", "slug": "/reference/api/architecture/"}
]
}
]
}
],
File diff suppressed because it is too large Load Diff
@@ -7,7 +7,7 @@ description: How to access RESTful APIs using Teleport Application Access
Application Access can be used to access applications' RESTful APIs with
tools like [curl](https://man7.org/linux/man-pages/man1/curl.1.html) or
[Postman](https://www.postman.com/).
Postman.
## Prerequisites
+1 -1
View File
@@ -62,7 +62,7 @@ $ kubectl --kubeconfig /path/to/kubeconfig get pods
>
Short lived certificates expire in hours or minutes. You don't have to revoke
them if the host gets compromised.
Generate a new kubeconfig every hour using `tctl` or [API](../../api-reference.mdx)
Generate a new kubeconfig every hour using `tctl` or [API](../../reference/api/introduction.mdx)
and publish it to secret storage, like [AWS](https://aws.amazon.com/secrets-manager/) or
[GCP](https://cloud.google.com/secret-manager) secret managers.
</Admonition>
+15
View File
@@ -0,0 +1,15 @@
---
title: Teleport API Reference
description: Reference for the Teleport API
---
# Reference
- [Introduction](./api/introduction.mdx)
- [Getting Started](./api/getting-started.mdx)
- [Architecture](./api/architecture.mdx)
- [pkg.go.dev](https://pkg.go.dev/github.com/gravitational/teleport/api/client)
- [Using the client](https://pkg.go.dev/github.com/gravitational/teleport/api/client#Client)
- [Working with credentials](https://pkg.go.dev/github.com/gravitational/teleport/api/client#Credentials)
+96
View File
@@ -0,0 +1,96 @@
---
title: API Architecture
description: Architecture of the Teleport API
---
## Authentication
The Auth API uses mTLS to authenticate a client-server connection. Therefore, the client must provide
TLS certificates signed by the Auth server to access the API. These are easy to create and provide using
[Credential loaders](#credentials).
## Authorization
The client certificates signed by the Auth server will be associated with a specific user. This user will
be used to authorize API requests made by the client.
It is recommended to make a new user and role for each client. This makes it easier to track client actions,
as well as carefully control client permissions.
For example, if your client needs to use `client.GetRole()`, the user must have permission to perform the `read`
action on the `role` resource. You should create a user and role with the minimum permissions required.
```bash
# Copy and Paste the below and run on the Teleport Auth server.
cat > api-role.yaml <<EOF
kind: role
metadata:
name: api-role
spec:
allow:
rules:
- resources: ['role']
verbs: ['read']
deny:
node_labels:
'*': '*'
version: v3
EOF
# Create role
tctl create -f api-role.yaml
# Add user and login via web proxy
tctl users add api-user --roles=api-role
```
See our [roles](../../access-controls/reference.mdx#roles) documentation for more details
on role based access control.
## Credentials
The Teleport Go client uses Credentials in order to gather and hold TLS certificates, connect
to proxy servers over SSH, and perform some other actions.
Credentials are created by using Credential loaders, which gather certificates and data
generated by [Teleport CLIs](../../cli-docs.mdx).
Since there are several Credential loaders to choose from with distinct benefits, here's a quick breakdown:
- Profile Credentials are the easiest to get started with. All you have to do is login
on your device with `tsh login`. Your Teleport proxy address and credentials will
automatically be located and used.
- IdentityFile Credentials are the most well rounded in terms of usability, functionality,
and customizability. Identity files can be generated through `tsh login` or `tctl auth sign`,
making them ideal for both long lived proxy and auth server connections.
- Key Pair Credentials have a much simpler implementation than the first two Credentials listed,
and may feel more familiar. These are good for authenticating client's hosted directly on the auth server.
- TLS Credentials leave everything up to the client user. This is mostly used internally, but
some advanced users may find it useful.
Here are some more specific details to differentiate them by:
| Type | Profile Credentials | Identity Credentials | Key Pair Credentials | TLS Credentials |
| - | - | - | - | - |
| Ease of use | easy | easy | med | hard |
| Supports long lived certificates | yes, but must be configured on server side | yes | yes | yes |
| Supports SSH connections | yes | yes (6.1+) | no | no |
| Automatic Proxy Address discovery | yes | no | no | no |
| CLI used | tsh | tctl/tsh | tctl | - |
| Available in | 6.1+ | 6.0+ | 6.0+ | 6.0+ |
See the [Credentials type](https://pkg.go.dev/github.com/gravitational/teleport/api/client#Credentials)
on pkg.go.dev for more information and examples for Credentials and Credential Loaders.
## Client Connection
The API client makes requests through an open connection to the Teleport Auth server.
If the Auth server is isolated behind a [Proxy Server](../../architecture/proxy.mdx), a reverse
tunnel connection can be made using SSH certificates signed by the auth server. You can either
provide the server's reverse tunnel address directly, or provide the web proxy address and have
the client automatically retrieve the reverse tunnel address.
<Admonition type="note">
While all Credential loaders support mTLS connections, only some support SSH connections (see the chart above).
</Admonition>
Take a look at this client constructor [example](https://pkg.go.dev/github.com/gravitational/teleport/api/client#example-New)
to see what these connection options look like.
@@ -0,0 +1,99 @@
---
title: API Getting Started Guide
description: Getting started with the Teleport API
---
# Getting Started
In this getting started guide we will use the Teleport API Go Client to connect to the Teleport Auth server.
Here are the steps we'll walk through:
1. Create an API user
2. Generate credentials
3. Create a Go program to demo the client
## Prerequisites
- Install [Go](https://golang.org/doc/install) (=teleport.golang=)+ and Setup Go Dev Environment
- Set up Teleport with the [Getting Started Guide](../../getting-started.mdx)
## 1/3 Create a User
Create a new user for the client to impersonate.
```bash
# Run this directly on your auth server
# Add user and login via web proxy
tctl users add api-user --roles=admin
```
<Admonition type="note">
It is generally best practice to create custom roles for each client user. See [API authorization](./architecture.mdx#authorization).
</Admonition>
## 2/3 Generate Client Credentials
Login as the newly created user with `tsh`.
```bash
# generate tsh profile
tsh login --user=api-user
```
The [profile Credentials loader](https://pkg.go.dev/github.com/gravitational/teleport/api/client#LoadProfile)
will automatically retrieve Credentials from the current profile in the next step.
## Step 3/3 Create a go project
Set up a new [Go module](https://golang.org/doc/tutorial/create-module) and import the `client` package:
```bash
mkdir client-demo && cd client-demo
go mod init client-demo
go get github.com/gravitational/teleport/api/client
```
Add the following code to a new `main.go` file.
```go
package main
import (
"context"
"log"
"github.com/gravitational/teleport/api/client"
)
func main() {
ctx := context.Background()
clt, err := client.New(ctx, client.Config{
Credentials: []client.Credentials{
client.LoadProfile("", ""),
},
})
if err != nil {
log.Fatalf("failed to create client: %v", err)
}
defer clt.Close()
resp, err := clt.Ping(ctx)
if err != nil {
log.Fatalf("failed to ping server: %v", err)
}
log.Printf("server version: %s", resp.ServerVersion)
}
```
Now you can run the program which will connect the client to the Teleport Auth server and fetch the server version.
```bash
go run main.go
```
## Next Steps
- Read about the [API architecture](./architecture.mdx) for a more in depth look at the API client.
- Visit the `client` package's [pkg.go.dev](https://pkg.go.dev/github.com/gravitational/teleport/api/client) for easy to navigate code oriented documentation.
- Familiarize yourself with the [admin manual](../../admin-guide.mdx) to make the best use of the API.
+35
View File
@@ -0,0 +1,35 @@
---
title: Teleport API Introduction
description: Introduction for the Teleport API
---
The Teleport Auth API provides a gRPC API for remotely interacting with a Teleport Auth server.
Teleport has a public [Go client](https://pkg.go.dev/github.com/gravitational/teleport/api/client)
to programatically interact with the API. [tsh and tctl](../../cli-docs.mdx/) use the same API.
## Go Client
Here is what you can do with the Go Client:
- Integrating with external tools, which we have already done
for [several tools](../../enterprise/workflow/index.mdx#integrating-with-an-external-tool),
such as Slack, Jira, and Mattermost.
- Writing a program/bot to manage access requests automatically, based on your use case. One idea
is to allow/deny developer requests based on their currently assigned tasks.
- Performing CRUD actions on resources, such as `roles`, `auth connectors`, and `provisioning tokens`.
- Dynamically configuring Teleport.
<Admonition type="warning">
Not all endpoints are supported by the new public API client yet. See this
[github issue](https://github.com/gravitational/teleport/issues/6394).
</Admonition>
<Admonition type="note">
We are currently working on improving our API and its documentation. If you have an API suggestion,
[please complete our survey](https://docs.google.com/forms/d/1HPQu5Asg3lR0cu5crnLDhlvovGpFVIIbDMRvqclPhQg/viewform).
</Admonition>
## Get Started
Create an API client in 3 minutes with the [Getting Started](./getting-started.mdx) Guide.
-1
View File
@@ -1 +0,0 @@
certs
+13 -21
View File
@@ -1,27 +1,10 @@
## Teleport Auth Go Client
### Introduction
This program demonstrates how to...
1. Authenticate the client using credential loaders.
2. Authorize API calls using an independent user and role.
3. Create a new client and make API calls.
### Authentication
Auth API clients must perform two-way authentication using x509 certificates to:
1. Validate the auth server x509 certificate to make sure the API endpoint can be trusted.
2. Offer their x509 certificate, which has been previously issued by the auth sever.
Credential loaders from the `github.com/gravitataional/teleport/api/client` package can be used to find and load certificates generated by `tctl` and `tsh` commands.
### Authorization
The server will authorize requests for the user associated with the certificates used to authenticate the client. Therefore, to use the API client, you need to create a user and any roles it may need for your use case.
The client will act on behalf of that user and have access as defined by the user's role(s). It is recommended to create an independent user and role to manage API access control.
3. Create a new client and make API calls to the Auth server.
### Demo
@@ -38,14 +21,14 @@ $ tctl users add access-admin --roles=access-admin
##### Generate Credentials
This demo uses the Profile credential loader. Login with `tsh` and the Client will use the credentials from the profile directory (`~/.tsh`).
Login with `tsh` to generate Profile credentials.
```bash
# login and automatically generate keys
$ tsh login --user=access-admin
```
NOTE: You can pass the `InsecureAddressDiscovery` in `client.Config` field to skip verification of the TLS certificate of the proxy. This is not recommended for production clients.
NOTE: You can pass the `InsecureAddressDiscovery` in `client.Config` field to skip verification of the TLS certificate of the proxy. Don't do this for production clients.
##### Run
@@ -53,4 +36,13 @@ NOTE: You can pass the `InsecureAddressDiscovery` in `client.Config` field to sk
$ go run main.go
```
To see more information on the Go Client and how to use it, visit our [API Documentation](https://goteleport.com/teleport/docs/api-reference/).
### Reference
To see more information on the Go Client and how to use it, visit our API documentation:
- [Introduction](https:/goteleport.com/docs/reference/api/introduction)
- [Getting Started](https:/goteleport.com/docs/reference/api/getting-started)
- [Architecture](https:/goteleport.com/docs/reference/api/architecture)
- [pkg.go.dev](https://pkg.go.dev/github.com/gravitational/teleport/api/client)
- [Client type](https://pkg.go.dev/github.com/gravitational/teleport/api/client#Client)
- [Credentials type](https://pkg.go.dev/github.com/gravitational/teleport/api/client#Credentials)
+2 -2
View File
@@ -44,7 +44,7 @@ import (
"golang.org/x/crypto/ssh"
"github.com/gravitational/teleport"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/profile"
"github.com/gravitational/teleport/lib"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/auth/testauthority"
@@ -141,7 +141,7 @@ func (s *IntSuite) TearDownSuite(c *check.C) {
}
func (s *IntSuite) SetUpTest(c *check.C) {
os.RemoveAll(apiclient.FullProfilePath(""))
os.RemoveAll(profile.FullProfilePath(""))
}
// setUpTest configures the specific test identified with the given c.
+2 -2
View File
@@ -38,11 +38,11 @@ import (
"time"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/wrappers"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/lib/auth/u2f"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/defaults"
@@ -1853,7 +1853,7 @@ func (a *Server) SetAccessRequestState(ctx context.Context, params services.Acce
Roles: params.Roles,
}
if delegator := client.GetDelegator(ctx); delegator != "" {
if delegator := apiutils.GetDelegator(ctx); delegator != "" {
event.Delegator = delegator
}
+1 -1
View File
@@ -132,7 +132,7 @@ func NewHTTPClient(cfg client.Config, tls *tls.Config, params ...roundtrip.Clien
if len(cfg.Addrs) == 0 {
return nil, trace.BadParameter("no addresses to dial")
}
contextDialer := client.NewDialer(cfg.KeepAlivePeriod, cfg.DialTimeout)
contextDialer := client.NewDirectDialer(cfg.KeepAlivePeriod, cfg.DialTimeout)
dialer = ContextDialerFunc(func(ctx context.Context, network, _ string) (conn net.Conn, err error) {
for _, addr := range cfg.Addrs {
conn, err = contextDialer.DialContext(ctx, network, addr)
+110 -2
View File
@@ -24,7 +24,6 @@ import (
"time"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
@@ -282,7 +281,7 @@ func (g *GRPCServer) WatchEvents(watch *proto.Watch, stream proto.AuthService_Wa
case <-watcher.Done():
return trail.ToGRPC(watcher.Error())
case event := <-watcher.Events():
out, err := client.EventToGRPC(event)
out, err := eventToGRPC(event)
if err != nil {
return trail.ToGRPC(err)
}
@@ -293,6 +292,115 @@ func (g *GRPCServer) WatchEvents(watch *proto.Watch, stream proto.AuthService_Wa
}
}
// eventToGRPC converts a types.Event to an proto.Event
func eventToGRPC(in types.Event) (*proto.Event, error) {
eventType, err := eventTypeToGRPC(in.Type)
if err != nil {
return nil, trace.Wrap(err)
}
out := proto.Event{
Type: eventType,
}
if in.Type == types.OpInit {
return &out, nil
}
switch r := in.Resource.(type) {
case *types.ResourceHeader:
out.Resource = &proto.Event_ResourceHeader{
ResourceHeader: r,
}
case *types.CertAuthorityV2:
out.Resource = &proto.Event_CertAuthority{
CertAuthority: r,
}
case *types.StaticTokensV2:
out.Resource = &proto.Event_StaticTokens{
StaticTokens: r,
}
case *types.ProvisionTokenV2:
out.Resource = &proto.Event_ProvisionToken{
ProvisionToken: r,
}
case *types.ClusterNameV2:
out.Resource = &proto.Event_ClusterName{
ClusterName: r,
}
case *types.ClusterConfigV3:
out.Resource = &proto.Event_ClusterConfig{
ClusterConfig: r,
}
case *types.UserV2:
out.Resource = &proto.Event_User{
User: r,
}
case *types.RoleV3:
out.Resource = &proto.Event_Role{
Role: r,
}
case *types.Namespace:
out.Resource = &proto.Event_Namespace{
Namespace: r,
}
case *types.ServerV2:
out.Resource = &proto.Event_Server{
Server: r,
}
case *types.ReverseTunnelV2:
out.Resource = &proto.Event_ReverseTunnel{
ReverseTunnel: r,
}
case *types.TunnelConnectionV2:
out.Resource = &proto.Event_TunnelConnection{
TunnelConnection: r,
}
case *types.AccessRequestV3:
out.Resource = &proto.Event_AccessRequest{
AccessRequest: r,
}
case *types.WebSessionV2:
switch r.GetSubKind() {
case types.KindAppSession:
out.Resource = &proto.Event_AppSession{
AppSession: r,
}
case types.KindWebSession:
out.Resource = &proto.Event_WebSession{
WebSession: r,
}
default:
return nil, trace.BadParameter("only %q supported", types.WebSessionSubKinds)
}
case *types.WebTokenV3:
out.Resource = &proto.Event_WebToken{
WebToken: r,
}
case *types.RemoteClusterV3:
out.Resource = &proto.Event_RemoteCluster{
RemoteCluster: r,
}
case *types.DatabaseServerV3:
out.Resource = &proto.Event_DatabaseServer{
DatabaseServer: r,
}
default:
return nil, trace.BadParameter("resource type %T is not supported", in.Resource)
}
return &out, nil
}
func eventTypeToGRPC(in types.OpType) (proto.Operation, error) {
switch in {
case types.OpInit:
return proto.Operation_INIT, nil
case types.OpPut:
return proto.Operation_PUT, nil
case types.OpDelete:
return proto.Operation_DELETE, nil
default:
return -1, trace.BadParameter("event type %v is not supported", in)
}
}
// UpsertNode upserts node
func (g *GRPCServer) UpsertNode(ctx context.Context, server *services.ServerV2) (*services.KeepAlive, error) {
auth, err := g.authenticate(ctx)
+2 -2
View File
@@ -23,9 +23,9 @@ import (
"time"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/wrappers"
"github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/tlsca"
@@ -620,7 +620,7 @@ const (
)
// WithDelegator alias for backwards compatibility
var WithDelegator = client.WithDelegator
var WithDelegator = utils.WithDelegator
// ClientUsername returns the username of a remote HTTP client making the call.
// If ctx didn't pass through auth middleware or did not come from an HTTP
+2 -2
View File
@@ -29,7 +29,7 @@ import (
"syscall"
"time"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/profile"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/utils"
@@ -272,7 +272,7 @@ func execute(m benchMeasure) error {
// makeTeleportClient creates an instance of a teleport client
func makeTeleportClient(host, login, proxy string) (*client.TeleportClient, error) {
c := client.Config{Host: host}
path := apiclient.FullProfilePath("")
path := profile.FullProfilePath("")
if login != "" {
c.HostLogin = login
c.Username = login
+18 -17
View File
@@ -46,9 +46,10 @@ import (
"golang.org/x/term"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/profile"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/wrappers"
"github.com/gravitational/teleport/lib/auth"
@@ -484,7 +485,7 @@ func readProfile(profileDir string, profileName string) (*ProfileStatus, error)
}
// Read in the profile for this proxy.
profile, err := client.ProfileFromDir(profileDir, profileName)
profile, err := profile.FromDir(profileDir, profileName)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -650,7 +651,7 @@ func StatusFor(profileDir, proxyHost, username string) (*ProfileStatus, error) {
// If no profile is active, Status returns a nil error and nil profile.
func Status(profileDir, proxyHost string) (*ProfileStatus, []*ProfileStatus, error) {
var err error
var profile *ProfileStatus
var profileStatus *ProfileStatus
var others []*ProfileStatus
// remove ports from proxy host, because profile name is stored
@@ -663,7 +664,7 @@ func Status(profileDir, proxyHost string) (*ProfileStatus, []*ProfileStatus, err
}
// Construct the full path to the profile requested and make sure it exists.
profileDir = client.FullProfilePath(profileDir)
profileDir = profile.FullProfilePath(profileDir)
stat, err := os.Stat(profileDir)
if err != nil {
log.Debugf("Failed to stat file: %v.", err)
@@ -683,7 +684,7 @@ func Status(profileDir, proxyHost string) (*ProfileStatus, []*ProfileStatus, err
// no proxyHost was supplied.
profileName := proxyHost
if profileName == "" {
profileName, err = client.GetCurrentProfileName(profileDir)
profileName, err = profile.GetCurrentProfileName(profileDir)
if err != nil {
if trace.IsNotFound(err) {
return nil, nil, trace.NotFound("not logged in")
@@ -695,7 +696,7 @@ func Status(profileDir, proxyHost string) (*ProfileStatus, []*ProfileStatus, err
// Read in the target profile first. If readProfile returns trace.NotFound,
// that means the profile may have been corrupted (for example keys were
// deleted but profile exists), treat this as the user not being logged in.
profile, err = readProfile(profileDir, profileName)
profileStatus, err = readProfile(profileDir, profileName)
if err != nil {
log.Debug(err)
if !trace.IsNotFound(err) {
@@ -703,11 +704,11 @@ func Status(profileDir, proxyHost string) (*ProfileStatus, []*ProfileStatus, err
}
// Make sure the profile is nil, which tsh uses to detect that no
// active profile exists.
profile = nil
profileStatus = nil
}
// load the rest of the profiles
profiles, err := client.ListProfileNames(profileDir)
profiles, err := profile.ListProfileNames(profileDir)
if err != nil {
return nil, nil, trace.Wrap(err)
}
@@ -729,7 +730,7 @@ func Status(profileDir, proxyHost string) (*ProfileStatus, []*ProfileStatus, err
others = append(others, ps)
}
return profile, others, nil
return profileStatus, others, nil
}
// LoadProfile populates Config with the values stored in the given
@@ -737,7 +738,7 @@ func Status(profileDir, proxyHost string) (*ProfileStatus, []*ProfileStatus, err
// directory ~/.tsh is used.
func (c *Config) LoadProfile(profileDir string, proxyName string) error {
// read the profile:
cp, err := client.ProfileFromDir(profileDir, ProxyHost(proxyName))
cp, err := profile.FromDir(profileDir, ProxyHost(proxyName))
if err != nil {
if trace.IsNotFound(err) {
return nil
@@ -772,9 +773,9 @@ func (c *Config) SaveProfile(dir string, makeCurrent bool) error {
return nil
}
dir = client.FullProfilePath(dir)
dir = profile.FullProfilePath(dir)
var cp client.Profile
var cp profile.Profile
cp.Username = c.Username
cp.WebProxyAddr = c.WebProxyAddr
cp.SSHProxyAddr = c.SSHProxyAddr
@@ -849,7 +850,7 @@ func (c *Config) KubeProxyHostPort() (string, int) {
}
// KubeClusterAddr returns a public HTTPS address of the proxy for use by
// Kubernetes clients.
// Kubernetes client.
func (c *Config) KubeClusterAddr() string {
host, port := c.KubeProxyHostPort()
return fmt.Sprintf("https://%s:%d", host, port)
@@ -928,7 +929,7 @@ type TeleportClient struct {
// Note: there's no mutex guarding this or localAgent, making
// TeleportClient NOT safe for concurrent use.
lastPing *client.PingResponse
lastPing *webclient.PingResponse
}
// ShellCreatedCallback can be supplied for every teleport client. It will
@@ -2251,14 +2252,14 @@ func (tc *TeleportClient) ActivateKey(ctx context.Context, key *Key) error {
//
// Ping can be called for its side-effect of applying the proxy-provided
// settings (such as various listening addresses).
func (tc *TeleportClient) Ping(ctx context.Context) (*client.PingResponse, error) {
func (tc *TeleportClient) Ping(ctx context.Context) (*webclient.PingResponse, error) {
// If, at some point, there's a need to bypass this caching, consider
// adding a bool argument. At the time of writing this we always want to
// cache.
if tc.lastPing != nil {
return tc.lastPing, nil
}
pr, err := client.Ping(
pr, err := webclient.Ping(
ctx,
tc.WebProxyAddr,
tc.InsecureSkipVerify,
@@ -2339,7 +2340,7 @@ func (tc *TeleportClient) UpdateTrustedCA(ctx context.Context, clusterName strin
// applyProxySettings updates configuration changes based on the advertised
// proxy settings, overriding existing fields in tc.
func (tc *TeleportClient) applyProxySettings(proxySettings client.ProxySettings) error {
func (tc *TeleportClient) applyProxySettings(proxySettings webclient.ProxySettings) error {
// Kubernetes proxy settings.
if proxySettings.Kube.Enabled {
switch {
+9 -9
View File
@@ -23,8 +23,8 @@ import (
"os"
"strings"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/identityfile"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/kube/kubeconfig"
"github.com/gravitational/teleport/lib/sshutils"
@@ -97,9 +97,9 @@ func Write(cfg WriteConfig) (filesWritten []string, err error) {
return nil, trace.Wrap(err)
}
idFile := &apiclient.IdentityFile{
idFile := &identityfile.IdentityFile{
PrivateKey: cfg.Key.Priv,
Certs: apiclient.Certs{
Certs: identityfile.Certs{
SSH: cfg.Key.Cert,
TLS: cfg.Key.TLSCert,
},
@@ -119,7 +119,7 @@ func Write(cfg WriteConfig) (filesWritten []string, err error) {
idFile.CACerts.TLS = append(idFile.CACerts.TLS, ca.TLSCertificates...)
}
if err := apiclient.WriteIdentityFile(idFile, cfg.OutputPath); err != nil {
if err := identityfile.Write(idFile, cfg.OutputPath); err != nil {
return nil, trace.Wrap(err)
}
@@ -132,12 +132,12 @@ func Write(cfg WriteConfig) (filesWritten []string, err error) {
return nil, trace.Wrap(err)
}
err = ioutil.WriteFile(certPath, cfg.Key.Cert, apiclient.IdentityFilePermissions)
err = ioutil.WriteFile(certPath, cfg.Key.Cert, identityfile.FilePermissions)
if err != nil {
return nil, trace.Wrap(err)
}
err = ioutil.WriteFile(keyPath, cfg.Key.Priv, apiclient.IdentityFilePermissions)
err = ioutil.WriteFile(keyPath, cfg.Key.Priv, identityfile.FilePermissions)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -151,12 +151,12 @@ func Write(cfg WriteConfig) (filesWritten []string, err error) {
return nil, trace.Wrap(err)
}
err = ioutil.WriteFile(certPath, cfg.Key.TLSCert, apiclient.IdentityFilePermissions)
err = ioutil.WriteFile(certPath, cfg.Key.TLSCert, identityfile.FilePermissions)
if err != nil {
return nil, trace.Wrap(err)
}
err = ioutil.WriteFile(keyPath, cfg.Key.Priv, apiclient.IdentityFilePermissions)
err = ioutil.WriteFile(keyPath, cfg.Key.Priv, identityfile.FilePermissions)
if err != nil {
return nil, trace.Wrap(err)
}
@@ -166,7 +166,7 @@ func Write(cfg WriteConfig) (filesWritten []string, err error) {
caCerts = append(caCerts, cert...)
}
}
err = ioutil.WriteFile(casPath, caCerts, apiclient.IdentityFilePermissions)
err = ioutil.WriteFile(casPath, caCerts, identityfile.FilePermissions)
if err != nil {
return nil, trace.Wrap(err)
}
+2 -2
View File
@@ -22,7 +22,7 @@ import (
"time"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/identityfile"
"github.com/gravitational/teleport/api/utils/sshutils"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/auth/native"
@@ -107,7 +107,7 @@ func NewKey() (key *Key, err error) {
// KeyFromIdentityFile loads the private key + certificate
// from an identity file into a Key.
func KeyFromIdentityFile(path string) (*Key, error) {
ident, err := client.ReadIdentityFile(path)
ident, err := identityfile.Read(path)
if err != nil {
return nil, trace.Wrap(err, "failed to parse identity file")
}
+2 -2
View File
@@ -29,8 +29,8 @@ import (
"golang.org/x/crypto/ssh"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/profile"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/sshutils"
"github.com/gravitational/teleport/lib/utils"
@@ -147,7 +147,7 @@ func NewFSLocalKeyStore(dirPath string) (s *FSLocalKeyStore, err error) {
// initKeysDir initializes the keystore root directory, usually `~/.tsh`.
func initKeysDir(dirPath string) (string, error) {
dirPath = client.FullProfilePath(dirPath)
dirPath = profile.FullProfilePath(dirPath)
if err := os.MkdirAll(dirPath, os.ModeDir|profileDirPerms); err != nil {
return "", trace.ConvertSystemError(err)
}
+3 -2
View File
@@ -26,6 +26,7 @@ import (
"github.com/gravitational/teleport"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/lib"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/backend"
@@ -850,7 +851,7 @@ func (process *TeleportProcess) findReverseTunnel(addrs []utils.NetAddr) (string
for _, addr := range addrs {
// In insecure mode, any certificate is accepted. In secure mode the hosts
// CAs are used to validate the certificate on the proxy.
resp, err := apiclient.Find(process.ExitContext(),
resp, err := webclient.Find(process.ExitContext(),
addr.String(),
lib.IsInsecureDevMode(),
nil)
@@ -867,7 +868,7 @@ func (process *TeleportProcess) findReverseTunnel(addrs []utils.NetAddr) (string
// 2. SSH Proxy Public Address.
// 3. HTTP Proxy Public Address.
// 4. Tunnel Listen Address.
func tunnelAddr(settings apiclient.ProxySettings) (string, error) {
func tunnelAddr(settings webclient.ProxySettings) (string, error) {
// Extract the port the tunnel server is listening on.
netAddr, err := utils.ParseHostPortAddr(settings.SSH.TunnelListenAddr, defaults.SSHProxyTunnelListenPort)
if err != nil {
+4 -4
View File
@@ -47,7 +47,7 @@ import (
"github.com/gravitational/trace"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/auth/native"
"github.com/gravitational/teleport/lib/backend"
@@ -2486,11 +2486,11 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {
var webServer *http.Server
var webHandler *web.RewritingHandler
if !process.Config.Proxy.DisableWebService {
proxySettings := client.ProxySettings{
Kube: client.KubeProxySettings{
proxySettings := webclient.ProxySettings{
Kube: webclient.KubeProxySettings{
Enabled: cfg.Proxy.Kube.Enabled,
},
SSH: client.SSHProxySettings{
SSH: webclient.SSHProxySettings{
ListenAddr: proxySSHAddr.Addr,
TunnelListenAddr: cfg.Proxy.ReverseTunnelListenAddr.String(),
},
+32 -32
View File
@@ -36,7 +36,7 @@ import (
"time"
"github.com/gravitational/teleport"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
@@ -131,7 +131,7 @@ type Config struct {
CipherSuites []uint16
// ProxySettings is a settings communicated to proxy
ProxySettings apiclient.ProxySettings
ProxySettings webclient.ProxySettings
// FIPS mode means Teleport started in a FedRAMP/FIPS 140-2 compliant
// configuration.
@@ -524,8 +524,8 @@ func (h *Handler) getUserContext(w http.ResponseWriter, r *http.Request, p httpr
return userContext, nil
}
func localSettings(authClient auth.ClientI, cap services.AuthPreference) (apiclient.AuthenticationSettings, error) {
as := apiclient.AuthenticationSettings{
func localSettings(authClient auth.ClientI, cap services.AuthPreference) (webclient.AuthenticationSettings, error) {
as := webclient.AuthenticationSettings{
Type: teleport.Local,
SecondFactor: cap.GetSecondFactor(),
}
@@ -537,17 +537,17 @@ func localSettings(authClient auth.ClientI, cap services.AuthPreference) (apicli
return as, nil
}
if err != nil {
return apiclient.AuthenticationSettings{}, trace.Wrap(err)
return webclient.AuthenticationSettings{}, trace.Wrap(err)
}
as.U2F = &apiclient.U2FSettings{AppID: u2fs.AppID}
as.U2F = &webclient.U2FSettings{AppID: u2fs.AppID}
return as, nil
}
func oidcSettings(connector services.OIDCConnector, cap services.AuthPreference) apiclient.AuthenticationSettings {
return apiclient.AuthenticationSettings{
func oidcSettings(connector services.OIDCConnector, cap services.AuthPreference) webclient.AuthenticationSettings {
return webclient.AuthenticationSettings{
Type: teleport.OIDC,
OIDC: &apiclient.OIDCSettings{
OIDC: &webclient.OIDCSettings{
Name: connector.GetName(),
Display: connector.GetDisplay(),
},
@@ -556,10 +556,10 @@ func oidcSettings(connector services.OIDCConnector, cap services.AuthPreference)
}
}
func samlSettings(connector services.SAMLConnector, cap services.AuthPreference) apiclient.AuthenticationSettings {
return apiclient.AuthenticationSettings{
func samlSettings(connector services.SAMLConnector, cap services.AuthPreference) webclient.AuthenticationSettings {
return webclient.AuthenticationSettings{
Type: teleport.SAML,
SAML: &apiclient.SAMLSettings{
SAML: &webclient.SAMLSettings{
Name: connector.GetName(),
Display: connector.GetDisplay(),
},
@@ -568,10 +568,10 @@ func samlSettings(connector services.SAMLConnector, cap services.AuthPreference)
}
}
func githubSettings(connector services.GithubConnector, cap services.AuthPreference) apiclient.AuthenticationSettings {
return apiclient.AuthenticationSettings{
func githubSettings(connector services.GithubConnector, cap services.AuthPreference) webclient.AuthenticationSettings {
return webclient.AuthenticationSettings{
Type: teleport.Github,
Github: &apiclient.GithubSettings{
Github: &webclient.GithubSettings{
Name: connector.GetName(),
Display: connector.GetDisplay(),
},
@@ -579,35 +579,35 @@ func githubSettings(connector services.GithubConnector, cap services.AuthPrefere
}
}
func defaultAuthenticationSettings(ctx context.Context, authClient auth.ClientI) (apiclient.AuthenticationSettings, error) {
func defaultAuthenticationSettings(ctx context.Context, authClient auth.ClientI) (webclient.AuthenticationSettings, error) {
cap, err := authClient.GetAuthPreference()
if err != nil {
return apiclient.AuthenticationSettings{}, trace.Wrap(err)
return webclient.AuthenticationSettings{}, trace.Wrap(err)
}
var as apiclient.AuthenticationSettings
var as webclient.AuthenticationSettings
switch cap.GetType() {
case teleport.Local:
as, err = localSettings(authClient, cap)
if err != nil {
return apiclient.AuthenticationSettings{}, trace.Wrap(err)
return webclient.AuthenticationSettings{}, trace.Wrap(err)
}
case teleport.OIDC:
if cap.GetConnectorName() != "" {
oidcConnector, err := authClient.GetOIDCConnector(ctx, cap.GetConnectorName(), false)
if err != nil {
return apiclient.AuthenticationSettings{}, trace.Wrap(err)
return webclient.AuthenticationSettings{}, trace.Wrap(err)
}
as = oidcSettings(oidcConnector, cap)
} else {
oidcConnectors, err := authClient.GetOIDCConnectors(ctx, false)
if err != nil {
return apiclient.AuthenticationSettings{}, trace.Wrap(err)
return webclient.AuthenticationSettings{}, trace.Wrap(err)
}
if len(oidcConnectors) == 0 {
return apiclient.AuthenticationSettings{}, trace.BadParameter("no oidc connectors found")
return webclient.AuthenticationSettings{}, trace.BadParameter("no oidc connectors found")
}
as = oidcSettings(oidcConnectors[0], cap)
@@ -616,17 +616,17 @@ func defaultAuthenticationSettings(ctx context.Context, authClient auth.ClientI)
if cap.GetConnectorName() != "" {
samlConnector, err := authClient.GetSAMLConnector(ctx, cap.GetConnectorName(), false)
if err != nil {
return apiclient.AuthenticationSettings{}, trace.Wrap(err)
return webclient.AuthenticationSettings{}, trace.Wrap(err)
}
as = samlSettings(samlConnector, cap)
} else {
samlConnectors, err := authClient.GetSAMLConnectors(ctx, false)
if err != nil {
return apiclient.AuthenticationSettings{}, trace.Wrap(err)
return webclient.AuthenticationSettings{}, trace.Wrap(err)
}
if len(samlConnectors) == 0 {
return apiclient.AuthenticationSettings{}, trace.BadParameter("no saml connectors found")
return webclient.AuthenticationSettings{}, trace.BadParameter("no saml connectors found")
}
as = samlSettings(samlConnectors[0], cap)
@@ -635,21 +635,21 @@ func defaultAuthenticationSettings(ctx context.Context, authClient auth.ClientI)
if cap.GetConnectorName() != "" {
githubConnector, err := authClient.GetGithubConnector(ctx, cap.GetConnectorName(), false)
if err != nil {
return apiclient.AuthenticationSettings{}, trace.Wrap(err)
return webclient.AuthenticationSettings{}, trace.Wrap(err)
}
as = githubSettings(githubConnector, cap)
} else {
githubConnectors, err := authClient.GetGithubConnectors(ctx, false)
if err != nil {
return apiclient.AuthenticationSettings{}, trace.Wrap(err)
return webclient.AuthenticationSettings{}, trace.Wrap(err)
}
if len(githubConnectors) == 0 {
return apiclient.AuthenticationSettings{}, trace.BadParameter("no github connectors found")
return webclient.AuthenticationSettings{}, trace.BadParameter("no github connectors found")
}
as = githubSettings(githubConnectors[0], cap)
}
default:
return apiclient.AuthenticationSettings{}, trace.BadParameter("unknown type %v", cap.GetType())
return webclient.AuthenticationSettings{}, trace.BadParameter("unknown type %v", cap.GetType())
}
return as, nil
@@ -663,7 +663,7 @@ func (h *Handler) ping(w http.ResponseWriter, r *http.Request, p httprouter.Para
return nil, trace.Wrap(err)
}
return apiclient.PingResponse{
return webclient.PingResponse{
Auth: defaultSettings,
Proxy: h.cfg.ProxySettings,
ServerVersion: teleport.Version,
@@ -672,7 +672,7 @@ func (h *Handler) ping(w http.ResponseWriter, r *http.Request, p httprouter.Para
}
func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
return apiclient.PingResponse{
return webclient.PingResponse{
Proxy: h.cfg.ProxySettings,
ServerVersion: teleport.Version,
MinClientVersion: teleport.MinClientVersion,
@@ -688,7 +688,7 @@ func (h *Handler) pingWithConnector(w http.ResponseWriter, r *http.Request, p ht
return nil, trace.Wrap(err)
}
response := &apiclient.PingResponse{
response := &webclient.PingResponse{
Proxy: h.cfg.ProxySettings,
ServerVersion: teleport.Version,
}
+3 -3
View File
@@ -44,7 +44,7 @@ import (
"golang.org/x/text/encoding/unicode"
"github.com/gravitational/teleport"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/api/constants"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
@@ -1547,7 +1547,7 @@ func (s *WebSuite) TestPing(c *C) {
re, err := wc.Get(context.Background(), wc.Endpoint("webapi", "ping"), url.Values{})
c.Assert(err, IsNil)
var out *apiclient.PingResponse
var out *webclient.PingResponse
c.Assert(json.Unmarshal(re.Bytes(), &out), IsNil)
preference, err := s.server.Auth().GetAuthPreference()
@@ -1593,7 +1593,7 @@ func (s *WebSuite) TestMultipleConnectors(c *C) {
// hit the ping endpoint to get the auth type and connector name
re, err := wc.Get(ctx, wc.Endpoint("webapi", "ping"), url.Values{})
c.Assert(err, IsNil)
var out *apiclient.PingResponse
var out *webclient.PingResponse
c.Assert(json.Unmarshal(re.Bytes(), &out), IsNil)
// make sure the connector name we got back was the first connector
+3 -2
View File
@@ -27,6 +27,7 @@ import (
"github.com/gravitational/teleport"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/webclient"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/config"
@@ -259,7 +260,7 @@ func findReverseTunnel(ctx context.Context, addrs []utils.NetAddr, insecureTLS b
for _, addr := range addrs {
// In insecure mode, any certificate is accepted. In secure mode the hosts
// CAs are used to validate the certificate on the proxy.
resp, err := apiclient.Find(ctx, addr.String(), insecureTLS, nil)
resp, err := webclient.Find(ctx, addr.String(), insecureTLS, nil)
if err == nil {
return tunnelAddr(addr, resp.Proxy)
}
@@ -273,7 +274,7 @@ func findReverseTunnel(ctx context.Context, addrs []utils.NetAddr, insecureTLS b
// 2. SSH Proxy Public Address.
// 3. HTTP Proxy Public Address.
// 4. Tunnel Listen Address.
func tunnelAddr(webAddr utils.NetAddr, settings apiclient.ProxySettings) (string, error) {
func tunnelAddr(webAddr utils.NetAddr, settings webclient.ProxySettings) (string, error) {
// Extract the port the tunnel server is listening on.
netAddr, err := utils.ParseHostPortAddr(settings.SSH.TunnelListenAddr, defaults.SSHProxyTunnelListenPort)
if err != nil {
+9 -9
View File
@@ -30,7 +30,7 @@ import (
"golang.org/x/crypto/ssh"
"github.com/gravitational/teleport"
apiclient "github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/profile"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/backend"
@@ -85,9 +85,9 @@ func (p *cliModules) IsBoringBinary() bool {
}
func TestFailedLogin(t *testing.T) {
os.RemoveAll(apiclient.FullProfilePath(""))
os.RemoveAll(profile.FullProfilePath(""))
t.Cleanup(func() {
os.RemoveAll(apiclient.FullProfilePath(""))
os.RemoveAll(profile.FullProfilePath(""))
})
connector := mockConnector(t)
@@ -117,9 +117,9 @@ func TestFailedLogin(t *testing.T) {
}
func TestOIDCLogin(t *testing.T) {
os.RemoveAll(apiclient.FullProfilePath(""))
os.RemoveAll(profile.FullProfilePath(""))
t.Cleanup(func() {
os.RemoveAll(apiclient.FullProfilePath(""))
os.RemoveAll(profile.FullProfilePath(""))
})
modules.SetModules(&cliModules{})
@@ -213,9 +213,9 @@ func TestOIDCLogin(t *testing.T) {
}
func TestRelogin(t *testing.T) {
os.RemoveAll(apiclient.FullProfilePath(""))
os.RemoveAll(profile.FullProfilePath(""))
t.Cleanup(func() {
os.RemoveAll(apiclient.FullProfilePath(""))
os.RemoveAll(profile.FullProfilePath(""))
})
connector := mockConnector(t)
@@ -271,9 +271,9 @@ func TestRelogin(t *testing.T) {
}
func TestMakeClient(t *testing.T) {
os.RemoveAll(apiclient.FullProfilePath(""))
os.RemoveAll(profile.FullProfilePath(""))
t.Cleanup(func() {
os.RemoveAll(apiclient.FullProfilePath(""))
os.RemoveAll(profile.FullProfilePath(""))
})
var conf CLIConf