- Remove telemetry: disabled all phone-home (installation notify, false positives, behavior tracking, upgrade tips) - Version branding: removed 'ce-' prefix, product name changed to '长亭雷池 WAF' - Detection engine enhancement: strict preset enables all 17 modules with aggressive configs - Multi-user RBAC: admin/operator/viewer roles with password + TOTP 2FA - Rate limiting: nginx limit_req per website (RPS, burst, action) - JS Challenge: browser fingerprint verification page with cookie-based bypass - Security fixes: removed InsecureSkipVerify, hardcoded credentials, NO_AUTH backdoor
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package analyze
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/chaitin/SafeLine/mcp_server/internal/api"
|
||||
)
|
||||
|
||||
type GetEventListRequest struct {
|
||||
Page int `json:"page"`
|
||||
PageSize int `json:"page_size"`
|
||||
IP string `json:"ip"`
|
||||
Start int64 `json:"start"`
|
||||
End int64 `json:"end"`
|
||||
}
|
||||
|
||||
type GetEventListResponse struct {
|
||||
Nodes []Event `json:"nodes"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
ID uint `json:"id"`
|
||||
IP string `json:"ip"`
|
||||
Protocol int `json:"protocol"`
|
||||
Host string `json:"host"`
|
||||
DstPort uint64 `json:"dst_port"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
StartAt int64 `json:"start_at"`
|
||||
EndAt int64 `json:"end_at"`
|
||||
DenyCount int64 `json:"deny_count"`
|
||||
PassCount int64 `json:"pass_count"`
|
||||
Finished bool `json:"finished"`
|
||||
Country string `json:"country"`
|
||||
Province string `json:"province"`
|
||||
City string `json:"city"`
|
||||
}
|
||||
|
||||
func GetEventList(ctx context.Context, req *GetEventListRequest) (*GetEventListResponse, error) {
|
||||
var resp api.Response[GetEventListResponse]
|
||||
err := api.Service().Get(ctx, fmt.Sprintf("/api/open/events?page=%d&page_size=%d&ip=%s&start=%d&end=%d", req.Page, req.PageSize, req.IP, req.Start, req.End), &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp.Data, nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/chaitin/SafeLine/mcp_server/internal/api"
|
||||
"github.com/chaitin/SafeLine/mcp_server/pkg/errors"
|
||||
)
|
||||
|
||||
type CreateAppRequest struct {
|
||||
ServerNames []string `json:"server_names"`
|
||||
Ports []string `json:"ports"`
|
||||
Upstreams []string `json:"upstreams"`
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
// CreateApp Create new website or app
|
||||
func CreateApp(ctx context.Context, req *CreateAppRequest) (int64, error) {
|
||||
if req == nil {
|
||||
return 0, errors.New("request is required")
|
||||
}
|
||||
|
||||
var resp api.Response[int64]
|
||||
err := api.Service().Post(ctx, "/api/open/site", req, &resp)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "failed to create app")
|
||||
}
|
||||
|
||||
if resp.Err != nil {
|
||||
return 0, errors.New(resp.Msg)
|
||||
}
|
||||
|
||||
return resp.Data, nil
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/chaitin/SafeLine/mcp_server/pkg/errors"
|
||||
"github.com/chaitin/SafeLine/mcp_server/pkg/logger"
|
||||
)
|
||||
|
||||
// Client API client
|
||||
type Client struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
headers map[string]string
|
||||
}
|
||||
|
||||
// ClientOption Client configuration options
|
||||
type ClientOption func(*Client)
|
||||
|
||||
// WithTimeout Set timeout duration
|
||||
func WithTimeout(timeout time.Duration) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.httpClient.Timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeader Set request header
|
||||
func WithHeader(key, value string) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.headers[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
// WithBaseURL Set base URL
|
||||
func WithBaseURL(baseURL string) ClientOption {
|
||||
return func(c *Client) {
|
||||
c.baseURL = baseURL
|
||||
}
|
||||
}
|
||||
|
||||
// WithInsecureSkipVerify Set whether to skip certificate verification
|
||||
func WithInsecureSkipVerify(skip bool) ClientOption {
|
||||
return func(c *Client) {
|
||||
if transport, ok := c.httpClient.Transport.(*http.Transport); ok {
|
||||
if transport.TLSClientConfig == nil {
|
||||
transport.TLSClientConfig = &tls.Config{}
|
||||
}
|
||||
transport.TLSClientConfig.InsecureSkipVerify = skip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewClient Create new API client
|
||||
func NewClient(opts ...ClientOption) *Client {
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{},
|
||||
}
|
||||
|
||||
c := &Client{
|
||||
httpClient: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: transport,
|
||||
},
|
||||
headers: make(map[string]string),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Request Send request
|
||||
func (c *Client) Request(ctx context.Context, method, path string, body interface{}, result interface{}) error {
|
||||
reqURL := fmt.Sprintf("%s%s", c.baseURL, path)
|
||||
|
||||
var bodyReader io.Reader
|
||||
if body != nil {
|
||||
bodyBytes, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshal request body failed")
|
||||
}
|
||||
bodyReader = bytes.NewReader(bodyBytes)
|
||||
}
|
||||
logger.With("url", reqURL).Debug("request url")
|
||||
req, err := http.NewRequestWithContext(ctx, method, reqURL, bodyReader)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create request failed")
|
||||
}
|
||||
|
||||
// Set common headers
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
for k, v := range c.headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "send request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Read response body
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read response body failed")
|
||||
}
|
||||
|
||||
// Check status code
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return errors.New(fmt.Sprintf("request failed with status %d: %s", resp.StatusCode, string(respBody)))
|
||||
}
|
||||
|
||||
// Parse response
|
||||
if result != nil {
|
||||
if err := json.Unmarshal(respBody, result); err == nil {
|
||||
return nil
|
||||
}
|
||||
var respData map[string]interface{}
|
||||
if err := json.Unmarshal(respBody, &respData); err != nil {
|
||||
return errors.Wrap(err, "unmarshal response failed")
|
||||
}
|
||||
if respData["err"] != nil || respData["msg"] != nil {
|
||||
return errors.New(respData["msg"].(string))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get Send GET request
|
||||
func (c *Client) Get(ctx context.Context, path string, result interface{}) error {
|
||||
return c.Request(ctx, http.MethodGet, path, nil, result)
|
||||
}
|
||||
|
||||
// Post Send POST request
|
||||
func (c *Client) Post(ctx context.Context, path string, body interface{}, result interface{}) error {
|
||||
return c.Request(ctx, http.MethodPost, path, body, result)
|
||||
}
|
||||
|
||||
// Put Send PUT request
|
||||
func (c *Client) Put(ctx context.Context, path string, body interface{}, result interface{}) error {
|
||||
return c.Request(ctx, http.MethodPut, path, body, result)
|
||||
}
|
||||
|
||||
// Delete Send DELETE request
|
||||
func (c *Client) Delete(ctx context.Context, path string, result interface{}) error {
|
||||
return c.Request(ctx, http.MethodDelete, path, nil, result)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package api
|
||||
|
||||
// Response Common API response structure
|
||||
type Response[T any] struct {
|
||||
// Response data
|
||||
Data T `json:"data"`
|
||||
// Error message
|
||||
Err any `json:"err"`
|
||||
// Prompt message
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/chaitin/SafeLine/mcp_server/internal/api"
|
||||
"github.com/chaitin/SafeLine/mcp_server/pkg/errors"
|
||||
)
|
||||
|
||||
type CreateRuleRequest struct {
|
||||
Name string `json:"name"`
|
||||
IP []string `json:"ip"`
|
||||
IsEnabled bool `json:"is_enabled"`
|
||||
Pattern [][]api.Pattern `json:"pattern"`
|
||||
Action int `json:"action"`
|
||||
}
|
||||
|
||||
// CreateRule Create new rule
|
||||
func CreateRule(ctx context.Context, req *CreateRuleRequest) (int64, error) {
|
||||
if req == nil {
|
||||
return 0, errors.New("request is required")
|
||||
}
|
||||
|
||||
var resp api.Response[int64]
|
||||
err := api.Service().Post(ctx, "/api/open/policy", req, &resp)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "failed to create policy rule")
|
||||
}
|
||||
|
||||
if resp.Err != nil {
|
||||
return 0, errors.New(resp.Msg)
|
||||
}
|
||||
|
||||
return resp.Data, nil
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/chaitin/SafeLine/mcp_server/internal/config"
|
||||
"github.com/chaitin/SafeLine/mcp_server/pkg/errors"
|
||||
"github.com/chaitin/SafeLine/mcp_server/pkg/logger"
|
||||
)
|
||||
|
||||
// APIClient API client implementation
|
||||
type APIClient struct {
|
||||
client *Client
|
||||
config *config.APIConfig
|
||||
}
|
||||
|
||||
var (
|
||||
instance *APIClient
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// Init Initialize API service
|
||||
func Init(cfg *config.APIConfig) error {
|
||||
var err error
|
||||
once.Do(func() {
|
||||
instance, err = newAPIClient(cfg)
|
||||
if err != nil {
|
||||
logger.With("error", err).Error("failed to initialize API service")
|
||||
return
|
||||
}
|
||||
logger.Info("API service initialized successfully")
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// Service Get API service instance
|
||||
func Service() *APIClient {
|
||||
if instance == nil {
|
||||
logger.Error("API service not initialized")
|
||||
panic("API service not initialized")
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
// newAPIClient Create new API client
|
||||
func newAPIClient(config *config.APIConfig) (*APIClient, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("config is required")
|
||||
}
|
||||
|
||||
if config.BaseURL == "" {
|
||||
return nil, errors.New("base_url is required")
|
||||
}
|
||||
|
||||
timeout := 30
|
||||
if config.Timeout > 0 {
|
||||
timeout = config.Timeout
|
||||
}
|
||||
|
||||
opts := []ClientOption{
|
||||
WithBaseURL(config.BaseURL),
|
||||
WithTimeout(time.Duration(timeout) * time.Second),
|
||||
WithHeader("User-Agent", "SafeLine-MCP/1.0"),
|
||||
WithInsecureSkipVerify(config.InsecureSkipVerify),
|
||||
}
|
||||
|
||||
// If token is configured, add authentication header
|
||||
if config.Token != "" {
|
||||
opts = append(opts, WithHeader("X-SLCE-API-TOKEN", config.Token))
|
||||
}
|
||||
|
||||
client := NewClient(opts...)
|
||||
|
||||
return &APIClient{
|
||||
client: client,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Post Send POST request
|
||||
func (c *APIClient) Post(ctx context.Context, path string, body interface{}, result interface{}) error {
|
||||
return c.client.Request(ctx, "POST", path, body, result)
|
||||
}
|
||||
|
||||
// Get Send GET request
|
||||
func (c *APIClient) Get(ctx context.Context, path string, result interface{}) error {
|
||||
return c.client.Request(ctx, "GET", path, nil, result)
|
||||
}
|
||||
|
||||
// Put Send PUT request
|
||||
func (c *APIClient) Put(ctx context.Context, path string, body interface{}, result interface{}) error {
|
||||
return c.client.Request(ctx, "PUT", path, body, result)
|
||||
}
|
||||
|
||||
// Delete Send DELETE request
|
||||
func (c *APIClient) Delete(ctx context.Context, path string, result interface{}) error {
|
||||
return c.client.Request(ctx, "DELETE", path, nil, result)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package api
|
||||
|
||||
type PolicyRuleAction int
|
||||
|
||||
const (
|
||||
PolicyRuleActionAllow PolicyRuleAction = iota
|
||||
PolicyRuleActionDeny
|
||||
PolicyRuleActionMax
|
||||
)
|
||||
|
||||
type Key = string
|
||||
|
||||
const (
|
||||
KeySrcIP Key = "src_ip"
|
||||
KeyURI Key = "uri"
|
||||
KeyURINoQuery Key = "uri_no_query"
|
||||
KeyHost Key = "host"
|
||||
KeyMethod Key = "method"
|
||||
KeyReqHeader Key = "req_header"
|
||||
KeyReqBody Key = "req_body"
|
||||
KeyGetParam Key = "get_param"
|
||||
KeyPostParam Key = "post_param"
|
||||
)
|
||||
|
||||
type Op = string
|
||||
|
||||
const (
|
||||
OpEq Op = "eq" // equal
|
||||
OpNotEq Op = "not_eq" // not equal
|
||||
OpMatch Op = "match" // match
|
||||
OpCIDR Op = "cidr" // cidr
|
||||
OpHas Op = "has" // has
|
||||
OpNotHas Op = "not_has" // not has
|
||||
OpPrefix Op = "prefix" // prefix
|
||||
OpRe Op = "re" // regex
|
||||
OpIn Op = "in" // in
|
||||
OpNotIn Op = "not_in" // not in
|
||||
OpNotCIDR Op = "not_cidr" // not cidr
|
||||
OpExist Op = "exist" // exist
|
||||
OpNotExist Op = "not_exist" // not exist
|
||||
OpGeoEq Op = "geo_eq" // geo equal
|
||||
OpGeoNotEq Op = "geo_not_eq" // geo not equal
|
||||
)
|
||||
|
||||
type Pattern struct {
|
||||
K Key `json:"k"`
|
||||
Op Op `json:"op"`
|
||||
V []string `json:"v"`
|
||||
SubK string `json:"sub_k"`
|
||||
}
|
||||
Reference in New Issue
Block a user