You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
7.8 KiB
Go
304 lines
7.8 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/pem"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/urfave/cli/v2"
|
|
"go-micro.dev/v4"
|
|
"go-micro.dev/v4/logger"
|
|
|
|
"jochum.dev/jo-micro/auth2"
|
|
"jochum.dev/jo-micro/auth2/cmd/microauth2sqld/config"
|
|
"jochum.dev/jo-micro/auth2/cmd/microauth2sqld/handler"
|
|
"jochum.dev/jo-micro/auth2/internal/proto/authpb"
|
|
"jochum.dev/jo-micro/buncomponent"
|
|
"jochum.dev/jo-micro/components"
|
|
"jochum.dev/jo-micro/logruscomponent"
|
|
"jochum.dev/jo-micro/router"
|
|
|
|
"jochum.dev/jo-micro/auth2/plugins/client/jwt"
|
|
)
|
|
|
|
var (
|
|
ErrorNoKeys = errors.New("config AUTH2_JWT_*_KEY or AUTH2_JWT_REFRESH_*_KEY not given")
|
|
)
|
|
|
|
func generateEd25519PEMKeyPair() (string, string, error) {
|
|
pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
privPKCS8, err := x509.MarshalPKCS8PrivateKey(privKey)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
privPem := pem.EncodeToMemory(&pem.Block{
|
|
Type: "PRIVATE KEY",
|
|
Bytes: privPKCS8,
|
|
})
|
|
|
|
pubPKCS8, err := x509.MarshalPKIXPublicKey(pubKey)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
pubPem := pem.EncodeToMemory(&pem.Block{
|
|
Type: "PUBLIC KEY",
|
|
Bytes: pubPKCS8,
|
|
})
|
|
|
|
return base64.StdEncoding.EncodeToString(pubPem), base64.StdEncoding.EncodeToString(privPem), nil
|
|
}
|
|
|
|
func generateRSAPEMKeyPair(bits int) (string, string, error) {
|
|
privKey, err := rsa.GenerateKey(rand.Reader, bits)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
privPKCS8, err := x509.MarshalPKCS8PrivateKey(privKey)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
privPem := pem.EncodeToMemory(&pem.Block{
|
|
Type: "RSA PRIVATE KEY",
|
|
Bytes: privPKCS8,
|
|
})
|
|
|
|
pubPKCS8, err := x509.MarshalPKIXPublicKey(&privKey.PublicKey)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
pubPem := pem.EncodeToMemory(&pem.Block{
|
|
Type: "PUBLIC KEY",
|
|
Bytes: pubPKCS8,
|
|
})
|
|
|
|
return base64.StdEncoding.EncodeToString(pubPem), base64.StdEncoding.EncodeToString(privPem), nil
|
|
}
|
|
|
|
func main() {
|
|
service := micro.NewService()
|
|
cReg := components.New(service, "auth2", logruscomponent.New(), auth2.ClientAuthComponent(), buncomponent.New(), router.New())
|
|
|
|
auth2ClientReg := auth2.ClientAuthMustReg(cReg)
|
|
auth2ClientReg.Register(jwt.New())
|
|
auth2ClientReg.ForcePlugin("jwt")
|
|
|
|
flags := []cli.Flag{
|
|
// Generate
|
|
&cli.BoolFlag{
|
|
Name: "auth2_generate_keys",
|
|
Usage: "Generate keys for the config and/or the environment",
|
|
Value: false,
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "auth2_generate_format",
|
|
Usage: "Format for \"auth2_generate_keys\", \"RSA4096\", \"RSA2048\" or \"Ed25519\"",
|
|
Value: "Ed25519",
|
|
},
|
|
|
|
// General
|
|
&cli.StringFlag{
|
|
Name: "auth2_sqld_router_basepath",
|
|
Usage: "Router basepath",
|
|
EnvVars: []string{"AUTH2_SQLD_ROUTER_BASEPATH"},
|
|
Value: "auth",
|
|
},
|
|
|
|
// Keys
|
|
// given by they ClientAuth
|
|
&cli.StringFlag{
|
|
Name: "auth2_jwt_pub_key",
|
|
Usage: "Public access key PEM base64 encoded",
|
|
EnvVars: []string{"AUTH2_JWT_PUB_KEY"},
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "auth2_jwt_priv_key",
|
|
Usage: "Private access key PEM base64 encoded",
|
|
EnvVars: []string{"AUTH2_JWT_PRIV_KEY"},
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "auth2_jwt_refresh_pub_key",
|
|
Usage: "Public refresh key PEM base64 encoded",
|
|
EnvVars: []string{"AUTH2_JWT_REFRESH_PUB_KEY"},
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "auth2_jwt_refresh_priv_key",
|
|
Usage: "Private refresh key PEM base64 encoded",
|
|
EnvVars: []string{"AUTH2_JWT_REFRESH_PRIV_KEY"},
|
|
},
|
|
|
|
// Token
|
|
&cli.Int64Flag{
|
|
Name: "auth2_jwt_refresh_expiry",
|
|
Usage: "Expire the refreshtoken after x seconds, default is one day",
|
|
EnvVars: []string{"AUTH2_JWT_REFRESH_EXPIRY"},
|
|
Value: 86400,
|
|
},
|
|
&cli.Int64Flag{
|
|
Name: "auth2_jwt_access_expiry",
|
|
Usage: "Expire the accesstoken after x seconds, default is 15 minutes",
|
|
EnvVars: []string{"AUTH2_JWT_ACCESS_EXPIRY"},
|
|
Value: 900,
|
|
},
|
|
&cli.StringSliceFlag{
|
|
Name: "auth2_jwt_audience",
|
|
Usage: "Add and expect this JWT audience",
|
|
EnvVars: []string{"AUTH2_JWT_AUDIENCES"},
|
|
},
|
|
}
|
|
|
|
authHandler := handler.NewHandler()
|
|
|
|
opts := []micro.Option{
|
|
micro.Name(config.Name),
|
|
micro.Version(config.Version),
|
|
micro.Flags(components.FilterDuplicateFlags(cReg.AppendFlags(flags))...),
|
|
micro.WrapHandler(auth2ClientReg.WrapHandler()),
|
|
micro.Action(func(c *cli.Context) error {
|
|
if c.Bool("auth2_generate_keys") {
|
|
var (
|
|
aPubKey string
|
|
aPrivKey string
|
|
rPubKey string
|
|
rPrivKey string
|
|
err error
|
|
)
|
|
|
|
// Just generate keys and print them to the commandline
|
|
switch c.String("auth2_generate_format") {
|
|
case "Ed25519":
|
|
aPubKey, aPrivKey, err = generateEd25519PEMKeyPair()
|
|
if err != nil {
|
|
logger.Fatal(err)
|
|
return err
|
|
}
|
|
|
|
rPubKey, rPrivKey, err = generateEd25519PEMKeyPair()
|
|
if err != nil {
|
|
logger.Fatal(err)
|
|
return err
|
|
}
|
|
case "RSA4096":
|
|
aPubKey, aPrivKey, err = generateRSAPEMKeyPair(4096)
|
|
if err != nil {
|
|
logger.Fatal(err)
|
|
return err
|
|
}
|
|
|
|
rPubKey, rPrivKey, err = generateRSAPEMKeyPair(4096)
|
|
if err != nil {
|
|
logger.Fatal(err)
|
|
return err
|
|
}
|
|
case "RSA2048":
|
|
aPubKey, aPrivKey, err = generateRSAPEMKeyPair(2048)
|
|
if err != nil {
|
|
logger.Fatal(err)
|
|
return err
|
|
}
|
|
|
|
rPubKey, rPrivKey, err = generateRSAPEMKeyPair(2048)
|
|
if err != nil {
|
|
logger.Fatal(err)
|
|
return err
|
|
}
|
|
default:
|
|
logger.Fatalf("unknown key format: %s", c.String("auth2_generate_format"))
|
|
return err
|
|
}
|
|
|
|
absPath, err := exec.LookPath(os.Args[0])
|
|
if err != nil {
|
|
// Don't fail here
|
|
absPath = os.Args[0]
|
|
}
|
|
|
|
fmt.Printf("# go.micro.auth %s JWT keys in PEM - generated using '%s %s'\n", c.String("auth2_generate_format"), absPath, strings.Join(os.Args[1:len(os.Args)], " "))
|
|
fmt.Printf("AUTH2_JWT_PRIV_KEY=\"%s\"\n", aPrivKey)
|
|
fmt.Printf("AUTH2_JWT_PUB_KEY=\"%s\"\n", aPubKey)
|
|
fmt.Printf("AUTH2_JWT_REFRESH_PRIV_KEY=\"%s\"\n", rPrivKey)
|
|
fmt.Printf("AUTH2_JWT_REFRESH_PUB_KEY=\"%s\"\n", rPubKey)
|
|
|
|
os.Exit(0)
|
|
}
|
|
|
|
// Start the components
|
|
if err := cReg.Init(c); err != nil {
|
|
logger.Fatal(err)
|
|
return err
|
|
}
|
|
|
|
logger := logruscomponent.MustReg(cReg).Logger()
|
|
|
|
// Check if we got keys
|
|
if c.String("auth2_jwt_pub_key") == "" || c.String("auth2_jwt_priv_key") == "" || c.String("auth2_jwt_refresh_pub_key") == "" || c.String("auth2_jwt_refresh_priv_key") == "" {
|
|
logger.Fatal(ErrorNoKeys)
|
|
return ErrorNoKeys
|
|
}
|
|
|
|
// Check the other handler cli arguments
|
|
if c.Int64("auth2_jwt_access_expiry") < 1 {
|
|
err := errors.New("AUTH2_JWT_ACCESS_EXPIRY must be great than 0")
|
|
logger.Fatal(err)
|
|
return err
|
|
}
|
|
if c.Int64("auth2_jwt_refresh_expiry") < 1 {
|
|
err := errors.New("AUTH2_JWT_REFRESH_EXPIRY must be great than 0")
|
|
logger.Fatal(err)
|
|
return err
|
|
}
|
|
if c.StringSlice("auth2_jwt_audience") == nil {
|
|
err := errors.New("AUTH2_JWT_AUDIENCES must be given")
|
|
logger.Fatal(err)
|
|
return err
|
|
}
|
|
|
|
if err := authHandler.Init(cReg, handler.InitConfig{
|
|
Audiences: c.StringSlice("auth2_jwt_audience"),
|
|
RefreshTokenExpiry: c.Int64("auth2_jwt_refresh_expiry"),
|
|
AccessTokenExpiry: c.Int64("auth2_jwt_access_expiry"),
|
|
AccessTokenPubKey: c.String("auth2_jwt_pub_key"),
|
|
AccessTokenPrivKey: c.String("auth2_jwt_priv_key"),
|
|
RefreshTokenPubKey: c.String("auth2_jwt_refresh_pub_key"),
|
|
RefreshTokenPrivKey: c.String("auth2_jwt_refresh_priv_key"),
|
|
}); err != nil {
|
|
logger.Fatal(err)
|
|
return err
|
|
}
|
|
authpb.RegisterAuthServiceHandler(service.Server(), authHandler)
|
|
|
|
return nil
|
|
}),
|
|
}
|
|
|
|
service.Init(opts...)
|
|
|
|
// Run server
|
|
if err := service.Run(); err != nil {
|
|
logruscomponent.MustReg(cReg).Logger().Fatal(err)
|
|
return
|
|
}
|
|
|
|
// Stop the auth Plugin
|
|
if err := cReg.Stop(); err != nil {
|
|
logger.Fatal(err)
|
|
return
|
|
}
|
|
}
|