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.
auth2/cmd/microauth2sqld/main.go

354 lines
9.6 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/ibun"
"jochum.dev/jo-micro/auth2/internal/ilogger"
"jochum.dev/jo-micro/auth2/internal/proto/authpb"
"jochum.dev/jo-micro/router"
_ "jochum.dev/jo-micro/auth2/plugins/client/jwt"
"jochum.dev/jo-micro/auth2/plugins/verifier/endpointroles"
)
var (
ErrorNoKeys = errors.New("config MICRO_AUTH2_JWT_*_KEY or MICRO_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() {
srv := micro.NewService()
auth2ClientReg := auth2.ClientAuthRegistry()
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{"MICRO_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{"MICRO_AUTH2_JWT_PUB_KEY"},
},
&cli.StringFlag{
Name: "auth2_jwt_priv_key",
Usage: "Private access key PEM base64 encoded",
EnvVars: []string{"MICRO_AUTH2_JWT_PRIV_KEY"},
},
&cli.StringFlag{
Name: "auth2_jwt_refresh_pub_key",
Usage: "Public refresh key PEM base64 encoded",
EnvVars: []string{"MICRO_AUTH2_JWT_REFRESH_PUB_KEY"},
},
&cli.StringFlag{
Name: "auth2_jwt_refresh_priv_key",
Usage: "Private refresh key PEM base64 encoded",
EnvVars: []string{"MICRO_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{"MICRO_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{"MICRO_AUTH2_JWT_ACCESS_EXPIRY"},
Value: 900,
},
&cli.StringSliceFlag{
Name: "auth2_jwt_audience",
Usage: "Add and expect this JWT audience",
EnvVars: []string{"MICRO_AUTH2_JWT_AUDIENCES"},
},
}
flags = ibun.AppendFlags(ilogger.AppendFlags(auth2ClientReg.AppendFlags(flags)))
authHandler := handler.NewHandler()
opts := []micro.Option{
micro.Name(config.Name),
micro.Version(config.Version),
micro.Flags(flags...),
micro.WrapHandler(auth2ClientReg.Plugin().Wrapper()),
micro.Action(func(c *cli.Context) error {
// Start the logger
if err := ilogger.Start(c); err != nil {
logger.Fatal(err)
return err
}
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 {
ilogger.Logrus().Fatal(err)
}
rPubKey, rPrivKey, err = generateEd25519PEMKeyPair()
if err != nil {
ilogger.Logrus().Fatal(err)
}
case "RSA4096":
aPubKey, aPrivKey, err = generateRSAPEMKeyPair(4096)
if err != nil {
ilogger.Logrus().Fatal(err)
}
rPubKey, rPrivKey, err = generateRSAPEMKeyPair(4096)
if err != nil {
ilogger.Logrus().Fatal(err)
}
case "RSA2048":
aPubKey, aPrivKey, err = generateRSAPEMKeyPair(2048)
if err != nil {
ilogger.Logrus().Fatal(err)
}
rPubKey, rPrivKey, err = generateRSAPEMKeyPair(2048)
if err != nil {
ilogger.Logrus().Fatal(err)
}
default:
ilogger.Logrus().Fatalf("unknown key format: %s", c.String("auth2_generate_format"))
}
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("MICRO_AUTH2_JWT_PRIV_KEY=\"%s\"\n", aPrivKey)
fmt.Printf("MICRO_AUTH2_JWT_PUB_KEY=\"%s\"\n", aPubKey)
fmt.Printf("MICRO_AUTH2_JWT_REFRESH_PRIV_KEY=\"%s\"\n", rPrivKey)
fmt.Printf("MICRO_AUTH2_JWT_REFRESH_PUB_KEY=\"%s\"\n", rPubKey)
os.Exit(0)
}
if err := auth2ClientReg.Init(c, srv); err != nil {
ilogger.Logrus().Fatal(err)
}
authVerifier := endpointroles.NewVerifier()
authVerifier.AddRules(
endpointroles.RouterRule,
endpointroles.NewRule(
endpointroles.Endpoint(authpb.AuthService.Delete),
endpointroles.RolesAllow(auth2.RolesServiceAndAdmin),
),
endpointroles.NewRule(
endpointroles.Endpoint(authpb.AuthService.Detail),
endpointroles.RolesAllow(auth2.RolesServiceAndUsersAndAdmin),
),
endpointroles.NewRule(
endpointroles.Endpoint(authpb.AuthService.Inspect),
endpointroles.RolesAllow(auth2.RolesServiceAndUsersAndAdmin),
),
endpointroles.NewRule(
endpointroles.Endpoint(authpb.AuthService.List),
endpointroles.RolesAllow(auth2.RolesServiceAndAdmin),
),
endpointroles.NewRule(
endpointroles.Endpoint(authpb.AuthService.Login),
endpointroles.RolesAllow(auth2.RolesAllAndAnon),
),
endpointroles.NewRule(
endpointroles.Endpoint(authpb.AuthService.Refresh),
endpointroles.RolesAllow(auth2.RolesUsersAndAdmin),
),
endpointroles.NewRule(
endpointroles.Endpoint(authpb.AuthService.Register),
endpointroles.RolesAllow(auth2.RolesAllAndAnon),
),
endpointroles.NewRule(
endpointroles.Endpoint(authpb.AuthService.UpdateRoles),
endpointroles.RolesAllow(auth2.RolesAdmin),
),
)
auth2ClientReg.Plugin().SetVerifier(authVerifier)
// Connect to the database
if err := ibun.Start(c); err != nil {
ilogger.Logrus().Fatal(err)
}
// 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") == "" {
ilogger.Logrus().Fatal(ErrorNoKeys)
}
// Check the other handler cli arguments
if c.Int64("auth2_jwt_access_expiry") < 1 {
ilogger.Logrus().Fatal(errors.New("MICRO_AUTH2_JWT_ACCESS_EXPIRY must be great than 0"))
}
if c.Int64("auth2_jwt_refresh_expiry") < 1 {
ilogger.Logrus().Fatal(errors.New("MICRO_AUTH2_JWT_REFRESH_EXPIRY must be great than 0"))
}
if c.StringSlice("auth2_jwt_audience") == nil {
ilogger.Logrus().Fatal(errors.New("MICRO_AUTH2_JWT_AUDIENCES must be given"))
}
if err := authHandler.Init(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 {
ilogger.Logrus().Fatal(err)
}
authpb.RegisterAuthServiceHandler(srv.Server(), authHandler)
// Register with https://jochum.dev/jo-micro/router
r := router.NewHandler(
c.String("auth2_sqld_router_basepath"),
router.NewRoute(
router.Method(router.MethodGet),
router.Path("/routes"),
router.Endpoint("routes"),
),
)
r.RegisterWithServer(srv.Server())
return nil
}),
}
srv.Init(opts...)
// Run server
if err := srv.Run(); err != nil {
ilogger.Logrus().Fatal(err)
}
// Disconnect from the database
if err := ibun.Stop(); err != nil {
ilogger.Logrus().Fatal(err)
}
// Stop the auth Plugin
if err := auth2ClientReg.Stop(); err != nil {
ilogger.Logrus().Fatal(err)
}
// Stop the logger
if err := ilogger.Stop(); err != nil {
logger.Fatal(err)
}
}