Skip to content

Commit

Permalink
mvp
Browse files Browse the repository at this point in the history
  • Loading branch information
kutovoys committed Aug 30, 2024
0 parents commit ec17483
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 0 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/build-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Build Docker images

on:
push:
branches:
- main

jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set outputs
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/xray-checker:latest
${{ secrets.DOCKERHUB_USERNAME }}/xray-checker:${{ steps.vars.outputs.sha_short }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
all/
configs/*
build/
xray-checker
33 changes: 33 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.21 as builder

ARG TARGETPLATFORM
ARG BUILDPLATFORM
ARG TARGETOS
ARG TARGETARCH
ARG GIT_TAG
ARG GIT_COMMIT

ENV CGO_ENABLED=0
ENV GO111MODULE=on

WORKDIR /go/src/github.com/kutovoys/xray-checker

# Cache the download before continuing
COPY go.mod go.mod
COPY go.sum go.sum
RUN go mod download

COPY . .

RUN CGO_ENABLED=${CGO_ENABLED} GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -a -installsuffix cgo -o /usr/bin/xray-checker .

FROM --platform=${BUILDPLATFORM:-linux/amd64} teddysun/xray:1.8.23

LABEL org.opencontainers.image.source=https://github.com/kutovoys/xray-checker

WORKDIR /
COPY --from=builder /usr/bin/xray-checker /checker/xray-checker
# USER nonroot:nonroot

CMD ["/checker/xray-checker"]
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module xray-checker

go 1.21.5

require (
github.com/go-co-op/gocron v1.37.0
golang.org/x/net v0.28.0
)

require (
github.com/google/uuid v1.4.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
go.uber.org/atomic v1.9.0 // indirect
)
40 changes: 40 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
222 changes: 222 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package main

import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"time"

"github.com/go-co-op/gocron"
"golang.org/x/net/proxy"
)

type Config struct {
Log map[string]interface{} `json:"log"`
Inbounds []struct {
Listen string `json:"listen"`
Port int `json:"port"`
Protocol string `json:"protocol"`
Sniffing struct {
Enabled bool `json:"enabled"`
DestOverride []string `json:"destOverride"`
RouteOnly bool `json:"routeOnly"`
} `json:"sniffing"`
} `json:"inbounds"`
Outbounds []map[string]interface{} `json:"outbounds"`
Webhook string `json:"webhook"`
}

type LogData struct {
ConfigFile string
SourceIP string
VPNIP string
WebhookURL string
ProxyAddress string
Status string
Error error
}

func getIP(url string, client *http.Client) (string, error) {
resp, err := client.Get(url)
if err != nil {
return "", err
}
defer resp.Body.Close()

ip, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return strings.TrimSpace(string(ip)), nil
}

func runXray(configPath string) (*exec.Cmd, error) {
cmd := exec.Command("xray", "-c", configPath)
err := cmd.Start()
if err != nil {
return nil, err
}
return cmd, nil
}

func killXray(cmd *exec.Cmd) error {
return cmd.Process.Kill()
}

func createProxyClient(proxyAddress string) (*http.Client, error) {
proxyURL, err := url.Parse(proxyAddress)
if err != nil {
return nil, fmt.Errorf("неправильный формат прокси: %v", err)
}

dialer, err := proxy.FromURL(proxyURL, proxy.Direct)
if err != nil {
return nil, fmt.Errorf("ошибка создания прокси-диалера: %v", err)
}

transport := &http.Transport{
Dial: dialer.Dial,
}

client := &http.Client{
Transport: transport,
}

return client, nil
}

func processConfigFile(configPath string) {
logData := LogData{ConfigFile: configPath}

configData, err := os.ReadFile(configPath)
if err != nil {
logData.Error = fmt.Errorf("ошибка чтения конфигурационного файла: %v", err)
logResult(logData)
return
}

var config Config
err = json.Unmarshal(configData, &config)
if err != nil {
logData.Error = fmt.Errorf("ошибка парсинга конфигурационного файла: %v", err)
logResult(logData)
return
}

logData.WebhookURL = config.Webhook
if logData.WebhookURL == "" {
logData.Error = fmt.Errorf("webhook URL не найден в конфигурационном файле")
logResult(logData)
return
}

ipCheckURL := "https://ifconfig.io"
logData.SourceIP, err = getIP(ipCheckURL, &http.Client{})
if err != nil {
logData.Error = fmt.Errorf("ошибка получения исходного IP: %v", err)
logResult(logData)
return
}

listen := config.Inbounds[0].Listen
port := config.Inbounds[0].Port
logData.ProxyAddress = fmt.Sprintf("socks5://%s:%d", listen, port)

cmd, err := runXray(configPath)
if err != nil {
logData.Error = fmt.Errorf("ошибка запуска Xray: %v", err)
logResult(logData)
return
}
defer killXray(cmd)
time.Sleep(2 * time.Second)

proxyClient, err := createProxyClient(logData.ProxyAddress)
if err != nil {
logData.Error = fmt.Errorf("ошибка создания прокси-клиента: %v", err)
logResult(logData)
return
}

logData.VPNIP, err = getIP(ipCheckURL, proxyClient)
if err != nil {
logData.Error = fmt.Errorf("ошибка получения VPN IP через прокси: %v", err)
logResult(logData)
return
}

if logData.VPNIP != logData.SourceIP {
_, err = http.Get(logData.WebhookURL)
if err != nil {
logData.Error = fmt.Errorf("ошибка отправки статуса: %v", err)
logData.Status = "Не удалось отправить статус"
} else {
logData.Status = "Статус отправлен успешно"
}
} else {
logData.Status = "IP-адреса совпадают, статус не отправлен"
}

logResult(logData)
}

func logResult(logData LogData) {
var logMsg string

if logData.Error != nil {
logMsg = fmt.Sprintf("Error: %v | Config: %s | Source IP: %s | VPN IP: %s",
logData.Error, logData.ConfigFile, logData.SourceIP, logData.VPNIP)
} else {
logMsg = fmt.Sprintf("Status: %s | Config: %s | Source IP: %s | VPN IP: %s",
logData.Status, logData.ConfigFile, logData.SourceIP, logData.VPNIP)
}

log.Println(logMsg)
}

func scheduleConfigs(configDir string, scheduler *gocron.Scheduler) {
files, err := os.ReadDir(configDir)
if err != nil {
fmt.Println("Ошибка чтения директории:", err)
return
}

for _, file := range files {
if filepath.Ext(file.Name()) == ".json" {
configPath := filepath.Join(configDir, file.Name())
// Запускаем задачу в шедулере каждые 10 секунд
scheduler.Every(40).Seconds().Do(func() {
processConfigFile(configPath)
})
}
}
}

func main() {
configDir := "./configs" // директория с конфигурационными файлами
var wg sync.WaitGroup

// Создаем новый шедулер
scheduler := gocron.NewScheduler(time.UTC)

// Планируем обработку конфигурационных файлов
scheduleConfigs(configDir, scheduler)

// Запускаем шедулер в отдельной горутине
wg.Add(1)
go func() {
defer wg.Done()
scheduler.StartBlocking()
}()

// Ожидаем завершения работы
wg.Wait()
}

0 comments on commit ec17483

Please sign in to comment.