mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-06-18 10:06:26 +00:00
Move jwt keys to database
Moved code for it to one place Updated to chi v5
This commit is contained in:
@ -13,7 +13,7 @@ import (
|
||||
"npm/internal/logger"
|
||||
"npm/internal/util"
|
||||
|
||||
"github.com/go-chi/jwtauth"
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
)
|
||||
|
||||
// DecodeAuth decodes an auth header
|
||||
@ -29,7 +29,7 @@ func DecodeAuth() func(http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
tokenAuth := jwtauth.New("RS256", privateKey, publicKey)
|
||||
return jwtauth.Verifier(tokenAuth)
|
||||
return jwtauth.Verify(tokenAuth, jwtauth.TokenFromHeader)
|
||||
}
|
||||
|
||||
// Enforce is a authentication middleware to enforce access from the
|
||||
@ -44,13 +44,14 @@ func Enforce(permission string) func(http.Handler) http.Handler {
|
||||
token, claims, err := jwtauth.FromContext(ctx)
|
||||
|
||||
if err != nil {
|
||||
logger.Debug("EnforceError: %+v", err)
|
||||
h.ResultErrorJSON(w, r, http.StatusUnauthorized, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
userID := uint(claims["uid"].(float64))
|
||||
_, enabled := user.IsEnabled(userID)
|
||||
if token == nil || !token.Valid || !enabled {
|
||||
if token == nil || !enabled {
|
||||
h.ResultErrorJSON(w, r, http.StatusUnauthorized, "Unauthorised", nil)
|
||||
return
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
var methodMap = []string{
|
||||
|
@ -12,8 +12,8 @@ import (
|
||||
"npm/internal/logger"
|
||||
"npm/internal/serverevents"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
chiMiddleware "github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/chi/v5"
|
||||
chiMiddleware "github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
)
|
||||
|
||||
|
@ -26,7 +26,6 @@ func Init(version, commit, sentryDSN *string) {
|
||||
initLogger(*sentryDSN)
|
||||
logger.Info("Build Version: %s (%s)", Version, Commit)
|
||||
createDataFolders()
|
||||
loadKeys()
|
||||
}
|
||||
|
||||
// InitIPRanges will initialise the config for the ipranges command
|
||||
@ -79,11 +78,3 @@ func initLogger(sentryDSN string) {
|
||||
func GetLogLevel() logger.Level {
|
||||
return logLevel
|
||||
}
|
||||
|
||||
func isError(errorClass string, err error) bool {
|
||||
if err != nil {
|
||||
logger.Error(errorClass, err)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -1,111 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"npm/internal/logger"
|
||||
)
|
||||
|
||||
var keysFolder string
|
||||
var publicKeyFile string
|
||||
var privateKeyFile string
|
||||
|
||||
func loadKeys() {
|
||||
// check if keys folder exists in data folder
|
||||
keysFolder = fmt.Sprintf("%s/keys", Configuration.DataFolder)
|
||||
publicKeyFile = fmt.Sprintf("%s/public.key", keysFolder)
|
||||
privateKeyFile = fmt.Sprintf("%s/private.key", keysFolder)
|
||||
|
||||
if _, err := os.Stat(keysFolder); os.IsNotExist(err) {
|
||||
// nolint:errcheck,gosec
|
||||
os.Mkdir(keysFolder, 0700)
|
||||
}
|
||||
|
||||
// check if keys exist on disk
|
||||
_, publicKeyErr := os.Stat(publicKeyFile)
|
||||
_, privateKeyErr := os.Stat(privateKeyFile)
|
||||
|
||||
// generate keys if either one doesn't exist
|
||||
if os.IsNotExist(publicKeyErr) || os.IsNotExist(privateKeyErr) {
|
||||
generateKeys()
|
||||
saveKeys()
|
||||
}
|
||||
|
||||
// Load keys from disk
|
||||
// nolint:gosec
|
||||
publicKeyBytes, publicKeyBytesErr := os.ReadFile(publicKeyFile)
|
||||
// nolint:gosec
|
||||
privateKeyBytes, privateKeyBytesErr := os.ReadFile(privateKeyFile)
|
||||
PublicKey = string(publicKeyBytes)
|
||||
PrivateKey = string(privateKeyBytes)
|
||||
|
||||
if isError("PublicKeyReadError", publicKeyBytesErr) || isError("PrivateKeyReadError", privateKeyBytesErr) || PublicKey == "" || PrivateKey == "" {
|
||||
logger.Warn("There was an error loading keys, proceeding to generate new RSA keys")
|
||||
generateKeys()
|
||||
saveKeys()
|
||||
}
|
||||
}
|
||||
|
||||
func generateKeys() {
|
||||
reader := rand.Reader
|
||||
bitSize := 4096
|
||||
|
||||
key, err := rsa.GenerateKey(reader, bitSize)
|
||||
if isError("RSAGenerateError", err) {
|
||||
return
|
||||
}
|
||||
|
||||
privateKey := &pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(key),
|
||||
}
|
||||
|
||||
privateKeyBuffer := new(bytes.Buffer)
|
||||
err = pem.Encode(privateKeyBuffer, privateKey)
|
||||
if isError("PrivatePEMEncodeError", err) {
|
||||
return
|
||||
}
|
||||
|
||||
asn1Bytes, err2 := asn1.Marshal(key.PublicKey)
|
||||
if isError("RSAMarshalError", err2) {
|
||||
return
|
||||
}
|
||||
|
||||
publicKey := &pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: asn1Bytes,
|
||||
}
|
||||
|
||||
publicKeyBuffer := new(bytes.Buffer)
|
||||
err = pem.Encode(publicKeyBuffer, publicKey)
|
||||
if isError("PublicPEMEncodeError", err) {
|
||||
return
|
||||
}
|
||||
|
||||
PublicKey = publicKeyBuffer.String()
|
||||
PrivateKey = privateKeyBuffer.String()
|
||||
logger.Info("Generated new RSA keys")
|
||||
}
|
||||
|
||||
func saveKeys() {
|
||||
err := os.WriteFile(publicKeyFile, []byte(PublicKey), 0600)
|
||||
if err != nil {
|
||||
logger.Error("PublicKeyWriteError", err)
|
||||
} else {
|
||||
logger.Info("Saved Public Key: %s", publicKeyFile)
|
||||
}
|
||||
|
||||
err = os.WriteFile(privateKeyFile, []byte(PrivateKey), 0600)
|
||||
if err != nil {
|
||||
logger.Error("PrivateKeyWriteError", err)
|
||||
} else {
|
||||
logger.Info("Saved Private Key: %s", privateKeyFile)
|
||||
}
|
||||
}
|
@ -16,12 +16,6 @@ var IsSetup bool
|
||||
// ErrorReporting defines whether we will send errors to Sentry
|
||||
var ErrorReporting bool
|
||||
|
||||
// PublicKey is the public key
|
||||
var PublicKey string
|
||||
|
||||
// PrivateKey is the private key
|
||||
var PrivateKey string
|
||||
|
||||
var logLevel logger.Level
|
||||
|
||||
type log struct {
|
||||
|
@ -1,11 +1,14 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/pem"
|
||||
|
||||
"npm/internal/config"
|
||||
"npm/internal/logger"
|
||||
|
||||
"github.com/rotisserie/eris"
|
||||
)
|
||||
@ -21,12 +24,12 @@ func GetPrivateKey() (*rsa.PrivateKey, error) {
|
||||
if privateKey == nil {
|
||||
var blankKey *rsa.PrivateKey
|
||||
|
||||
if config.PrivateKey == "" {
|
||||
if currentKeys.PrivateKey == "" {
|
||||
return blankKey, eris.New("Could not get Private Key from configuration")
|
||||
}
|
||||
|
||||
var err error
|
||||
privateKey, err = LoadPemPrivateKey(config.PrivateKey)
|
||||
privateKey, err = LoadPemPrivateKey(currentKeys.PrivateKey)
|
||||
if err != nil {
|
||||
return blankKey, err
|
||||
}
|
||||
@ -48,12 +51,12 @@ func GetPublicKey() (*rsa.PublicKey, error) {
|
||||
if publicKey == nil {
|
||||
var blankKey *rsa.PublicKey
|
||||
|
||||
if config.PublicKey == "" {
|
||||
return blankKey, eris.New("Could not get Public Key filename, check environment variables")
|
||||
if currentKeys.PublicKey == "" {
|
||||
return blankKey, eris.New("Could not get Public Key from configuration")
|
||||
}
|
||||
|
||||
var err error
|
||||
publicKey, err = LoadPemPublicKey(config.PublicKey)
|
||||
publicKey, err = LoadPemPublicKey(currentKeys.PublicKey)
|
||||
if err != nil {
|
||||
return blankKey, err
|
||||
}
|
||||
@ -85,3 +88,48 @@ func LoadPemPublicKey(content string) (*rsa.PublicKey, error) {
|
||||
|
||||
return publicKeyFileImported, nil
|
||||
}
|
||||
|
||||
func generateKeys() (KeysModel, error) {
|
||||
m := KeysModel{}
|
||||
reader := rand.Reader
|
||||
bitSize := 4096
|
||||
|
||||
key, err := rsa.GenerateKey(reader, bitSize)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
|
||||
privateKey := &pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(key),
|
||||
}
|
||||
|
||||
privateKeyBuffer := new(bytes.Buffer)
|
||||
err = pem.Encode(privateKeyBuffer, privateKey)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
|
||||
asn1Bytes, err := asn1.Marshal(key.PublicKey)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
|
||||
publicKey := &pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: asn1Bytes,
|
||||
}
|
||||
|
||||
publicKeyBuffer := new(bytes.Buffer)
|
||||
err = pem.Encode(publicKeyBuffer, publicKey)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
|
||||
m.PublicKey = publicKeyBuffer.String()
|
||||
m.PrivateKey = privateKeyBuffer.String()
|
||||
|
||||
logger.Info("Generated new RSA keys")
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
54
backend/internal/jwt/keys_db.go
Normal file
54
backend/internal/jwt/keys_db.go
Normal file
@ -0,0 +1,54 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"npm/internal/database"
|
||||
"npm/internal/entity"
|
||||
"npm/internal/logger"
|
||||
)
|
||||
|
||||
var currentKeys KeysModel
|
||||
|
||||
// KeysModel is the model
|
||||
type KeysModel struct {
|
||||
entity.ModelBase
|
||||
PublicKey string `gorm:"column:public_key"`
|
||||
PrivateKey string `gorm:"column:private_key"`
|
||||
}
|
||||
|
||||
// TableName overrides the table name used by gorm
|
||||
func (KeysModel) TableName() string {
|
||||
return "keys"
|
||||
}
|
||||
|
||||
// LoadByID will load from an ID
|
||||
func (m *KeysModel) LoadLatest() error {
|
||||
db := database.GetDB()
|
||||
result := db.Order("created_at DESC").First(&m)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// Save will save this model to the DB
|
||||
func (m *KeysModel) Save() error {
|
||||
db := database.GetDB()
|
||||
result := db.Save(m)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// LoadKeys will load from the database, or generate and save new ones
|
||||
func LoadKeys() error {
|
||||
// Try to find in db
|
||||
if err := currentKeys.LoadLatest(); err != nil {
|
||||
// Keys probably don't exist, so we need to generate some
|
||||
if currentKeys, err = generateKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// and save them
|
||||
if err = currentKeys.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
logger.Debug("private: %s", currentKeys.PrivateKey)
|
||||
logger.Debug("public: %s", currentKeys.PublicKey)
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user