Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into kopiczko/add-role.s…
Browse files Browse the repository at this point in the history
…pec.allow.request.reason.required
  • Loading branch information
kopiczko committed Nov 27, 2024
2 parents 3bbc36b + 3a24bdd commit f1e5833
Show file tree
Hide file tree
Showing 24 changed files with 998 additions and 586 deletions.
588 changes: 295 additions & 293 deletions api/client/proto/authservice.pb.go

Large diffs are not rendered by default.

67 changes: 67 additions & 0 deletions api/client/webclient/webclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ func Find(cfg *Config) (*PingResponse, error) {
}
defer clt.CloseIdleConnections()

return findWithClient(cfg, clt)
}

func findWithClient(cfg *Config, clt *http.Client) (*PingResponse, error) {
ctx, span := cfg.TraceProvider.Tracer("webclient").Start(cfg.Context, "webclient/Find")
defer span.End()

Expand Down Expand Up @@ -214,6 +218,10 @@ func Ping(cfg *Config) (*PingResponse, error) {
}
defer clt.CloseIdleConnections()

return pingWithClient(cfg, clt)
}

func pingWithClient(cfg *Config, clt *http.Client) (*PingResponse, error) {
ctx, span := cfg.TraceProvider.Tracer("webclient").Start(cfg.Context, "webclient/Ping")
defer span.End()

Expand Down Expand Up @@ -267,13 +275,18 @@ func Ping(cfg *Config) (*PingResponse, error) {
return pr, nil
}

// GetMOTD retrieves the Message Of The Day from the web proxy.
func GetMOTD(cfg *Config) (*MotD, error) {
clt, err := newWebClient(cfg)
if err != nil {
return nil, trace.Wrap(err)
}
defer clt.CloseIdleConnections()

return getMOTDWithClient(cfg, clt)
}

func getMOTDWithClient(cfg *Config, clt *http.Client) (*MotD, error) {
ctx, span := cfg.TraceProvider.Tracer("webclient").Start(cfg.Context, "webclient/GetMOTD")
defer span.End()

Expand Down Expand Up @@ -302,6 +315,60 @@ func GetMOTD(cfg *Config) (*MotD, error) {
return motd, nil
}

// NewReusableClient creates a reusable webproxy client. If you need to do a single call,
// use the webclient.Ping or webclient.Find functions instead.
func NewReusableClient(cfg *Config) (*ReusableClient, error) {
// no need to check and set config defaults, this happens in newWebClient
client, err := newWebClient(cfg)
if err != nil {
return nil, trace.Wrap(err, "building new web client")
}

return &ReusableClient{
client: client,
config: cfg,
}, nil
}

// ReusableClient is a webproxy client that allows the caller to make multiple calls
// without having to buildi a new HTTP client each time.
// Before retiring the client, you must make sure no calls are still in-flight, then call
// ReusableClient.CloseIdleConnections().
type ReusableClient struct {
client *http.Client
config *Config
}

// Find fetches discovery data by connecting to the given web proxy address.
// It is designed to fetch proxy public addresses without any inefficiencies.
func (c *ReusableClient) Find() (*PingResponse, error) {
return findWithClient(c.config, c.client)
}

// Ping serves two purposes. The first is to validate the HTTP endpoint of a
// Teleport proxy. This leads to better user experience: users get connection
// errors before being asked for passwords. The second is to return the form
// of authentication that the server supports. This also leads to better user
// experience: users only get prompted for the type of authentication the server supports.
func (c *ReusableClient) Ping() (*PingResponse, error) {
return pingWithClient(c.config, c.client)
}

// GetMOTD retrieves the Message Of The Day from the web proxy.
func (c *ReusableClient) GetMOTD() (*MotD, error) {
return getMOTDWithClient(c.config, c.client)
}

// CloseIdleConnections closes any connections on its [Transport] which
// were previously connected from previous requests but are now
// sitting idle in a "keep-alive" state. It does not interrupt any
// connections currently in use.
//
// This must be run before retiring the ReusableClient.
func (c *ReusableClient) CloseIdleConnections() {
c.client.CloseIdleConnections()
}

// MotD holds data about the current message of the day.
type MotD struct {
Text string
Expand Down
8 changes: 4 additions & 4 deletions api/proto/teleport/legacy/client/proto/authservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2553,10 +2553,10 @@ message InventoryPingRequest {
// ServerID is the ID of the instance to ping.
string ServerID = 1;

// ControlLog forces the ping to use the standard "commit then act" model of control log synchronization
// for the ping. This significantly increases the amount of time it takes for the ping request to
// complete, but is useful for testing/debugging control log issues.
bool ControlLog = 2;
// ControlLog used to signal that the ping should use the control log synchronization.
//
// Deprecated: the control log is unsupported and unsound to use.
bool ControlLog = 2 [deprecated = true];
}

// InventoryPingResponse returns the result of an inventory ping initiated via an
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Teleport processes joining the cluster.
by default). Remove the data directory if this instance has previously joined
a Teleport cluster.

## Step 1/4. Set up AWS IAM credentials
## Step 1/5. Set up AWS IAM credentials

The Teleport Auth Service needs permission to call `ec2:DescribeInstances` in order to check
that the EC2 instances attempting to join your cluster are legitimate and
Expand Down Expand Up @@ -86,7 +86,7 @@ file or environment variables. See
[Specifying Credentials](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#specifying-credentials)
for details.

## Step 2/4. Create the AWS joining token
## Step 2/5. Create the AWS joining token

Configure your Teleport Auth Service with a special dynamic token which will
allow services from your AWS account to join your Teleport cluster.
Expand All @@ -102,7 +102,13 @@ account and the AWS regions in which your EC2 instances will run.

Run `tctl create token.yaml` to create the token.

## Step 3/4. Configure your services
## Step 3/5 Install Teleport

Install Teleport on your AWS EC2 Instance.

(!docs/pages/includes/install-linux.mdx!)

## Step 4/5. Configure your services

The EC2 join method can be used for Teleport processes running the SSH, Proxy,
Kubernetes, Application, Database, or Windows Desktop Services. The Teleport
Expand All @@ -129,7 +135,7 @@ proxy_service:
enabled: no
```

## Step 4/4. Launch your Teleport process
## Step 5/5. Launch your Teleport process

<Admonition
type="note"
Expand All @@ -144,6 +150,8 @@ proxy_service:

</Admonition>

(!docs/pages/includes/start-teleport.mdx!)

Start Teleport on the host and confirm that it is able to connect to and join
your cluster. You're all set!

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ balancer or reverse proxy is available in Teleport 13.0+.

(!docs/pages/includes/tctl.mdx!)

## Step 1/4. Set up AWS IAM credentials
## Step 1/5. Set up AWS IAM credentials

Every Teleport process using the IAM method to join your Teleport cluster needs
AWS IAM credentials in order to call the `sts:GetCallerIdentity` API. No
Expand All @@ -62,7 +62,7 @@ attached IAM policies at all. If your instance does not otherwise need AWS
credentials, it is preferred to create and attach an empty role with no attached
policies.

## Step 2/4. Create the AWS joining token
## Step 2/5. Create the AWS joining token

Create the following `token.yaml` with an `allow` rule specifying your AWS
account and the ARN that the Teleport process's identity must match.
Expand All @@ -82,7 +82,13 @@ Run the following command to create the token:
$ tctl create -f token.yaml
```

## Step 3/4. Configure your services
## Step 3/5 Install Teleport

Install Teleport on your AWS EC2 instance.

(!docs/pages/includes/install-linux.mdx!)

## Step 4/5. Configure your services

The IAM join method can be used for Teleport processes running the SSH, Proxy,
Kubernetes, Application, or Database Service.
Expand Down Expand Up @@ -111,7 +117,7 @@ In the `teleport.proxy_server` field, replace the value with the host and web
port of your Teleport Proxy Service or Teleport Enterprise Cloud tenant, e.g.,
`mytenant.teleport.sh:443`.

## Step 4/4. Launch your Teleport process
## Step 5/5. Launch your Teleport process

(!docs/pages/includes/aws-credentials.mdx!)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Teleport Services to a Cluster](join-services-to-your-cluster.mdx).
assigned to it with permission to read virtual machine info.
- (!docs/pages/includes/tctl.mdx!)

## Step 1/4. Set up a Managed Identity
## Step 1/5. Set up a Managed Identity

Every virtual machine hosting a Teleport process using the Azure method to join
your Teleport cluster needs a Managed Identity assigned to it. The identity
Expand All @@ -33,7 +33,7 @@ look up the virtual machine. No other permissions are required.

(!docs/pages/includes/server-access/azure-join-managed-identity.mdx!)

## Step 2/4. Create the Azure joining token
## Step 2/5. Create the Azure joining token

Under the hood, Teleport processes will prove that they are running in your
Azure subscription by sending a signed attested data document and access token
Expand All @@ -54,7 +54,13 @@ Run the following command to create the token:
$ tctl create -f token.yaml
```

## Step 3/4. Configure your Teleport process
## Step 3/5 Install Teleport

Install Teleport on your Azure Linux VM.

(!docs/pages/includes/install-linux.mdx!)

## Step 4/5. Configure your Teleport process

The Azure join method can be used for Teleport processes running the SSH, Proxy,
Kubernetes, Application, Database, or Desktop Service.
Expand Down Expand Up @@ -82,7 +88,7 @@ proxy_service:
enabled: no
```
## Step 4/4. Launch your Teleport process
## Step 5/5. Launch your Teleport process
Start Teleport on the Azure VM.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ on the Teleport process joining the cluster.
and with the Teleport binary installed.
- (!docs/pages/includes/tctl.mdx!)

## Step 1/3. Create the GCP joining token
## Step 1/4. Create the GCP joining token

Configure your Teleport Auth Service with a special dynamic token which will
allow services from your GCP projects to join your Teleport cluster.
Expand All @@ -42,7 +42,13 @@ Run the following command to create the token:
$ tctl create token.yaml
```

## Step 2/3. Configure your services
## Step 2/4 Install Teleport

Install Teleport on your GCP Linux VM.

(!docs/pages/includes/install-linux.mdx!)

## Step 3/4. Configure your services

The GCP join method can be used for Teleport processes running the SSH (`Node`), Proxy,
Kubernetes, Application, Database, or Windows Desktop Services. The Teleport
Expand All @@ -68,7 +74,7 @@ proxy_service:
enabled: no
```
## Step 3/3. Launch your Teleport process
## Step 4/4. Launch your Teleport process
(!docs/pages/includes/start-teleport.mdx!)
Expand Down
76 changes: 2 additions & 74 deletions lib/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -4831,94 +4831,22 @@ func (a *Server) GetInventoryConnectedServiceCount(service types.SystemRole) uin
}

func (a *Server) PingInventory(ctx context.Context, req proto.InventoryPingRequest) (proto.InventoryPingResponse, error) {
const pingAttempt = "ping-attempt"
const pingSuccess = "ping-success"
const maxAttempts = 16
stream, ok := a.inventory.GetControlStream(req.ServerID)
if !ok {
return proto.InventoryPingResponse{}, trace.NotFound("no control stream found for server %q", req.ServerID)
}

id := mathrand.Uint64()

if !req.ControlLog {
// this ping doesn't pass through the control log, so just execute it immediately.
d, err := stream.Ping(ctx, id)
return proto.InventoryPingResponse{
Duration: d,
}, trace.Wrap(err)
}

// matchEntry is used to check if our log entry has been included
// in the control log.
matchEntry := func(entry types.InstanceControlLogEntry) bool {
return entry.Type == pingAttempt && entry.ID == id
}

var included bool
for i := 1; i <= maxAttempts; i++ {
stream.VisitInstanceState(func(ref inventory.InstanceStateRef) (update inventory.InstanceStateUpdate) {
// check if we've already successfully included the ping entry
if ref.LastHeartbeat != nil {
if slices.IndexFunc(ref.LastHeartbeat.GetControlLog(), matchEntry) >= 0 {
included = true
return
}
}

// if the entry pending already, we just need to wait
if slices.IndexFunc(ref.QualifiedPendingControlLog, matchEntry) >= 0 {
return
}

// either this is the first iteration, or the pending control log was reset.
update.QualifiedPendingControlLog = append(update.QualifiedPendingControlLog, types.InstanceControlLogEntry{
Type: pingAttempt,
ID: id,
Time: time.Now(),
})
stream.HeartbeatInstance()
return
})

if included {
// entry appeared in control log
break
}

// pause briefly, then re-sync our state. note that this strategy is not scalable. control log usage is intended only
// for periodic operations. control-log based pings are a mechanism for testing/debugging only, hence the use of a
// simple sleep loop.
select {
case <-time.After(time.Millisecond * 100 * time.Duration(i)):
case <-stream.Done():
return proto.InventoryPingResponse{}, trace.Errorf("control stream closed during ping attempt")
case <-ctx.Done():
return proto.InventoryPingResponse{}, trace.Wrap(ctx.Err())
}
}

if !included {
return proto.InventoryPingResponse{}, trace.LimitExceeded("failed to include ping %d in control log for instance %q (max attempts exceeded)", id, req.ServerID)
if req.ControlLog { //nolint:staticcheck // SA1019. Checking deprecated field that may be sent by older clients.
return proto.InventoryPingResponse{}, trace.BadParameter("ControlLog pings are not supported")
}

d, err := stream.Ping(ctx, id)
if err != nil {
return proto.InventoryPingResponse{}, trace.Wrap(err)
}

stream.VisitInstanceState(func(_ inventory.InstanceStateRef) (update inventory.InstanceStateUpdate) {
update.UnqualifiedPendingControlLog = append(update.UnqualifiedPendingControlLog, types.InstanceControlLogEntry{
Type: pingSuccess,
ID: id,
Labels: map[string]string{
"duration": d.String(),
},
})
return
})
stream.HeartbeatInstance()

return proto.InventoryPingResponse{
Duration: d,
}, nil
Expand Down
Loading

0 comments on commit f1e5833

Please sign in to comment.