codersdk/agentsdk/azure.go
Rama main · réplica de lectura del repositorio original.
package agentsdk
import (
"context"
"encoding/json"
"net/http"
"github.com/coder/coder/v2/codersdk"
)
type AzureInstanceIdentityToken struct {
Signature string `json:"signature" validate:"required"`
Encoding string `json:"encoding" validate:"required"`
// AgentName optionally selects a specific agent when multiple
// agents share the same instance identity. An empty string is
// treated as unspecified.
AgentName string `json:"agent_name,omitempty"`
}
// AzureSessionTokenExchanger exchanges Azure attested metadata for a Coder session token.
// @typescript-ignore AzureSessionTokenExchanger
type AzureSessionTokenExchanger struct {
client *codersdk.Client
agentName string
}
func WithAzureInstanceIdentity(opts ...InstanceIdentityOption) SessionTokenSetup {
cfg := applyInstanceIdentityOptions(opts)
return func(client *codersdk.Client) RefreshableSessionTokenProvider {
return &InstanceIdentitySessionTokenProvider{
TokenExchanger: &AzureSessionTokenExchanger{client: client, agentName: cfg.AgentName},
}
}
}
// AuthWorkspaceAzureInstanceIdentity uses the Azure Instance Metadata Service to
// fetch a signed payload, and exchange it for a session token for a workspace agent.
func (a *AzureSessionTokenExchanger) exchange(ctx context.Context) (AuthenticateResponse, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://169.254.169.254/metadata/attested/document?api-version=2020-09-01", nil)
if err != nil {
return AuthenticateResponse{}, nil
}
req.Header.Set("Metadata", "true")
res, err := a.client.HTTPClient.Do(req)
if err != nil {
return AuthenticateResponse{}, err
}
defer res.Body.Close()
var token AzureInstanceIdentityToken
err = json.NewDecoder(res.Body).Decode(&token) //nolint:gocritic // Azure IMDS attested document response, not the Coder API.
if err != nil {
return AuthenticateResponse{}, err
}
token.AgentName = a.agentName
res, err = a.client.RequestWithoutSessionToken(ctx, http.MethodPost, "/api/v2/workspaceagents/azure-instance-identity", token)
if err != nil {
return AuthenticateResponse{}, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return AuthenticateResponse{}, codersdk.ReadBodyAsError(res)
}
var resp AuthenticateResponse
return resp, codersdk.ReadBodyAsJSON(res, &resp)
}