Compare commits

3 Commits
vSerf ... main

Author SHA1 Message Date
752f294392 Alias WildCard
Возможность добавлять Alias по маске WildCard
2025-10-03 01:59:56 +07:00
d11834da37 Добавление vAccess для Proxy
Теперь наши правила работают так же для Proxy серверов.
2025-10-03 01:04:50 +07:00
Falknat
f518171825 Proxy - Добавление AutoHTTPS
Включает Автоматическая перенаправление на HTTPS на proxy
2025-10-02 09:34:19 +07:00
8 changed files with 510 additions and 80 deletions

View File

@@ -12,6 +12,70 @@ func StartHandler() {
http.HandleFunc("/", handler)
}
// Проверка wildcard паттерна для alias
func matchWildcardAlias(pattern, host string) bool {
// Если нет звёздочки - точное совпадение
if !strings.Contains(pattern, "*") {
return pattern == host
}
// Поддержка wildcard: *.example.com, example.*, *example*, *
// Заменяем * на регулярное выражение
pattern = strings.ReplaceAll(pattern, ".", "\\.")
pattern = strings.ReplaceAll(pattern, "*", ".*")
pattern = "^" + pattern + "$"
// Простая проверка без regexp (более быстрая)
return matchSimplePattern(pattern, host)
}
// Простая проверка паттерна (без использования regexp для скорости)
func matchSimplePattern(pattern, host string) bool {
// Убираем ^ и $ добавленные выше
pattern = strings.TrimPrefix(pattern, "^")
pattern = strings.TrimSuffix(pattern, "$")
// Если паттерн = .* (любой хост)
if pattern == ".*" {
return true
}
// Разбиваем паттерн на части по .*
parts := strings.Split(pattern, ".*")
// Проверяем каждую часть
currentPos := 0
for i, part := range parts {
if part == "" {
continue
}
// Ищем часть в хосте начиная с текущей позиции
idx := strings.Index(host[currentPos:], part)
if idx == -1 {
return false
}
// Для первой части проверяем что она в начале (если паттерн не начинается с *)
if i == 0 && !strings.HasPrefix(pattern, ".*") {
if idx != 0 {
return false
}
}
// Для последней части проверяем что она в конце (если паттерн не кончается на *)
if i == len(parts)-1 && !strings.HasSuffix(pattern, ".*") {
if currentPos+idx+len(part) != len(host) {
return false
}
}
currentPos += idx + len(part)
}
return true
}
func Alias_check(r *http.Request) (alias_found bool, host string) {
alias_found = false
@@ -20,10 +84,10 @@ func Alias_check(r *http.Request) (alias_found bool, host string) {
for _, alias := range site.Alias {
if alias == r.Host {
// Поддержка wildcard паттернов
if matchWildcardAlias(alias, r.Host) {
alias_found = true
return alias_found, site.Host
} else {
alias_found = false
}

View File

@@ -35,8 +35,25 @@ func StartHandlerProxy(w http.ResponseWriter, r *http.Request) (valid bool) {
valid = true
// Логирование прокси-запроса
// Проверяем vAccess для прокси
accessAllowed, errorPage := CheckProxyVAccess(r.URL.Path, proxyConfig.ExternalDomain, r)
if !accessAllowed {
// Доступ запрещён - обрабатываем страницу ошибки
HandleProxyVAccessError(w, r, errorPage)
return valid
}
// Проверяем AutoHTTPS - редирект с HTTP на HTTPS
https_check := !(r.TLS == nil)
if !https_check && proxyConfig.AutoHTTPS {
// Перенаправляем на HTTPS
httpsURL := "https://" + r.Host + r.URL.RequestURI()
http.Redirect(w, r, httpsURL, http.StatusMovedPermanently)
tools.Logs_file(0, "P-HTTP", "🔀 IP клиента: "+r.RemoteAddr+" Редирект HTTP → HTTPS: "+r.Host+r.URL.Path, "logs_http.log", false)
return valid
}
// Логирование прокси-запроса
if https_check {
tools.Logs_file(0, "P-HTTPS", "🔍 IP клиента: "+r.RemoteAddr+" Обработка запроса: https://"+r.Host+r.URL.Path+" → "+proxyConfig.LocalAddress+":"+proxyConfig.LocalPort, "logs_https.log", false)
} else {
@@ -45,7 +62,7 @@ func StartHandlerProxy(w http.ResponseWriter, r *http.Request) (valid bool) {
// Определяем протокол для локального соединения
protocol := "http"
if proxyConfig.UseHTTPS {
if proxyConfig.ServiceHTTPSuse {
protocol = "https"
}
@@ -85,7 +102,7 @@ func StartHandlerProxy(w http.ResponseWriter, r *http.Request) (valid bool) {
}
// Для HTTPS соединений настраиваем TLS (если понадобится)
if proxyConfig.UseHTTPS {
if proxyConfig.ServiceHTTPSuse {
client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true, // Простая настройка для внутренних соединений

View File

@@ -307,6 +307,103 @@ func matchExceptions(exceptions []string, requestPath string) bool {
return false
}
// Универсальная функция проверки правил vAccess
// Возвращает (разрешён_доступ, страница_ошибки)
func checkRules(rules []VAccessRule, requestPath string, r *http.Request, checkFileExtensions bool, logPrefix string, logFile string) (bool, string) {
// Проверяем каждое правило
for _, rule := range rules {
// Проверяем соответствие путей (если указаны)
pathMatched := true // По умолчанию true, если путей нет
if len(rule.PathAccess) > 0 {
pathMatched = false
for _, rulePath := range rule.PathAccess {
if matchPath(rulePath, requestPath) {
pathMatched = true
break
}
}
}
// Если путь не совпадает - переходим к следующему правилу
if !pathMatched {
continue
}
// Проверяем исключения - если путь в исключениях, пропускаем правило
if matchExceptions(rule.ExceptionsDir, requestPath) {
continue
}
// Проверяем соответствие расширения файла (если включена проверка)
fileMatches := true // По умолчанию true
if checkFileExtensions && len(rule.TypeFile) > 0 {
fileMatches = matchFileExtension(rule.TypeFile, requestPath)
}
// Проверяем соответствие IP адреса (если указаны)
ipMatches := true // По умолчанию true, если IP не указаны
if len(rule.IPList) > 0 {
clientIP := getClientIP(r)
ipMatches = matchIPAddress(rule.IPList, clientIP)
}
// Применяем правило в зависимости от типа
switch rule.Type {
case "Allow":
// Allow правило: разрешаем только если ВСЕ условия выполнены
conditionsFailed := false
if checkFileExtensions && len(rule.TypeFile) > 0 && !fileMatches {
conditionsFailed = true
}
if len(rule.IPList) > 0 && !ipMatches {
conditionsFailed = true
}
if conditionsFailed {
// Условия НЕ выполнены - блокируем
errorPage := rule.UrlError
if errorPage == "" {
errorPage = "404"
}
tools.Logs_file(1, logPrefix, "🚫 Доступ запрещён для "+getClientIP(r)+" к "+requestPath, logFile, false)
return false, errorPage
}
// Все условия Allow выполнены - разрешаем доступ
return true, ""
case "Disable":
// Disable правило: запрещаем если ЛЮБОЕ условие выполнено
shouldBlock := true
// Для расширений файлов (только если проверка включена)
if checkFileExtensions && len(rule.TypeFile) > 0 && !fileMatches {
shouldBlock = false
}
// Для IP адресов
if len(rule.IPList) > 0 && !ipMatches {
shouldBlock = false
}
if shouldBlock {
errorPage := rule.UrlError
if errorPage == "" {
errorPage = "404"
}
tools.Logs_file(1, logPrefix, "🚫 Доступ запрещён для "+getClientIP(r)+" к "+requestPath, logFile, false)
return false, errorPage
}
default:
// Неизвестный тип правила - игнорируем
continue
}
}
// Все проверки пройдены - разрешаем доступ
return true, ""
}
// Основная функция проверки доступа
// Возвращает (разрешён_доступ, страница_ошибки)
func CheckVAccess(requestPath string, host string, r *http.Request) (bool, string) {
@@ -326,70 +423,10 @@ func CheckVAccess(requestPath string, host string, r *http.Request) (bool, strin
continue
}
// Проверяем каждое правило в конфиге
for _, rule := range config.Rules {
// Проверяем соответствие путей (если указаны)
pathMatched := true // По умолчанию true, если путей нет
if len(rule.PathAccess) > 0 {
pathMatched = false
for _, rulePath := range rule.PathAccess {
if matchPath(rulePath, requestPath) {
pathMatched = true
break
}
}
}
// Если путь не совпадает - переходим к следующему правилу
if !pathMatched {
continue
}
// Проверяем исключения - если путь в исключениях, пропускаем правило
if matchExceptions(rule.ExceptionsDir, requestPath) {
continue
}
// Проверяем соответствие расширения файла (если указаны)
fileMatches := true // По умолчанию true, если типов файлов нет
if len(rule.TypeFile) > 0 {
fileMatches = matchFileExtension(rule.TypeFile, requestPath)
}
// Проверяем соответствие IP адреса (если указаны)
ipMatches := true // По умолчанию true, если IP не указаны
if len(rule.IPList) > 0 {
clientIP := getClientIP(r)
ipMatches = matchIPAddress(rule.IPList, clientIP)
}
// Применяем правило в зависимости от типа
switch rule.Type {
case "Allow":
// Allow правило: разрешаем только если ВСЕ условия выполнены
if (len(rule.TypeFile) > 0 && !fileMatches) || (len(rule.IPList) > 0 && !ipMatches) {
// Условия НЕ выполнены - блокируем
errorPage := rule.UrlError
if errorPage == "" {
errorPage = "404" // По умолчанию 404
}
return false, errorPage
}
// Все условия Allow выполнены - разрешаем доступ
return true, ""
case "Disable":
// Disable правило: запрещаем если ЛЮБОЕ условие выполнено
if (len(rule.TypeFile) == 0 || fileMatches) && (len(rule.IPList) == 0 || ipMatches) {
errorPage := rule.UrlError
if errorPage == "" {
errorPage = "404" // По умолчанию 404
}
return false, errorPage
}
default:
// Неизвестный тип правила - игнорируем
continue
}
// Используем универсальную функцию проверки правил (с проверкой расширений файлов)
allowed, errorPage := checkRules(config.Rules, requestPath, r, true, "vAccess", "logs_vaccess.log")
if !allowed {
return false, errorPage
}
}
@@ -420,3 +457,49 @@ func HandleVAccessError(w http.ResponseWriter, r *http.Request, errorPage string
}
}
}
// ========================================
// ФУНКЦИИ ДЛЯ ПРОКСИ-СЕРВЕРА
// ========================================
// Основная функция проверки доступа для прокси-сервера
// Возвращает (разрешён_доступ, страница_ошибки)
func CheckProxyVAccess(requestPath string, domain string, r *http.Request) (bool, string) {
// Путь к конфигурационному файлу прокси
configPath := "WebServer/tools/Proxy_vAccess/" + domain + "_vAccess.conf"
// Проверяем существование файла
if _, err := os.Stat(configPath); os.IsNotExist(err) {
// Нет конфигурационного файла - разрешаем доступ
return true, ""
}
// Парсим конфигурационный файл
config, err := parseVAccessFile(configPath)
if err != nil {
tools.Logs_file(1, "vAccess-Proxy", "❌ Ошибка парсинга "+configPath+": "+err.Error(), "logs_vaccess_proxy.log", false)
return true, "" // При ошибке парсинга разрешаем доступ
}
// Используем универсальную функцию проверки правил (с проверкой расширений файлов)
return checkRules(config.Rules, requestPath, r, true, "vAccess-Proxy", "logs_vaccess_proxy.log")
}
// Обработка страницы ошибки vAccess для прокси
func HandleProxyVAccessError(w http.ResponseWriter, r *http.Request, errorPage string) {
switch {
case errorPage == "404":
// Стандартная 404 страница
w.WriteHeader(http.StatusForbidden)
http.ServeFile(w, r, "WebServer/tools/error_page/index.html")
case strings.HasPrefix(errorPage, "http://") || strings.HasPrefix(errorPage, "https://"):
// Внешний сайт - редирект
http.Redirect(w, r, errorPage, http.StatusFound)
default:
// Для прокси возвращаем 403 Forbidden
w.WriteHeader(http.StatusForbidden)
http.ServeFile(w, r, "WebServer/tools/error_page/index.html")
}
}

View File

@@ -33,11 +33,12 @@ type Soft_Settings struct {
}
type Proxy_Service struct {
Enable bool `json:"Enable"`
ExternalDomain string `json:"ExternalDomain"`
LocalAddress string `json:"LocalAddress"`
LocalPort string `json:"LocalPort"`
UseHTTPS bool `json:"UseHTTPS"`
Enable bool `json:"Enable"`
ExternalDomain string `json:"ExternalDomain"`
LocalAddress string `json:"LocalAddress"`
LocalPort string `json:"LocalPort"`
ServiceHTTPSuse bool `json:"ServiceHTTPSuse"`
AutoHTTPS bool `json:"AutoHTTPS"`
}
func LoadConfig() {

100
README.md
View File

@@ -12,6 +12,7 @@
-**Proxy сервер** для проксирования запросов
-**PHP сервер** со встроенной поддержкой PHP 8
-**Статический контент** для размещения веб-сайтов
-**vAccess** - система контроля доступа для сайтов и прокси
### 🗄️ База данных
-**MySQL сервер** с полной поддержкой
@@ -88,7 +89,8 @@ go build -o MyApp.exe
"ExternalDomain": "git.example.ru",
"LocalAddress": "127.0.0.1",
"LocalPort": "3333",
"UseHTTPS": false
"ServiceHTTPSuse": false,
"AutoHTTPS": true
}
],
"Soft_Settings": {
@@ -104,6 +106,29 @@ go build -o MyApp.exe
- `Proxy_Service` - конфигурация прокси-сервисов
- `Soft_Settings` - порты и хосты сервисов (MySQL, PHP, админка)
### 🌐 Alias с поддержкой Wildcard
Для сайтов поддерживается wildcard (`*`) в алиасах:
```json
{
"alias": [
"*.test.ru", // Все поддомены voxsel.ru
"*.test.com", // Все поддомены voxsel.com
"test.com", // Точное совпадение
"api.*" // api с любой зоной
],
"host": "test.ru"
}
```
**Примеры работы wildcard:**
- `*.example.com``api.example.com`, `admin.example.com`, `test.example.com`
- `example.*``example.com`, `example.ru`, `example.org`
- `*example.com``test-example.com`, `my-example.com`
- `*` → любой домен ✅ (осторожно!)
- `example.com` → только `example.com` ✅ (без wildcard)
### 🔄 Прокси-сервер
Прокси-сервер позволяет перенаправлять внешние запросы на локальные сервисы.
@@ -113,7 +138,8 @@ go build -o MyApp.exe
- `ExternalDomain` - внешний домен для перехвата запросов
- `LocalAddress` - локальный адрес сервиса
- `LocalPort` - порт локального сервиса
- `UseHTTPS` - использовать HTTPS для локального подключения (true/false)
- `ServiceHTTPSuse` - использовать HTTPS для подключения к локальному сервису (true/false)
- `AutoHTTPS` - автоматически перенаправлять HTTP → HTTPS (true/false)
**Пример множественных прокси:**
```json
@@ -123,22 +149,85 @@ go build -o MyApp.exe
"ExternalDomain": "git.example.com",
"LocalAddress": "127.0.0.1",
"LocalPort": "3000",
"UseHTTPS": false
"ServiceHTTPSuse": false,
"AutoHTTPS": true
},
{
"Enable": false,
"ExternalDomain": "api.example.com",
"LocalAddress": "127.0.0.1",
"LocalPort": "8080",
"UseHTTPS": false
"ServiceHTTPSuse": false,
"AutoHTTPS": false
}
]
```
#### 📖 Подробное описание параметров:
**`ServiceHTTPSuse`** - протокол подключения к локальному сервису:
- `false` - vServer подключается к локальному сервису по HTTP (по умолчанию)
- `true` - vServer подключается к локальному сервису по HTTPS
**`AutoHTTPS`** - автоматический редирект на HTTPS:
- `true` - все HTTP запросы автоматически перенаправляются на HTTPS (рекомендуется)
- `false` - разрешены как HTTP, так и HTTPS запросы
**Схема работы:**
```
Клиент (HTTP/HTTPS) → vServer (проверка AutoHTTPS) → Локальный сервис (ServiceHTTPSuse)
```
**Применение изменений:**
- Введите команду `config_reload` в консоли для перезагрузки конфигурации
- Изменения применятся к новым запросам без перезапуска сервера
## 🔒 vAccess - Система контроля доступа
vServer включает гибкую систему контроля доступа **vAccess** для сайтов и прокси-сервисов.
### 📁 Расположение конфигураций
**Для сайтов:**
```
WebServer/www/{host}/vAccess.conf
```
**Для прокси:**
```
WebServer/tools/Proxy_vAccess/{domain}_vAccess.conf
```
### ⚙️ Основные возможности
-**IP-фильтрация** - разрешение/блокировка по IP адресам
-**Контроль путей** - ограничение доступа к определённым директориям
-**Фильтрация файлов** - блокировка по расширениям (*.php, *.exe)
-**Исключения** - гибкие правила с exceptions_dir
-**Кастомные ошибки** - редиректы или страницы ошибок
### 📝 Пример конфигурации
```conf
# Разрешаем админку только с локальных IP
type: Allow
path_access: /admin/*, /api/admin/*
ip_list: 127.0.0.1, 192.168.1.100
url_error: 404
# Блокируем опасные файлы в uploads
type: Disable
type_file: *.php, *.exe, *.sh
path_access: /uploads/*
url_error: 404
```
### 📚 Документация
Подробная документация по vAccess:
- **Для сайтов:** см. `WebServer/www/{host}/vAccess.conf` (примеры в файле)
- **Для прокси:** см. `WebServer/tools/Proxy_vAccess/README.md`
## 📝 Логирование
Все логи сохраняются в `WebServer/tools/logs/`:
@@ -149,7 +238,8 @@ go build -o MyApp.exe
- 🗄️ `logs_mysql.log` - MySQL операции
- 🐘 `logs_php.log` - PHP ошибки
- ⚙️ `logs_config.log` - Конфигурация
- 🔒 `logs_vaccess.log` - Контроль доступа
- 🔐 `logs_vaccess.log` - Контроль доступа для сайтов
- 🔐 `logs_vaccess_proxy.log` - Контроль доступа для прокси
## 🔐 SSL Сертификаты

View File

@@ -16,7 +16,8 @@
"ExternalDomain": "git.example.ru",
"LocalAddress": "127.0.0.1",
"LocalPort": "3333",
"UseHTTPS": false
"ServiceHTTPSuse": false,
"AutoHTTPS": true
}
],

View File

@@ -0,0 +1,158 @@
# 🔒 vAccess для Proxy-сервиса
Система контроля доступа для прокси-сервера, аналогичная vAccess для веб-сайтов.
## 📁 Структура
```
Proxy_vAccess/
├── _example_vAccess.conf - Пример конфигурации с комментариями
├── {domain}_vAccess.conf - Конфигурация для конкретного домена
└── README.md - Эта документация
```
## 🚀 Как использовать
### 1. Создание конфигурации
Для каждого прокси-домена создайте файл:
```
{ExternalDomain}_vAccess.conf
```
**Пример:**
- `git.example.ru_vAccess.conf`
- `api.myservice.com_vAccess.conf`
- `proxy.domain.org_vAccess.conf`
### 2. Синтаксис конфигурации
Каждый блок правил начинается с комментария `#`:
```conf
# Описание правила
type: Allow | Disable
path_access: /path1/*, /path2/*
ip_list: 192.168.1.1, 127.0.0.1
exceptions_dir: /public/*
url_error: 404 | https://site.com | /error.html
```
## 📋 Параметры правил
| Параметр | Обязательный | Описание |
|----------|--------------|----------|
| `type` | ✅ Да | `Allow` - разрешить доступ, `Disable` - запретить |
| `type_file` | ❌ Нет | Расширения файлов через запятую (*.json, *.pdf) |
| `path_access` | ❌ Нет | Список путей через запятую |
| `ip_list` | ❌ Нет | Список IP адресов через запятую |
| `exceptions_dir` | ❌ Нет | Пути-исключения (правило не применяется) |
| `url_error` | ❌ Нет | Страница ошибки при блокировке |
## 🔍 Примеры использования
### Пример 1: Админка только с локальных IP
```conf
# Разрешаем админку только с определённых IP
type: Allow
path_access: /admin/*, /api/admin/*
ip_list: 127.0.0.1, 192.168.1.100
url_error: 404
```
### Пример 2: Блокировка определённых IP
```conf
# Блокируем подозрительные IP для всего сервиса
type: Disable
ip_list: 10.0.0.50, 192.168.1.200
url_error: https://example.com/blocked
```
### Пример 3: Защита внутренних API
```conf
# Блокируем доступ к служебным API
type: Disable
path_access: /internal/*, /private/*
exceptions_dir: /internal/health
url_error: 404
```
### Пример 4: Доступ только с локальной сети
```conf
# Разрешаем доступ к API только с локальных адресов
type: Allow
path_access: /api/*
ip_list: 127.0.0.1, ::1, 192.168.0.0/16
url_error: 404
```
### Пример 5: Блокировка опасных файлов
```conf
# Блокируем исполняемые файлы через прокси
type: Disable
type_file: *.php, *.exe, *.sh, *.bat
path_access: /uploads/*, /files/*
url_error: 404
```
### Пример 6: Разрешить только определённые типы
```conf
# В API разрешаем только JSON и XML
type: Allow
type_file: *.json, *.xml, no_extension
path_access: /api/*
url_error: 404
```
### Пример 7: Блокировка архивов
```conf
# Блокируем скачивание архивов с приватных путей
type: Disable
type_file: *.zip, *.rar, *.7z, *.tar.gz
path_access: /downloads/private/*
exceptions_dir: /downloads/private/public/*
url_error: https://example.com/access-denied
```
## ⚙️ Логика работы
1. **Порядок проверки:** правила проверяются сверху вниз
2. **Первое совпадение:** первое подходящее правило срабатывает
3. **Нет конфига = доступ разрешён:** если файла нет, доступ не ограничен
4. **Allow правило:** доступ разрешён ТОЛЬКО если ВСЕ условия выполнены
5. **Disable правило:** доступ запрещён если ЛЮБОЕ условие выполнено
## 📊 Логирование
Все события vAccess для прокси записываются в:
```
WebServer/tools/logs/logs_vaccess_proxy.log
```
Формат логов:
```
[2025-10-03 00:45:23] [vAccess-Proxy] 🚫 Доступ запрещён для 192.168.1.50 к git.example.ru/admin
```
## 🔗 Связь с основной системой
vAccess для прокси использует **ту же кодовую базу**, что и vAccess для сайтов:
- Все функции парсинга, проверки IP, путей общие
- Изменения в логике автоматически применяются везде
- Единый стиль конфигурации
## ⚠️ Важные замечания
1. **IP-адреса берутся из соединения** (не из заголовков прокси)
2. **Порядок правил важен** - специфичные правила размещайте ВЫШЕ общих
3. **Проверка расширений работает** - можно фильтровать по type_file даже для прокси
4. **Поддержка подсетей:** можно использовать CIDR нотацию (192.168.0.0/24)
5. **Множественные расширения:** поддержка файлов типа .tar.gz, .backup.sql
## 📞 Поддержка
При ошибках парсинга конфигурации:
1. Проверьте синтаксис в `_example_vAccess.conf`
2. Убедитесь что у правила есть `type:` и хотя бы одно условие
3. Посмотрите логи в `logs_vaccess_proxy.log`

View File

@@ -0,0 +1,16 @@
# ========================================
# vAccess Configuration для git.example.ru
# Система контроля доступа для прокси-сервиса
# ========================================
# Правило 1: Разрешаем доступ к админке только с конкретныхлокальных адресов
type: Allow
path_access: /admin/*, /-/admin/*
ip_list: 127.0.0.1, 192.168.1.0
url_error: 404
# Правило 2: Блокируем доступ к служебным API
type: Disable
path_access: /api/internal/*, /-/metrics/*
url_error: 404