安全设置增加“检查客户端指纹"和"检查客户端区域"选项

This commit is contained in:
刘祥超
2023-04-19 18:25:10 +08:00
parent a31f9ed9c5
commit c9fb3153eb
9 changed files with 129 additions and 11 deletions

View File

@@ -29,7 +29,7 @@ func LoadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
return nil, err return nil, err
} }
v := reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.SecurityConfig) var v = reflect.Indirect(reflect.ValueOf(config)).Interface().(systemconfigs.SecurityConfig)
return &v, nil return &v, nil
} }
@@ -83,7 +83,12 @@ func loadSecurityConfig() (*systemconfigs.SecurityConfig, error) {
return sharedSecurityConfig, nil return sharedSecurityConfig, nil
} }
config := &systemconfigs.SecurityConfig{} var config = &systemconfigs.SecurityConfig{
Frame: FrameSameOrigin,
AllowLocal: true,
CheckClientFingerprint: false,
CheckClientRegion: true,
}
err = json.Unmarshal(resp.ValueJSON, config) err = json.Unmarshal(resp.ValueJSON, config)
if err != nil { if err != nil {
logs.Println("[SECURITY_MANAGER]" + err.Error()) logs.Println("[SECURITY_MANAGER]" + err.Error())
@@ -102,5 +107,7 @@ func defaultSecurityConfig() *systemconfigs.SecurityConfig {
return &systemconfigs.SecurityConfig{ return &systemconfigs.SecurityConfig{
Frame: FrameSameOrigin, Frame: FrameSameOrigin,
AllowLocal: true, AllowLocal: true,
CheckClientFingerprint: false,
CheckClientRegion: true,
} }
} }

View File

@@ -85,6 +85,9 @@ func (this *AdminNode) Run() {
// 启动API节点 // 启动API节点
this.startAPINode() this.startAPINode()
// 启动IP库
this.startIPLibrary()
// 启动Web服务 // 启动Web服务
sessionManager, err := NewSessionManager() sessionManager, err := NewSessionManager()
if err != nil { if err != nil {

View File

@@ -0,0 +1,18 @@
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package nodes
import (
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/iwind/TeaGo/logs"
)
// 启动IP库
func (this *AdminNode) startIPLibrary() {
logs.Println("NODE", "initializing ip library ...")
err := iplibrary.InitDefault()
if err != nil {
logs.Println("NODE", "initialize ip library failed: "+err.Error())
}
}

View File

@@ -2,6 +2,9 @@ package rpc
import ( import (
"github.com/TeaOSLab/EdgeAdmin/internal/configs" "github.com/TeaOSLab/EdgeAdmin/internal/configs"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"strings"
"sync" "sync"
) )
@@ -28,3 +31,23 @@ func SharedRPC() (*RPCClient, error) {
sharedRPC = client sharedRPC = client
return sharedRPC, nil return sharedRPC, nil
} }
// IsConnError 是否为连接错误
func IsConnError(err error) bool {
if err == nil {
return false
}
// 检查是否为连接错误
statusErr, ok := status.FromError(err)
if ok {
var errorCode = statusErr.Code()
return errorCode == codes.Unavailable || errorCode == codes.Canceled
}
if strings.Contains(err.Error(), "code = Canceled") {
return true
}
return false
}

View File

@@ -4,16 +4,41 @@ package loginutils
import ( import (
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const" teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/iwind/TeaGo/actions" "github.com/iwind/TeaGo/actions"
stringutil "github.com/iwind/TeaGo/utils/string" stringutil "github.com/iwind/TeaGo/utils/string"
"net"
"net/http" "net/http"
) )
// CalculateClientFingerprint 计算客户端指纹 // CalculateClientFingerprint 计算客户端指纹
func CalculateClientFingerprint(action *actions.ActionObject) string { func CalculateClientFingerprint(action *actions.ActionObject) string {
return stringutil.Md5(action.RequestRemoteIP() + "@" + action.Request.UserAgent()) return stringutil.Md5(RemoteIP(action) + "@" + action.Request.UserAgent())
} }
// RemoteIP 获取客户端IP
// TODO 将来增加是否使用代理设置即从X-Real-IP中获取IP
func RemoteIP(action *actions.ActionObject) string {
ip, _, _ := net.SplitHostPort(action.Request.RemoteAddr)
return ip
}
// LookupIPRegion 查找登录区域
func LookupIPRegion(ip string) string {
if len(ip) == 0 {
return ""
}
var result = iplibrary.LookupIP(ip)
if result != nil && result.IsOk() {
// 这里不需要网络运营商信息
return result.CountryName() + "@" + result.ProvinceName() + "@" + result.CityName() + "@" + result.TownName()
}
return ""
}
// SetCookie 设置Cookie
func SetCookie(action *actions.ActionObject, remember bool) { func SetCookie(action *actions.ActionObject, remember bool) {
if remember { if remember {
var cookie = &http.Cookie{ var cookie = &http.Cookie{
@@ -44,6 +69,7 @@ func SetCookie(action *actions.ActionObject, remember bool) {
} }
} }
// UnsetCookie 重置Cookie
func UnsetCookie(action *actions.ActionObject) { func UnsetCookie(action *actions.ActionObject) {
cookie := &http.Cookie{ cookie := &http.Cookie{
Name: teaconst.CookieSID, Name: teaconst.CookieSID,

View File

@@ -79,6 +79,9 @@ func (this *IndexAction) RunPost(params struct {
DenySearchEngines bool DenySearchEngines bool
DenySpiders bool DenySpiders bool
CheckClientFingerprint bool
CheckClientRegion bool
DomainsJSON []byte DomainsJSON []byte
Must *actions.Must Must *actions.Must
@@ -150,6 +153,10 @@ func (this *IndexAction) RunPost(params struct {
// 允许记住登录 // 允许记住登录
config.AllowRememberLogin = params.AllowRememberLogin config.AllowRememberLogin = params.AllowRememberLogin
// Cookie检查
config.CheckClientFingerprint = params.CheckClientFingerprint
config.CheckClientRegion = params.CheckClientRegion
err = configloaders.UpdateSecurityConfig(config) err = configloaders.UpdateSecurityConfig(config)
if err != nil { if err != nil {
this.ErrorPage(err) this.ErrorPage(err)

View File

@@ -175,6 +175,7 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
} }
// 检查指纹 // 检查指纹
if securityConfig != nil && securityConfig.CheckClientFingerprint {
var clientFingerprint = session.GetString("@fingerprint") var clientFingerprint = session.GetString("@fingerprint")
if len(clientFingerprint) > 0 && clientFingerprint != loginutils.CalculateClientFingerprint(action) { if len(clientFingerprint) > 0 && clientFingerprint != loginutils.CalculateClientFingerprint(action) {
loginutils.UnsetCookie(action) loginutils.UnsetCookie(action)
@@ -183,6 +184,24 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
this.login(action) this.login(action)
return false return false
} }
}
// 检查区域
if securityConfig != nil && securityConfig.CheckClientRegion {
var oldClientIP = session.GetString("@ip")
var currentClientIP = loginutils.RemoteIP(action)
if len(oldClientIP) > 0 && len(currentClientIP) > 0 && oldClientIP != currentClientIP {
var oldRegion = loginutils.LookupIPRegion(oldClientIP)
var newRegion = loginutils.LookupIPRegion(currentClientIP)
if newRegion != oldRegion {
loginutils.UnsetCookie(action)
session.Delete()
this.login(action)
return false
}
}
}
// 检查用户是否存在 // 检查用户是否存在
if !configloaders.CheckAdmin(adminId) { if !configloaders.CheckAdmin(adminId) {

View File

@@ -58,6 +58,7 @@ func (this *UserShouldAuth) StoreAdmin(adminId int64, remember bool) {
var session = this.action.Session() var session = this.action.Session()
session.Write("adminId", numberutils.FormatInt64(adminId)) session.Write("adminId", numberutils.FormatInt64(adminId))
session.Write("@fingerprint", loginutils.CalculateClientFingerprint(this.action)) session.Write("@fingerprint", loginutils.CalculateClientFingerprint(this.action))
session.Write("@ip", loginutils.RemoteIP(this.action))
} }
func (this *UserShouldAuth) IsUser() bool { func (this *UserShouldAuth) IsUser() bool {

View File

@@ -77,6 +77,20 @@
<p class="comment">只允许通过这些域名或者IP作为主机地址访问当前管理系统不填表示没有限制。</p> <p class="comment">只允许通过这些域名或者IP作为主机地址访问当前管理系统不填表示没有限制。</p>
</td> </td>
</tr> </tr>
<tr>
<td>检查客户端指纹</td>
<td>
<checkbox name="checkClientFingerprint" v-model="config.checkClientFingerprint"></checkbox>
<p class="comment">选中后,表示每次访问时都检查客户端相关信息是否跟登录时一致。</p>
</td>
</tr>
<tr>
<td>检查客户端区域</td>
<td>
<checkbox name="checkClientRegion" v-model="config.checkClientRegion"></checkbox>
<p class="comment">选中后,表示每次访问时都检查客户端所在地理区域是否和登录时一致。</p>
</td>
</tr>
</tbody> </tbody>
</table> </table>
<submit-btn></submit-btn> <submit-btn></submit-btn>