mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-10 23:57:34 +02:00
174 lines
6.1 KiB
Go
174 lines
6.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/rs/zerolog"
|
|
"github.com/spf13/cobra"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
"gopkg.in/square/go-jose.v2/jwt"
|
|
|
|
"github.com/pomerium/pomerium/internal/encoding/jws"
|
|
"github.com/pomerium/pomerium/internal/log"
|
|
"github.com/pomerium/pomerium/pkg/grpc"
|
|
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
|
"github.com/pomerium/pomerium/pkg/grpc/user"
|
|
)
|
|
|
|
type stringSlice []string
|
|
|
|
func (i *stringSlice) String() string {
|
|
return fmt.Sprint(*i)
|
|
}
|
|
|
|
func (i *stringSlice) Set(value string) error {
|
|
if len(*i) > 0 {
|
|
return errors.New("already set")
|
|
}
|
|
for _, dt := range strings.Split(value, ",") {
|
|
*i = append(*i, dt)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (i *stringSlice) Type() string {
|
|
return "slice"
|
|
}
|
|
|
|
type serviceAccount struct {
|
|
// Standard claims (as specified in RFC 7519).
|
|
jwt.Claims
|
|
// Pomerium claims (not standard claims)
|
|
Email string `json:"email"`
|
|
Groups []string `json:"groups,omitempty"`
|
|
User string `json:"user,omitempty"`
|
|
ImpersonateEmail string `json:"impersonate_email,omitempty"`
|
|
ImpersonateGroups []string `json:"impersonate_groups,omitempty"`
|
|
}
|
|
|
|
var serviceAccountOptions struct {
|
|
aud stringSlice
|
|
groups stringSlice
|
|
impersonateGroups stringSlice
|
|
expiry time.Duration
|
|
serviceAccount serviceAccount
|
|
dataBrokerURL string
|
|
overrideCertificateName string
|
|
ca string
|
|
caFile string
|
|
}
|
|
|
|
func init() {
|
|
flags := serviceAccountCmd.PersistentFlags()
|
|
flags.StringVar(&serviceAccountOptions.serviceAccount.Email, "email", "", "Email")
|
|
flags.StringVar(&serviceAccountOptions.serviceAccount.ImpersonateEmail, "impersonate_email", "", "Impersonation Email (optional)")
|
|
flags.StringVar(&serviceAccountOptions.serviceAccount.Issuer, "iss", "", "Issuing Server (e.g authenticate.int.pomerium.io)")
|
|
flags.StringVar(&serviceAccountOptions.serviceAccount.Subject, "sub", "", "Subject (typically User's GUID)")
|
|
flags.StringVar(&serviceAccountOptions.serviceAccount.User, "user", "", "User (typically User's GUID)")
|
|
flags.Var(&serviceAccountOptions.aud, "aud", "Audience (e.g. httpbin.int.pomerium.io,prometheus.int.pomerium.io)")
|
|
flags.Var(&serviceAccountOptions.groups, "groups", "Groups (e.g. admins@pomerium.io,users@pomerium.io)")
|
|
flags.Var(&serviceAccountOptions.impersonateGroups, "impersonate_groups", "Impersonation Groups (optional)")
|
|
flags.DurationVar(&serviceAccountOptions.expiry, "expiry", time.Hour, "Expiry")
|
|
flags.StringVar(&serviceAccountOptions.dataBrokerURL, "databroker-url", "http://localhost:5443", "the URL of the databroker used to store service accounts")
|
|
flags.StringVar(&serviceAccountOptions.overrideCertificateName, "override-certificate-name", "", "override the certificate name")
|
|
flags.StringVar(&serviceAccountOptions.ca, "certificate-authority", "", "custom certificate authority")
|
|
flags.StringVar(&serviceAccountOptions.caFile, "certificate-authority-file", "", "customer certificate authority file")
|
|
rootCmd.AddCommand(serviceAccountCmd)
|
|
}
|
|
|
|
var serviceAccountCmd = &cobra.Command{
|
|
Use: "service-account",
|
|
Short: "generates a pomerium service account from a shared key.",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
l := zerolog.Nop()
|
|
log.SetLogger(&l)
|
|
|
|
// hydrate our session
|
|
serviceAccountOptions.serviceAccount.Audience = jwt.Audience(serviceAccountOptions.aud)
|
|
serviceAccountOptions.serviceAccount.Groups = []string(serviceAccountOptions.groups)
|
|
serviceAccountOptions.serviceAccount.ImpersonateGroups = []string(serviceAccountOptions.impersonateGroups)
|
|
serviceAccountOptions.serviceAccount.Expiry = jwt.NewNumericDate(time.Now().Add(serviceAccountOptions.expiry))
|
|
serviceAccountOptions.serviceAccount.IssuedAt = jwt.NewNumericDate(time.Now())
|
|
serviceAccountOptions.serviceAccount.NotBefore = jwt.NewNumericDate(time.Now())
|
|
|
|
var sharedKey string
|
|
if len(args) == 1 {
|
|
sharedKey = args[0]
|
|
} else if k := os.Getenv("POMERIUM_SHARED_KEY"); k != "" {
|
|
sharedKey = k
|
|
} else {
|
|
fmt.Print("Enter base64 encoded shared key >")
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
scanner.Scan()
|
|
sharedKey = scanner.Text()
|
|
}
|
|
|
|
if sharedKey == "" {
|
|
return errors.New("shared key required")
|
|
}
|
|
|
|
if serviceAccountOptions.serviceAccount.Email == "" {
|
|
return errors.New("email is required")
|
|
}
|
|
|
|
if len(serviceAccountOptions.serviceAccount.Audience) == 0 {
|
|
return errors.New("aud is required")
|
|
}
|
|
|
|
if serviceAccountOptions.serviceAccount.Issuer == "" {
|
|
return errors.New("iss is required")
|
|
}
|
|
|
|
dataBrokerURL, err := url.Parse(serviceAccountOptions.dataBrokerURL)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid databroker url: %w", err)
|
|
}
|
|
|
|
rawSharedKey, _ := base64.StdEncoding.DecodeString(sharedKey)
|
|
|
|
cc, err := grpc.GetGRPCClientConn("databroker", &grpc.Options{
|
|
Addr: dataBrokerURL,
|
|
OverrideCertificateName: serviceAccountOptions.overrideCertificateName,
|
|
CA: serviceAccountOptions.ca,
|
|
CAFile: serviceAccountOptions.caFile,
|
|
WithInsecure: !strings.HasSuffix(dataBrokerURL.Scheme, "s"),
|
|
SignedJWTKey: rawSharedKey,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("error creating databroker connection: %w", err)
|
|
}
|
|
defer cc.Close()
|
|
|
|
sa := &user.ServiceAccount{
|
|
Id: uuid.New().String(),
|
|
UserId: serviceAccountOptions.serviceAccount.User,
|
|
ExpiresAt: timestamppb.New(serviceAccountOptions.serviceAccount.Expiry.Time()),
|
|
IssuedAt: timestamppb.Now(),
|
|
}
|
|
_, err = user.SetServiceAccount(context.Background(), databroker.NewDataBrokerServiceClient(cc), sa)
|
|
if err != nil {
|
|
return fmt.Errorf("error saving service account: %w", err)
|
|
}
|
|
serviceAccountOptions.serviceAccount.ID = sa.GetId()
|
|
|
|
encoder, err := jws.NewHS256Signer([]byte(sharedKey))
|
|
if err != nil {
|
|
return fmt.Errorf("bad shared key: %w", err)
|
|
}
|
|
raw, err := encoder.Marshal(serviceAccountOptions.serviceAccount)
|
|
if err != nil {
|
|
return fmt.Errorf("bad encode: %w", err)
|
|
}
|
|
fmt.Fprintf(os.Stdout, "%s", raw)
|
|
return nil
|
|
},
|
|
}
|