mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 10:25:52 +02:00
Merge branch 'dev' into refactor/otp
This commit is contained in:
commit
121287df21
40 changed files with 1084 additions and 326 deletions
102
internal/crypto/crypto.go
Normal file
102
internal/crypto/crypto.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/cache"
|
||||
"github.com/uozi-tech/cosy/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
CacheKey = "crypto"
|
||||
timeout = 10 * time.Minute
|
||||
)
|
||||
|
||||
type Params struct {
|
||||
PrivateKey string `json:"-"`
|
||||
PublicKey string `json:"public_key"`
|
||||
}
|
||||
|
||||
// GenerateRSAKeyPair generates a new RSA key pair
|
||||
func GenerateRSAKeyPair() (privateKeyPEM, publicKeyPEM []byte, err error) {
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
privateKeyPEM = pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
})
|
||||
publicKeyPEM = pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PUBLIC KEY",
|
||||
Bytes: x509.MarshalPKCS1PublicKey(&privateKey.PublicKey),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// GetCryptoParams registers a new key pair in the cache if it doesn't exist
|
||||
// otherwise, it returns the existing nonce and public key
|
||||
func GetCryptoParams() (params *Params, err error) {
|
||||
// Check if the key pair exists in then cache
|
||||
if value, ok := cache.Get(CacheKey); ok {
|
||||
return value.(*Params), nil
|
||||
}
|
||||
// Generate a nonce = hash(publicKey)
|
||||
privateKeyPEM, publicKeyPEM, err := GenerateRSAKeyPair()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = &Params{
|
||||
PrivateKey: string(privateKeyPEM),
|
||||
PublicKey: string(publicKeyPEM),
|
||||
}
|
||||
cache.Set(CacheKey, params, timeout)
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt decrypts the data with the private key (nonce, paramEncrypted)
|
||||
func Decrypt(paramEncrypted string) (data map[string]interface{}, err error) {
|
||||
// Get crypto params from cache
|
||||
value, ok := cache.Get(CacheKey)
|
||||
if !ok {
|
||||
return nil, ErrTimeout
|
||||
}
|
||||
|
||||
params := value.(*Params)
|
||||
block, _ := pem.Decode([]byte(params.PrivateKey))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("failed to decode PEM block containing private key")
|
||||
}
|
||||
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to parse private key: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paramEncryptedDecoded, err := base64.StdEncoding.DecodeString(paramEncrypted)
|
||||
if err != nil {
|
||||
logger.Errorf("base64 decode error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decrypted, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, paramEncryptedDecoded)
|
||||
if err != nil {
|
||||
logger.Errorf("decryption failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(decrypted, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -6,4 +6,5 @@ var (
|
|||
e = cosy.NewErrorScope("crypto")
|
||||
ErrPlainTextEmpty = e.New(50001, "plain text is empty")
|
||||
ErrCipherTextTooShort = e.New(50002, "cipher text is too short")
|
||||
ErrTimeout = e.New(40401, "request timeout")
|
||||
)
|
||||
|
|
|
@ -3,6 +3,10 @@ package kernel
|
|||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"mime"
|
||||
"path"
|
||||
"runtime"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/analytic"
|
||||
"github.com/0xJacky/Nginx-UI/internal/cache"
|
||||
"github.com/0xJacky/Nginx-UI/internal/cert"
|
||||
|
@ -17,10 +21,8 @@ import (
|
|||
"github.com/uozi-tech/cosy"
|
||||
sqlite "github.com/uozi-tech/cosy-driver-sqlite"
|
||||
"github.com/uozi-tech/cosy/logger"
|
||||
cModel "github.com/uozi-tech/cosy/model"
|
||||
cSettings "github.com/uozi-tech/cosy/settings"
|
||||
"mime"
|
||||
"path"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func Boot() {
|
||||
|
@ -73,12 +75,13 @@ func recovery() {
|
|||
}
|
||||
|
||||
func InitDatabase() {
|
||||
cModel.ResolvedModels()
|
||||
// Skip install
|
||||
if settings.NodeSettings.SkipInstallation {
|
||||
skipInstall()
|
||||
}
|
||||
|
||||
if "" != cSettings.AppSettings.JwtSecret {
|
||||
if cSettings.AppSettings.JwtSecret != "" {
|
||||
db := cosy.InitDB(sqlite.Open(path.Dir(cSettings.ConfPath), settings.DatabaseSettings))
|
||||
model.Use(db)
|
||||
query.Init(db)
|
||||
|
@ -88,7 +91,7 @@ func InitDatabase() {
|
|||
}
|
||||
|
||||
func InitNodeSecret() {
|
||||
if "" == settings.NodeSettings.Secret {
|
||||
if settings.NodeSettings.Secret == "" {
|
||||
logger.Info("Secret is empty, generating...")
|
||||
uuidStr := uuid.New().String()
|
||||
settings.NodeSettings.Secret = uuidStr
|
||||
|
@ -102,7 +105,7 @@ func InitNodeSecret() {
|
|||
}
|
||||
|
||||
func InitCryptoSecret() {
|
||||
if "" == settings.CryptoSettings.Secret {
|
||||
if settings.CryptoSettings.Secret == "" {
|
||||
logger.Info("Secret is empty, generating...")
|
||||
|
||||
key := make([]byte, 32)
|
||||
|
|
46
internal/middleware/encrypted_params.go
Normal file
46
internal/middleware/encrypted_params.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/crypto"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/uozi-tech/cosy"
|
||||
)
|
||||
|
||||
var (
|
||||
e = cosy.NewErrorScope("middleware")
|
||||
ErrInvalidRequestFormat = e.New(40000, "invalid request format")
|
||||
ErrDecryptionFailed = e.New(40001, "decryption failed")
|
||||
)
|
||||
|
||||
func EncryptedParams() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// read the encrypted payload
|
||||
var encryptedReq struct {
|
||||
EncryptedParams string `json:"encrypted_params"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&encryptedReq); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, ErrInvalidRequestFormat)
|
||||
return
|
||||
}
|
||||
|
||||
// decrypt the parameters
|
||||
decryptedData, err := crypto.Decrypt(encryptedReq.EncryptedParams)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, ErrDecryptionFailed)
|
||||
return
|
||||
}
|
||||
|
||||
// replace request body with decrypted data
|
||||
newBody, _ := json.Marshal(decryptedData)
|
||||
c.Request.Body = io.NopCloser(bytes.NewReader(newBody))
|
||||
c.Request.ContentLength = int64(len(newBody))
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -53,16 +53,16 @@ func AuthRequired() gin.HandlerFunc {
|
|||
c.Set("ProxyNodeID", xNodeID)
|
||||
}
|
||||
|
||||
if token := c.GetHeader("X-Node-Secret"); token != "" && token == settings.NodeSettings.Secret {
|
||||
c.Set("Secret", token)
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
token := getToken(c)
|
||||
if token == "" {
|
||||
if token = c.GetHeader("X-Node-Secret"); token != "" && token == settings.NodeSettings.Secret {
|
||||
c.Set("Secret", token)
|
||||
c.Next()
|
||||
return
|
||||
} else {
|
||||
abortWithAuthFailure()
|
||||
return
|
||||
}
|
||||
abortWithAuthFailure()
|
||||
return
|
||||
}
|
||||
|
||||
u, ok := user.GetTokenUser(token)
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/query"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pretty66/websocketproxy"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/uozi-tech/cosy/logger"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func ProxyWs() gin.HandlerFunc {
|
||||
|
@ -26,7 +27,6 @@ func ProxyWs() gin.HandlerFunc {
|
|||
|
||||
env := query.Environment
|
||||
environment, err := env.Where(env.ID.Eq(id)).First()
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
|
@ -43,6 +43,10 @@ func ProxyWs() gin.HandlerFunc {
|
|||
|
||||
wp, err := websocketproxy.NewProxy(decodedUri, func(r *http.Request) error {
|
||||
r.Header.Set("X-Node-Secret", environment.Token)
|
||||
r.Header.Del("X-Node-ID")
|
||||
queryValues := r.URL.Query()
|
||||
queryValues.Del("x_node_id")
|
||||
r.URL.RawQuery = queryValues.Encode()
|
||||
return nil
|
||||
})
|
||||
|
||||
|
|
|
@ -741,6 +741,11 @@
|
|||
"https://nginx.org/en/docs/http/ngx_http_grpc_module.html#grpc_ssl_certificate"
|
||||
]
|
||||
},
|
||||
"grpc_ssl_certificate_cache": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/http/ngx_http_grpc_module.html#grpc_ssl_certificate_cache"
|
||||
]
|
||||
},
|
||||
"grpc_ssl_certificate_key": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/http/ngx_http_grpc_module.html#grpc_ssl_certificate_key"
|
||||
|
@ -1241,6 +1246,11 @@
|
|||
"https://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_disable"
|
||||
]
|
||||
},
|
||||
"keepalive_min_timeout": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_min_timeout"
|
||||
]
|
||||
},
|
||||
"keepalive_requests": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_requests",
|
||||
|
@ -2100,6 +2110,12 @@
|
|||
"https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_ssl_certificate"
|
||||
]
|
||||
},
|
||||
"proxy_ssl_certificate_cache": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate_cache",
|
||||
"https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_ssl_certificate_cache"
|
||||
]
|
||||
},
|
||||
"proxy_ssl_certificate_key": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate_key",
|
||||
|
@ -2750,6 +2766,12 @@
|
|||
"https://nginx.org/en/docs/stream/ngx_stream_ssl_module.html#ssl_certificate"
|
||||
]
|
||||
},
|
||||
"ssl_certificate_cache": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_cache",
|
||||
"https://nginx.org/en/docs/stream/ngx_stream_ssl_module.html#ssl_certificate_cache"
|
||||
]
|
||||
},
|
||||
"ssl_certificate_key": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate_key",
|
||||
|
@ -2821,6 +2843,11 @@
|
|||
"https://nginx.org/en/docs/stream/ngx_stream_ssl_module.html#ssl_key_log"
|
||||
]
|
||||
},
|
||||
"ssl_object_cache_inheritable": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/ngx_core_module.html#ssl_object_cache_inheritable"
|
||||
]
|
||||
},
|
||||
"ssl_ocsp": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ocsp",
|
||||
|
@ -3374,6 +3401,11 @@
|
|||
"https://nginx.org/en/docs/http/ngx_http_uwsgi_module.html#uwsgi_ssl_certificate"
|
||||
]
|
||||
},
|
||||
"uwsgi_ssl_certificate_cache": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/http/ngx_http_uwsgi_module.html#uwsgi_ssl_certificate_cache"
|
||||
]
|
||||
},
|
||||
"uwsgi_ssl_certificate_key": {
|
||||
"links": [
|
||||
"https://nginx.org/en/docs/http/ngx_http_uwsgi_module.html#uwsgi_ssl_certificate_key"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue