cmd: add cli to generate service accounts (#552)

Signed-off-by: Bobby DeSimone <bobbydesimone@gmail.com>
This commit is contained in:
Bobby DeSimone 2020-03-24 20:23:07 -07:00 committed by GitHub
parent cc504362e4
commit 9bee6bb648
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 173 additions and 0 deletions

169
cmd/pomerium-cli/cli.go Normal file
View file

@ -0,0 +1,169 @@
package main
import (
"bufio"
"encoding/base64"
"encoding/json"
"errors"
"flag"
"fmt"
"os"
"strings"
"time"
"github.com/fatih/color"
"gopkg.in/square/go-jose.v2"
"gopkg.in/square/go-jose.v2/jwt"
)
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
}
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"`
}
func main() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, color.RedString("\n⛔ %s\n\n"), err)
printHelp(flags)
os.Exit(1)
}
os.Exit(0)
}
var flags *flag.FlagSet
func run() error {
var sa serviceAccount
// temporary variables we will use to hydrate our service account
// struct from basic types pulled in from our flags
var (
aud stringSlice
groups stringSlice
impersonateGroups stringSlice
expiry time.Duration
)
// set our JWT claims from the supplied CLI flags
flags = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
flags.StringVar(&sa.Email, "email", "", "Email")
flags.StringVar(&sa.ImpersonateEmail, "impersonate_email", "", "Impersonation Email (optional)")
flags.StringVar(&sa.Issuer, "iss", "", "Issuing Server (e.g authenticate.int.pomerium.io)")
flags.StringVar(&sa.Subject, "sub", "", "Subject (typically User's GUID)")
flags.StringVar(&sa.User, "user", "", "User (typically User's GUID)")
flags.Var(&aud, "aud", "Audience (e.g. httpbin.int.pomerium.io,prometheus.int.pomerium.io)")
flags.Var(&groups, "groups", "Groups (e.g. admins@pomerium.io,users@pomerium.io)")
flags.Var(&impersonateGroups, "impersonate_groups", "Impersonation Groups (optional)")
flags.DurationVar(&expiry, "expiry", time.Hour, "Expiry")
// hydrate the sessions non-primate types
if err := flags.Parse(os.Args[1:]); err != nil {
return err
}
sa.Audience = jwt.Audience(aud)
sa.Groups = []string(groups)
sa.ImpersonateGroups = []string(impersonateGroups)
sa.Expiry = jwt.NewNumericDate(time.Now().Add(expiry))
sa.IssuedAt = jwt.NewNumericDate(time.Now())
sa.NotBefore = jwt.NewNumericDate(time.Now())
// why not be pretty?
c := color.New(color.FgGreen)
// check that we've got our shared key to sign our jwt
var sharedKey string
args := flags.Args()
if len(args) == 1 {
sharedKey = args[0]
} else {
if _, err := c.Println("Enter base64 encoded shared key >"); err != nil {
return err
}
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
sharedKey = scanner.Text()
}
if sharedKey == "" {
return errors.New("shared key required")
}
if sa.Email == "" {
return errors.New("email is required")
}
if len(sa.Audience) == 0 {
return errors.New("aud is required")
}
if sa.Issuer == "" {
return errors.New("iss is required")
}
decodedKey, err := base64.StdEncoding.DecodeString(sharedKey)
if err != nil {
return fmt.Errorf("shared key not base64: %w", err)
}
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: decodedKey}, nil)
if err != nil {
return fmt.Errorf("bad shared key: %w", err)
}
raw, err := jwt.Signed(signer).Claims(sa).CompactSerialize()
if err != nil {
return fmt.Errorf("couldn't sign jwt: %w", err)
}
saJSON, err := json.MarshalIndent(sa, "", " ")
if err != nil {
return fmt.Errorf("couldn't pretty print jwt: %w", err)
}
if _, err := c.Println("Service Account"); err != nil {
return err
}
fmt.Fprintf(os.Stdout, "%s\n\n", saJSON)
if _, err := c.Println("JWT 🍪"); err != nil {
return err
}
fmt.Fprintf(os.Stdout, "%s\n\n", raw)
return nil
}
func printHelp(fs *flag.FlagSet) {
fmt.Fprintf(os.Stderr, strings.TrimSpace(help)+"\n\n", os.Args[0])
fs.PrintDefaults()
}
const help = `
pomerium-sa generates a pomerium service account from a shared key.
Usage: %[1]s [flags] [base64'd shared secret setting]
For additional help see:
https://www.pomerium.io
https://jwt.io/
Flags:
`

1
go.mod
View file

@ -8,6 +8,7 @@ require (
contrib.go.opencensus.io/exporter/prometheus v0.1.0
github.com/cespare/xxhash/v2 v2.1.1
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/fatih/color v1.7.0
github.com/fsnotify/fsnotify v1.4.9
github.com/go-redis/redis/v7 v7.2.0
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e

3
go.sum
View file

@ -63,6 +63,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@ -193,7 +194,9 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39 h1:0E3wlIAcvD6zt/8UJgTd4JMT6UQhsnYyjCIqllyVLbs=
github.com/mattn/go-runewidth v0.0.0-20181025052659-b20a3daf6a39/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=