mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-17 17:40:30 +08:00
Refactor api package and docs to use pkg.go.dev effectively. (#6388)
This commit is contained in:
@@ -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
@@ -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...)
|
||||
|
||||
@@ -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
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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 +0,0 @@
|
||||
certs
|
||||
@@ -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)
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user