Files
tsrun/internal/oauth/authkey.go
T
dmgnr e588b9a527
Deploy tsrun / deploy (push) Failing after 15s
feat: add tsrun bootstrap service and deploy
2026-07-22 06:23:21 +00:00

96 lines
2.2 KiB
Go

package oauth
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"golang.org/x/oauth2/clientcredentials"
"tsrun/internal/vault"
)
const tokenURL = "https://api.tailscale.com/api/v2/oauth/token"
type createKeyRequest struct {
Capabilities capabilities `json:"capabilities"`
ExpirySecs int `json:"expirySeconds,omitempty"`
Description string `json:"description,omitempty"`
}
type capabilities struct {
Devices devicesCaps `json:"devices"`
}
type devicesCaps struct {
Create deviceCreate `json:"create"`
}
type deviceCreate struct {
Reusable bool `json:"reusable"`
Ephemeral bool `json:"ephemeral"`
Preauthorized bool `json:"preauthorized"`
Tags []string `json:"tags,omitempty"`
}
type createKeyResponse struct {
ID string `json:"id"`
Key string `json:"key"`
}
func CreateEphemeralAuthKey(ctx context.Context, payload vault.Payload, description string) (string, error) {
config := &clientcredentials.Config{
ClientID: payload.OAuthClientID,
ClientSecret: payload.OAuthClientSecret,
TokenURL: tokenURL,
}
httpClient := config.Client(ctx)
httpClient.Timeout = 20 * time.Second
body, err := json.Marshal(createKeyRequest{
Capabilities: capabilities{Devices: devicesCaps{Create: deviceCreate{
Reusable: false,
Ephemeral: true,
Preauthorized: true,
Tags: payload.Tags,
}}},
ExpirySecs: 3600,
Description: description,
})
if err != nil {
return "", err
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.tailscale.com/api/v2/tailnet/-/keys", bytes.NewReader(body))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")
resp, err := httpClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return "", fmt.Errorf("tailscale key request failed: %s", resp.Status)
}
var out createKeyResponse
if err := json.Unmarshal(bodyBytes, &out); err != nil {
return "", err
}
if out.Key == "" {
return "", fmt.Errorf("tailscale key request returned empty key")
}
return out.Key, nil
}