Skip to content

Commit 2cc4d56

Browse files
committed
optimize offline detection
1 parent a158962 commit 2cc4d56

File tree

16 files changed

+168
-133
lines changed

16 files changed

+168
-133
lines changed

README.md

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# [Janusec Application Gateway / JANUSEC应用网关](https://www.janusec.com/)   [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Protect%20web%20applications%20from%20network%20attacks%20with%20open%20source%20Janusec%20Application%20Gateway&url=https://github.com/Janusec/janusec&via=janusec&hashtags=waf,web,application,firewall,gateway)
1+
# Janusec Application Gateway / JANUSEC应用网关
22

33
[![Build Status](https://travis-ci.org/Janusec/janusec.svg?branch=master)](https://travis-ci.org/Janusec/janusec)
44

@@ -52,21 +52,20 @@
5252

5353
## 产品网站
5454

55-
https://doc.janusec.com/cn/
55+
https://janusec.github.io/cn/
56+
5657

5758
## 需求
5859

59-
* PostgreSQL 10/11/12+ (开发环境,及生产环境主节点需要)
60-
* Debian 9/10+, CentOS/RHEL 7/8+, 首选Debian 10+
60+
* PostgreSQL 10/11/12/13/14+ (开发环境,及生产环境主节点需要)
61+
* Debian 9/10/11+, CentOS/RHEL 7/8+, 首选Debian 10+
6162
* systemd
6263
* nftables
6364
* Golang 1.15+ (仅开发环境需要)
6465

6566
## 部署快速指引
6667

67-
详细文档可在这里获取: [Janusec应用网关快速入门](https://doc.janusec.com/cn/quick-start/)
68-
69-
如希望快速体验,可尝试使用 [Docker镜像](https://www.janusec.com/articles/opensource/1615470598.html)
68+
详细文档可在这里获取: [Janusec应用网关快速入门](https://janusec.github.io/cn/quick-start/)
7069

7170
## 开发快速指引
7271

@@ -103,7 +102,7 @@ Janusec将自动加密数据库口令
103102
只使用主节点时,任意应用域名均可用于访问管理入口。
104103
如果使用了副本节点,应为主节点申请一个单独的域名。
105104

106-
[Janusec应用网关配置](https://doc.janusec.com/cn/quick-start/)
105+
[Janusec应用网关配置](https://janusec.github.io/cn/quick-start/)
107106

108107
## 发布
109108

@@ -119,15 +118,15 @@ Janusec将自动加密数据库口令
119118

120119
Web化管理所需的文件在 `./static/janusec-admin/` 目录, 源码在 [Janusec-Admin Github](https://github.com/Janusec/janusec-admin) ,前端源码使用Angular 9.
121120

122-
## 许可证
121+
## 多许可证
123122

124-
Janusec应用网关源文件使用GNU [AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html)授权.
123+
JANUSEC应用网关开源版本的源文件使用GNU [AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html)授权.
124+
专业增强特性版本闭源发布,增强特性包括:GSLB、Cookie合规(应用无需修改)等。
125125

126126
## 支持
127127

128-
* 产品网站 [https://doc.janusec.com/cn/](https://doc.janusec.com/cn/)
129-
* 官方网站: [https://www.janusec.com/](https://www.janusec.com/)
130-
* Email: `support#janusec.com`
128+
* 产品网站 [https://janusec.github.io/cn/](https://janusec.github.io/cn/)
129+
* Email: `support`**@**`janusec.com`
131130
* QQ群: 776900157
132131

133132

@@ -185,17 +184,15 @@ https://janusec.github.io/
185184

186185
## Requirements
187186

188-
* PostgreSQL 10/11/12+ (Required by Development and Primary Node of Deployment)
189-
* Debian 9/10+, CentOS/RHEL 7/8+, Debian 10+ is preferred
187+
* PostgreSQL 10/11/12/13/14+ (Required by Development and Primary Node of Deployment)
188+
* Debian 9/10/11+, CentOS/RHEL 7/8+, Debian 10+ is preferred
190189
* systemd
191190
* nftables
192191
* Golang 1.15+ (Required by Development Only)
193192

194193
## Quick Start for Deployment
195194

196-
Detailed documentation is available at: [Janusec Application Gateway Quick Start](https://janusec.github.io/documentation/quick-start/).
197-
198-
You can also try it with [Docker Image](https://www.janusec.com/articles/opensource/1615470598.html)
195+
Detailed documentation is available at: [Janusec Application Gateway Quick Start](https://janusec.github.io/documentation/quick-start/).
199196

200197
## Quick Start for Developer
201198

@@ -247,13 +244,14 @@ The release package is under `./dist` .
247244

248245
Release directory is `./static/janusec-admin/` , and source code is available at [Janusec-Admin Github](https://github.com/Janusec/janusec-admin) with Angular 9.
249246

250-
## LICENSE
247+
## Multiple LICENSES
248+
249+
The open source files are made available under the terms of the GNU Affero General Public License ([GNU AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html)).
251250

252-
Janusec Application Gateway source files are made available under the terms of the GNU Affero General Public License ([GNU AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html)).
251+
The professional enhanced version is released in closed source, and the enhanced features including GSLB, Cookie compliance (No need to modify applications), etc.
253252

254253
## Support
255254

256-
* Product: [https://janusec.github.io/](https://janusec.github.io/)
257-
* Official site : [https://www.janusec.com/](https://www.janusec.com/)
258-
* Email: `support#janusec.com`
255+
* Product: [https://janusec.github.io/](https://janusec.github.io/)
256+
* Email: `support`**@**`janusec.com`
259257
* QQ Group: 776900157

backend/application.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ func SelectBackendRoute(app *models.Application, r *http.Request, srcIP string)
7474
// get online destinations
7575
var onlineDests = []*models.Destination{}
7676
for _, dest := range dests {
77+
dest.Mutex.Lock()
78+
defer dest.Mutex.Unlock()
7779
if dest.Online {
7880
onlineDests = append(onlineDests, dest)
7981
}

backend/destination.go

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,18 @@ package backend
99

1010
import (
1111
"encoding/json"
12+
"janusec/data"
1213
"janusec/models"
1314
"janusec/utils"
1415
"net"
1516
"net/http"
1617
"time"
18+
19+
"github.com/patrickmn/go-cache"
20+
)
21+
22+
var (
23+
offlineCache = cache.New(30*time.Second, 30*time.Second)
1724
)
1825

1926
// ContainsDestinationID ...
@@ -31,13 +38,17 @@ func ContainsDestinationID(destinations []*models.Destination, destID int64) boo
3138
func CheckOfflineDestinations(nowTimeStamp int64) {
3239
for _, app := range Apps {
3340
for _, dest := range app.Destinations {
41+
dest.Mutex.Lock()
42+
defer dest.Mutex.Unlock()
3443
if dest.RouteType == models.ReverseProxyRoute && !dest.Online {
35-
go func(dest *models.Destination) {
36-
conn, err := net.DialTimeout("tcp", dest.Destination, time.Second)
44+
go func(dest2 *models.Destination) {
45+
conn, err := net.DialTimeout("tcp", dest2.Destination, time.Second)
3746
if err == nil {
3847
defer conn.Close()
39-
dest.Online = true
40-
dest.CheckTime = nowTimeStamp
48+
dest2.Mutex.Lock()
49+
defer dest2.Mutex.Unlock()
50+
dest2.Online = true
51+
dest2.CheckTime = nowTimeStamp
4152
}
4253
}(dest)
4354
} else if dest.RouteType == models.K8S_Ingress && !dest.Online {
@@ -54,8 +65,6 @@ func CheckOfflineDestinations(nowTimeStamp int64) {
5465
if err != nil {
5566
utils.DebugPrintln("Unmarshal K8S API", err)
5667
}
57-
dest.Mutex.Lock()
58-
defer dest.Mutex.Unlock()
5968
dest.Pods = ""
6069
for _, podItem := range pods.Items {
6170
if podItem.Status.Phase == "Running" {
@@ -71,3 +80,45 @@ func CheckOfflineDestinations(nowTimeStamp int64) {
7180
}
7281
}
7382
}
83+
84+
// SetDestinationOffline added on Mar 23, 2024, v1.5.0
85+
func SetDestinationOffline(dest *models.Destination) {
86+
targetDest := dest.Destination
87+
if dest.RouteType == models.K8S_Ingress {
88+
targetDest = dest.PodsAPI
89+
}
90+
if count, ok := offlineCache.Get(targetDest); !ok {
91+
offlineCache.Set(targetDest, int64(1), cache.DefaultExpiration)
92+
} else {
93+
nowCount := count.(int64) + int64(1)
94+
if nowCount > 5 {
95+
// more than 5 requests timeout
96+
dest.Online = false
97+
app, err := GetApplicationByID(dest.AppID)
98+
if err == nil {
99+
sendOfflineNotification(app, targetDest)
100+
}
101+
}
102+
offlineCache.Set(targetDest, nowCount, cache.DefaultExpiration)
103+
}
104+
}
105+
106+
// sendOfflineNotification ...
107+
func sendOfflineNotification(app *models.Application, dest string) {
108+
var emails string
109+
if data.IsPrimary {
110+
emails = data.DAL.GetAppAdminAndOwnerEmails(app.Owner)
111+
} else {
112+
emails = data.NodeSetting.SMTP.AdminEmails
113+
}
114+
mailBody := "Backend server: " + dest + " (" + app.Name + ") was offline."
115+
if len(mailBody) > 0 && len(emails) > 0 {
116+
go utils.SendEmail(data.NodeSetting.SMTP.SMTPServer,
117+
data.NodeSetting.SMTP.SMTPPort,
118+
data.NodeSetting.SMTP.SMTPAccount,
119+
data.NodeSetting.SMTP.SMTPPassword,
120+
emails,
121+
"[JANUSEC] Backend server offline notification",
122+
mailBody)
123+
}
124+
}

backend/k8s.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func UpdatePods(dest *models.Destination, nowTimeStamp int64) {
2727
if err != nil {
2828
utils.DebugPrintln("Check K8S API GetResponse", err)
2929
dest.CheckTime = nowTimeStamp
30-
dest.Online = false
30+
SetDestinationOffline(dest)
3131
}
3232
pods := models.PODS{}
3333
err = json.Unmarshal(resp, &pods)
@@ -96,7 +96,7 @@ func SelectPodFromVIPTarget(dest *models.VipTarget, srcIP string) string {
9696
if err != nil {
9797
utils.DebugPrintln("Check K8S API GetResponse", err)
9898
dest.CheckTime = nowTimeStamp
99-
dest.Online = false
99+
SetVipTargetOffline(dest)
100100
}
101101
pods := models.PODS{}
102102
err = json.Unmarshal(resp, &pods)

backend/vip_app.go

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -104,22 +104,14 @@ func UDPForwarding(vipApp *models.VipApp, udpListenConn *net.UDPConn) {
104104
//fmt.Println("UDPForwarding ReadMsgUDP", err)
105105
break
106106
}
107-
108107
vipTarget := SelectVipTarget(vipApp, clientAddr.String())
109-
if err != nil {
110-
//fmt.Println("UDPForwarding ResolveUDPAddr", err)
111-
break
112-
}
113108
if vipTarget != nil {
114109
vipTarget.CheckTime = time.Now().Unix()
115110
targetAddr, _ := net.ResolveUDPAddr("udp", vipTarget.Destination)
116111
udpTargetConn, err := net.DialUDP("udp", nil, targetAddr)
117112
if err != nil {
118113
utils.DebugPrintln("UDPForwarding DialUDP could not connect to target", vipTarget.Destination, err)
119-
vipTarget.Online = false
120-
if data.NodeSetting.SMTP.SMTPEnabled {
121-
sendVIPOfflineNotification(vipApp, vipTarget.Destination)
122-
}
114+
SetVipTargetOffline(vipTarget)
123115
break
124116
}
125117
if udpTargetConn == nil {
@@ -138,10 +130,7 @@ func UDPForwarding(vipApp *models.VipApp, udpListenConn *net.UDPConn) {
138130
for {
139131
n, _, err := udpTargetConn.ReadFromUDP(dataBuf)
140132
if err != nil {
141-
vipTarget.Online = false
142-
if data.NodeSetting.SMTP.SMTPEnabled {
143-
sendVIPOfflineNotification(vipApp, vipTarget.Destination)
144-
}
133+
SetVipTargetOffline(vipTarget)
145134
break
146135
}
147136
// Response to client
@@ -190,10 +179,7 @@ func TCPForwarding(vipApp *models.VipApp, vipListener net.Listener) {
190179
vipTarget.CheckTime = time.Now().Unix()
191180
if err != nil {
192181
utils.DebugPrintln("TCPForwarding could not connect to target", targetDest, err)
193-
vipTarget.Online = false
194-
if data.NodeSetting.SMTP.SMTPEnabled {
195-
sendVIPOfflineNotification(vipApp, targetDest)
196-
}
182+
SetVipTargetOffline(vipTarget)
197183
continue
198184
}
199185
vipTarget.Online = true
@@ -385,23 +371,3 @@ func GetVipAppIndex(vipAppID int64) int {
385371
}
386372
return -1
387373
}
388-
389-
// sendVIPOfflineNotification ...
390-
func sendVIPOfflineNotification(app *models.VipApp, dest string) {
391-
var emails string
392-
if data.IsPrimary {
393-
emails = data.DAL.GetAppAdminAndOwnerEmails(app.Owner)
394-
} else {
395-
emails = data.NodeSetting.SMTP.AdminEmails
396-
}
397-
mailBody := "Backend virtual IP server: " + dest + " (" + app.Name + ") was offline."
398-
if len(mailBody) > 0 && len(emails) > 0 {
399-
go utils.SendEmail(data.NodeSetting.SMTP.SMTPServer,
400-
data.NodeSetting.SMTP.SMTPPort,
401-
data.NodeSetting.SMTP.SMTPAccount,
402-
data.NodeSetting.SMTP.SMTPPassword,
403-
emails,
404-
"[JANUSEC] Backend server offline notification",
405-
mailBody)
406-
}
407-
}

backend/vip_target.go

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"janusec/utils"
1414
"net"
1515
"time"
16+
17+
"github.com/patrickmn/go-cache"
1618
)
1719

1820
// DeleteVipTargetsByAppID delete backend targets for port forwarding
@@ -42,7 +44,7 @@ func CheckOfflineVipTargets(nowTimeStamp int64) {
4244
targetAddr, _ := net.ResolveUDPAddr("udp", vTarget.Destination)
4345
udpTargetConn, err := net.DialUDP("udp", nil, targetAddr)
4446
if err != nil {
45-
vTarget.Online = false
47+
SetVipTargetOffline(vTarget)
4648
return
4749
}
4850
// udpTargetConn will be closed in go thread
@@ -51,7 +53,7 @@ func CheckOfflineVipTargets(nowTimeStamp int64) {
5153
data := make([]byte, 2048)
5254
_, _, err := udpConn.ReadFromUDP(data)
5355
if err != nil {
54-
vipTarget.Online = false
56+
SetVipTargetOffline(vipTarget)
5557
} else {
5658
vipTarget.Online = true
5759
}
@@ -61,7 +63,7 @@ func CheckOfflineVipTargets(nowTimeStamp int64) {
6163
// send test data to target
6264
_, err = udpTargetConn.Write([]byte("Hi"))
6365
if err != nil {
64-
vTarget.Online = false
66+
SetVipTargetOffline(vTarget)
6567
return
6668
}
6769
}
@@ -79,3 +81,44 @@ func ContainsTargetID(targets []*models.VipTarget, targetID int64) bool {
7981
}
8082
return false
8183
}
84+
85+
func SetVipTargetOffline(dest *models.VipTarget) {
86+
target := dest.Destination
87+
if dest.RouteType == models.K8S_Ingress {
88+
target = dest.PodsAPI
89+
}
90+
if count, ok := offlineCache.Get(target); !ok {
91+
offlineCache.Set(target, int64(1), cache.DefaultExpiration)
92+
} else {
93+
nowCount := count.(int64) + int64(1)
94+
if nowCount > 5 {
95+
// more than 5 requests timeout
96+
dest.Online = false
97+
app, err := GetVipAppByID(dest.VipAppID)
98+
if err == nil {
99+
sendVIPOfflineNotification(app, target)
100+
}
101+
}
102+
offlineCache.Set(target, nowCount, cache.DefaultExpiration)
103+
}
104+
}
105+
106+
// sendVIPOfflineNotification ...
107+
func sendVIPOfflineNotification(app *models.VipApp, dest string) {
108+
var emails string
109+
if data.IsPrimary {
110+
emails = data.DAL.GetAppAdminAndOwnerEmails(app.Owner)
111+
} else {
112+
emails = data.NodeSetting.SMTP.AdminEmails
113+
}
114+
mailBody := "Backend virtual IP: " + dest + " (" + app.Name + ") was offline."
115+
if len(mailBody) > 0 && len(emails) > 0 {
116+
go utils.SendEmail(data.NodeSetting.SMTP.SMTPServer,
117+
data.NodeSetting.SMTP.SMTPPort,
118+
data.NodeSetting.SMTP.SMTPAccount,
119+
data.NodeSetting.SMTP.SMTPPassword,
120+
emails,
121+
"[JANUSEC] Backend server offline notification",
122+
mailBody)
123+
}
124+
}

data/data.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ var (
3535
// IsPrimary i.e. Is Primary Node
3636
IsPrimary bool
3737
// Version of JANUSEC
38-
Version = "1.4.2"
38+
Version = "1.5.0"
3939
)
4040

4141
// InitConfig init Data Access Layer

0 commit comments

Comments
 (0)