mirror of
https://github.com/acepanel/acepanel.github.io.git
synced 2026-02-04 09:13:09 +08:00
refactor: 为翻译做准备
This commit is contained in:
661
en/advanced/api.md
Normal file
661
en/advanced/api.md
Normal file
@@ -0,0 +1,661 @@
|
||||
# API 参考文档
|
||||
|
||||
## 概述
|
||||
|
||||
耗子面板提供了一套安全的 RESTful 接口,用于与面板系统进行交互。所有 API 请求都需要进行 HMAC-SHA256 签名认证以确保通信的安全性和完整性。
|
||||
|
||||
## 基础信息
|
||||
|
||||
- **基础 URL**: `http(s)://your-panel-domain/{entry}/api/`
|
||||
- **内容类型**: 所有请求和响应均使用 `application/json`
|
||||
- **字符编码**: UTF-8
|
||||
|
||||
## 认证机制
|
||||
|
||||
API 使用 HMAC-SHA256 签名算法进行认证。每个请求必须包含以下 HTTP 头:
|
||||
|
||||
| 头部名称 | 描述 |
|
||||
|-----------------|-----------------------------------------------------------------|
|
||||
| `Content-Type` | 设置为 `application/json` |
|
||||
| `X-Timestamp` | 当前 UNIX 时间戳(秒) |
|
||||
| `Authorization` | 身份验证信息,格式为 `HMAC-SHA256 Credential={id}, Signature={signature}` |
|
||||
|
||||
## 签名算法
|
||||
|
||||
签名过程包含四个主要步骤:
|
||||
|
||||
### 1. 构造规范化请求
|
||||
|
||||
规范化请求字符串由以下部分组成,各部分之间使用换行符(\n)分隔:
|
||||
|
||||
```
|
||||
HTTP方法
|
||||
规范化路径
|
||||
规范化查询字符串
|
||||
请求体的SHA256哈希值
|
||||
```
|
||||
|
||||
**注意**:规范化路径应始终使用 `/api/` 开头的路径部分,忽略入口前缀。
|
||||
|
||||
### 2. 构造待签名字符串
|
||||
|
||||
待签名字符串包含以下部分,各部分使用换行符(\n)分隔:
|
||||
|
||||
```
|
||||
"HMAC-SHA256"
|
||||
时间戳
|
||||
规范化请求的SHA256哈希值
|
||||
```
|
||||
|
||||
### 3. 计算签名
|
||||
|
||||
使用您的令牌(token)对待签名字符串进行 HMAC-SHA256 计算,然后将结果转换为十六进制字符串。
|
||||
|
||||
### 4. 构造授权头
|
||||
|
||||
将计算得到的签名添加到 `Authorization` 头:
|
||||
|
||||
```
|
||||
Authorization: HMAC-SHA256 Credential={id}, Signature={signature}
|
||||
```
|
||||
|
||||
## Go 示例
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 创建一个获取用户信息的请求
|
||||
req, err := http.NewRequest("GET", "http://example.com/entrance/api/user/info", nil)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating request:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 设置内容类型
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
// 签名请求 - 传入您的用户ID和API令牌
|
||||
if err = SignReq(req, uint(16), "YourSecretToken"); err != nil {
|
||||
fmt.Println("Error signing request:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Println("Error sending request:", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 处理响应
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
fmt.Println("Error reading response:", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Response Status:", resp.Status)
|
||||
fmt.Println("Response Body:", string(body))
|
||||
}
|
||||
|
||||
// SignReq 对HTTP请求进行签名
|
||||
func SignReq(req *http.Request, id uint, token string) error {
|
||||
// 步骤一:构造规范化请求
|
||||
var body []byte
|
||||
var err error
|
||||
|
||||
if req.Body != nil {
|
||||
// 读取并保存请求体
|
||||
body, err = io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 恢复请求体以便后续使用
|
||||
req.Body = io.NopCloser(bytes.NewReader(body))
|
||||
}
|
||||
|
||||
// 规范化路径
|
||||
canonicalPath := req.URL.Path
|
||||
if !strings.HasPrefix(canonicalPath, "/api") {
|
||||
index := strings.Index(canonicalPath, "/api")
|
||||
if index != -1 {
|
||||
canonicalPath = canonicalPath[index:]
|
||||
}
|
||||
}
|
||||
|
||||
canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s",
|
||||
req.Method,
|
||||
canonicalPath,
|
||||
req.URL.Query().Encode(),
|
||||
SHA256(string(body)))
|
||||
|
||||
// 步骤二:设置时间戳和构造待签名字符串
|
||||
timestamp := time.Now().Unix()
|
||||
req.Header.Set("X-Timestamp", fmt.Sprintf("%d", timestamp))
|
||||
|
||||
stringToSign := fmt.Sprintf("%s\n%d\n%s",
|
||||
"HMAC-SHA256",
|
||||
timestamp,
|
||||
SHA256(canonicalRequest))
|
||||
|
||||
// 步骤三:计算签名
|
||||
signature := HMACSHA256(stringToSign, token)
|
||||
|
||||
// 步骤四:设置Authorization头
|
||||
authHeader := fmt.Sprintf("HMAC-SHA256 Credential=%d, Signature=%s", id, signature)
|
||||
req.Header.Set("Authorization", authHeader)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SHA256(str string) string {
|
||||
sum := sha256.Sum256([]byte(str))
|
||||
dst := make([]byte, hex.EncodedLen(len(sum)))
|
||||
hex.Encode(dst, sum[:])
|
||||
return string(dst)
|
||||
}
|
||||
|
||||
func HMACSHA256(data string, secret string) string {
|
||||
h := hmac.New(sha256.New, []byte(secret))
|
||||
h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
```
|
||||
|
||||
## PHP 示例
|
||||
|
||||
```php
|
||||
<?php
|
||||
/**
|
||||
* 耗子面板 API 请求示例 (PHP)
|
||||
*/
|
||||
|
||||
function signRequest($method, $url, $body, $id, $token) {
|
||||
// 解析URL并获取路径
|
||||
$parsedUrl = parse_url($url);
|
||||
$path = $parsedUrl['path'];
|
||||
$query = isset($parsedUrl['query']) ? $parsedUrl['query'] : '';
|
||||
|
||||
// 规范化路径
|
||||
$canonicalPath = $path;
|
||||
if (strpos($path, '/api') !== 0) {
|
||||
$apiPos = strpos($path, '/api');
|
||||
if ($apiPos !== false) {
|
||||
$canonicalPath = substr($path, $apiPos);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算请求体的SHA256哈希值
|
||||
$bodySha256 = hash('sha256', $body ?: '');
|
||||
|
||||
// 构造规范化请求
|
||||
$canonicalRequest = implode("\n", [
|
||||
$method,
|
||||
$canonicalPath,
|
||||
$query,
|
||||
$bodySha256
|
||||
]);
|
||||
|
||||
// 获取当前时间戳
|
||||
$timestamp = time();
|
||||
|
||||
// 构造待签名字符串
|
||||
$stringToSign = implode("\n", [
|
||||
'HMAC-SHA256',
|
||||
$timestamp,
|
||||
hash('sha256', $canonicalRequest)
|
||||
]);
|
||||
|
||||
// 计算签名
|
||||
$signature = hash_hmac('sha256', $stringToSign, $token);
|
||||
|
||||
// 返回签名和时间戳
|
||||
return [
|
||||
'timestamp' => $timestamp,
|
||||
'signature' => $signature,
|
||||
'id' => $id
|
||||
];
|
||||
}
|
||||
|
||||
// 示例请求
|
||||
$apiUrl = 'http://example.com/entrance/api/user/info';
|
||||
$method = 'GET';
|
||||
$body = ''; // 对于GET请求,通常没有请求体
|
||||
$id = 16;
|
||||
$token = 'YourSecretToken';
|
||||
|
||||
// 生成签名信息
|
||||
$signingData = signRequest($method, $apiUrl, $body, $id, $token);
|
||||
|
||||
// 准备HTTP请求头
|
||||
$headers = [
|
||||
'Content-Type: application/json',
|
||||
'X-Timestamp: ' . $signingData['timestamp'],
|
||||
'Authorization: HMAC-SHA256 Credential=' . $signingData['id'] . ', Signature=' . $signingData['signature']
|
||||
];
|
||||
|
||||
// 使用cURL发送请求
|
||||
$ch = curl_init($apiUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
|
||||
if (!empty($body)) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
|
||||
// 执行请求并获取响应
|
||||
$response = curl_exec($ch);
|
||||
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
// 输出结果
|
||||
echo "响应状态码: " . $statusCode . PHP_EOL;
|
||||
echo "响应内容: " . $response . PHP_EOL;
|
||||
```
|
||||
|
||||
## Python 示例
|
||||
|
||||
```python
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import requests
|
||||
import time
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
||||
def sha256_hash(text):
|
||||
"""计算字符串的SHA256哈希值"""
|
||||
return hashlib.sha256(text.encode('utf-8')).hexdigest()
|
||||
|
||||
def hmac_sha256(key, message):
|
||||
"""使用HMAC-SHA256算法计算签名"""
|
||||
return hmac.new(key.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()
|
||||
|
||||
def sign_request(method, url, body, user_id, token):
|
||||
"""为API请求生成签名"""
|
||||
# 解析URL
|
||||
parsed_url = urlparse(url)
|
||||
path = parsed_url.path
|
||||
query = parsed_url.query
|
||||
|
||||
# 规范化路径
|
||||
canonical_path = path
|
||||
if not path.startswith('/api'):
|
||||
api_pos = path.find('/api')
|
||||
if api_pos != -1:
|
||||
canonical_path = path[api_pos:]
|
||||
|
||||
# 构造规范化请求
|
||||
body_str = body if body else ""
|
||||
canonical_request = "\n".join([
|
||||
method,
|
||||
canonical_path,
|
||||
query,
|
||||
sha256_hash(body_str)
|
||||
])
|
||||
|
||||
# 获取当前时间戳
|
||||
timestamp = int(time.time())
|
||||
|
||||
# 构造待签名字符串
|
||||
string_to_sign = "\n".join([
|
||||
"HMAC-SHA256",
|
||||
str(timestamp),
|
||||
sha256_hash(canonical_request)
|
||||
])
|
||||
|
||||
# 计算签名
|
||||
signature = hmac_sha256(token, string_to_sign)
|
||||
|
||||
return {
|
||||
"timestamp": timestamp,
|
||||
"signature": signature,
|
||||
"id": user_id
|
||||
}
|
||||
|
||||
# 示例请求
|
||||
api_url = "http://example.com/entrance/api/user/info"
|
||||
method = "GET"
|
||||
body = "" # GET请求通常没有请求体
|
||||
user_id = 16
|
||||
token = "YourSecretToken"
|
||||
|
||||
# 生成签名信息
|
||||
signing_data = sign_request(method, api_url, body, user_id, token)
|
||||
|
||||
# 准备HTTP请求头
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Timestamp": str(signing_data["timestamp"]),
|
||||
"Authorization": f"HMAC-SHA256 Credential={signing_data['id']}, Signature={signing_data['signature']}"
|
||||
}
|
||||
|
||||
# 发送请求
|
||||
response = requests.request(
|
||||
method=method,
|
||||
url=api_url,
|
||||
headers=headers,
|
||||
data=body
|
||||
)
|
||||
|
||||
# 输出结果
|
||||
print(f"响应状态码: {response.status_code}")
|
||||
print(f"响应内容: {response.text}")
|
||||
```
|
||||
|
||||
## Java 示例
|
||||
|
||||
```java
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.time.Instant;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* 耗子面板 API 请求示例 (Java)
|
||||
*/
|
||||
public class RatPanelApiExample {
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
// 示例请求
|
||||
String apiUrl = "http://example.com/entrance/api/user/info";
|
||||
String method = "GET";
|
||||
String body = ""; // 对于GET请求,通常没有请求体
|
||||
int id = 16;
|
||||
String token = "YourSecretToken";
|
||||
|
||||
// 生成签名信息
|
||||
SigningData signingData = signRequest(method, apiUrl, body, id, token);
|
||||
|
||||
// 准备HTTP请求
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
|
||||
.uri(URI.create(apiUrl))
|
||||
.header("Content-Type", "application/json")
|
||||
.header("X-Timestamp", String.valueOf(signingData.timestamp))
|
||||
.header("Authorization", "HMAC-SHA256 Credential=" + signingData.id +
|
||||
", Signature=" + signingData.signature);
|
||||
|
||||
// 设置请求方法和请求体
|
||||
if (method.equals("GET")) {
|
||||
requestBuilder.GET();
|
||||
} else {
|
||||
requestBuilder.method(method, HttpRequest.BodyPublishers.ofString(body));
|
||||
}
|
||||
|
||||
HttpRequest request = requestBuilder.build();
|
||||
|
||||
// 发送请求
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
// 输出结果
|
||||
System.out.println("响应状态码: " + response.statusCode());
|
||||
System.out.println("响应内容: " + response.body());
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static class SigningData {
|
||||
long timestamp;
|
||||
String signature;
|
||||
int id;
|
||||
|
||||
SigningData(long timestamp, String signature, int id) {
|
||||
this.timestamp = timestamp;
|
||||
this.signature = signature;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
public static SigningData signRequest(String method, String url, String body, int id, String token) throws Exception {
|
||||
// 解析URL
|
||||
URI uri = new URI(url);
|
||||
String path = uri.getPath();
|
||||
String query = uri.getQuery() != null ? uri.getQuery() : "";
|
||||
|
||||
// 规范化路径
|
||||
String canonicalPath = path;
|
||||
if (!path.startsWith("/api")) {
|
||||
int apiPos = path.indexOf("/api");
|
||||
if (apiPos != -1) {
|
||||
canonicalPath = path.substring(apiPos);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算请求体的SHA256哈希值
|
||||
String bodySha256 = sha256Hash(body != null ? body : "");
|
||||
|
||||
// 构造规范化请求
|
||||
String canonicalRequest = String.join("\n",
|
||||
method,
|
||||
canonicalPath,
|
||||
query,
|
||||
bodySha256);
|
||||
|
||||
// 获取当前时间戳
|
||||
long timestamp = Instant.now().getEpochSecond();
|
||||
|
||||
// 构造待签名字符串
|
||||
String stringToSign = String.join("\n",
|
||||
"HMAC-SHA256",
|
||||
String.valueOf(timestamp),
|
||||
sha256Hash(canonicalRequest));
|
||||
|
||||
// 计算签名
|
||||
String signature = hmacSha256(token, stringToSign);
|
||||
|
||||
// 返回签名和时间戳
|
||||
return new SigningData(timestamp, signature, id);
|
||||
}
|
||||
|
||||
private static String sha256Hash(String text) throws Exception {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
|
||||
return bytesToHex(hash);
|
||||
}
|
||||
|
||||
private static String hmacSha256(String key, String message) throws Exception {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
|
||||
mac.init(secretKeySpec);
|
||||
byte[] hash = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
|
||||
return bytesToHex(hash);
|
||||
}
|
||||
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : bytes) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append('0');
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Node.js 示例
|
||||
|
||||
```javascript
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
const url = require('url');
|
||||
|
||||
/**
|
||||
* 计算字符串的SHA256哈希值
|
||||
* @param {string} text 待哈希的字符串
|
||||
* @returns {string} 哈希结果(十六进制)
|
||||
*/
|
||||
function sha256Hash(text) {
|
||||
return crypto.createHash('sha256').update(text || '').digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用HMAC-SHA256算法计算签名
|
||||
* @param {string} key 密钥
|
||||
* @param {string} message 待签名的消息
|
||||
* @returns {string} 签名结果(十六进制)
|
||||
*/
|
||||
function hmacSha256(key, message) {
|
||||
return crypto.createHmac('sha256', key).update(message).digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* 为API请求生成签名
|
||||
* @param {string} method HTTP方法
|
||||
* @param {string} apiUrl API地址
|
||||
* @param {string} body 请求体
|
||||
* @param {number} id 用户ID
|
||||
* @param {string} token 密钥
|
||||
* @returns {object} 包含签名、时间戳和ID的对象
|
||||
*/
|
||||
function signRequest(method, apiUrl, body, id, token) {
|
||||
// 解析URL
|
||||
const parsedUrl = new url.URL(apiUrl);
|
||||
const path = parsedUrl.pathname;
|
||||
const query = parsedUrl.search.slice(1); // 移除开头的'?'
|
||||
|
||||
// 规范化路径
|
||||
let canonicalPath = path;
|
||||
if (!path.startsWith('/api')) {
|
||||
const apiPos = path.indexOf('/api');
|
||||
if (apiPos !== -1) {
|
||||
canonicalPath = path.slice(apiPos);
|
||||
}
|
||||
}
|
||||
|
||||
// 构造规范化请求
|
||||
const canonicalRequest = [
|
||||
method,
|
||||
canonicalPath,
|
||||
query,
|
||||
sha256Hash(body || '')
|
||||
].join('\n');
|
||||
|
||||
// 获取当前时间戳
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
|
||||
// 构造待签名字符串
|
||||
const stringToSign = [
|
||||
'HMAC-SHA256',
|
||||
timestamp,
|
||||
sha256Hash(canonicalRequest)
|
||||
].join('\n');
|
||||
|
||||
// 计算签名
|
||||
const signature = hmacSha256(token, stringToSign);
|
||||
|
||||
return {
|
||||
timestamp,
|
||||
signature,
|
||||
id
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送API请求
|
||||
*/
|
||||
async function sendApiRequest() {
|
||||
// 示例请求参数
|
||||
const apiUrl = 'http://example.com/entrance/api/user/info';
|
||||
const method = 'GET';
|
||||
const body = ''; // GET请求通常没有请求体
|
||||
const id = 16;
|
||||
const token = 'YourSecretToken';
|
||||
|
||||
try {
|
||||
// 生成签名信息
|
||||
const signingData = signRequest(method, apiUrl, body, id, token);
|
||||
|
||||
// 准备HTTP请求头
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Timestamp': signingData.timestamp,
|
||||
'Authorization': `HMAC-SHA256 Credential=${signingData.id}, Signature=${signingData.signature}`
|
||||
};
|
||||
|
||||
// 发送请求
|
||||
const response = await axios({
|
||||
method,
|
||||
url: apiUrl,
|
||||
headers,
|
||||
data: body || undefined
|
||||
});
|
||||
|
||||
// 输出结果
|
||||
console.log(`响应状态码: ${response.status}`);
|
||||
console.log(`响应内容: ${JSON.stringify(response.data)}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('请求出错:', error.message);
|
||||
if (error.response) {
|
||||
console.error(`响应状态码: ${error.response.status}`);
|
||||
console.error(`响应内容: ${JSON.stringify(error.response.data)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行请求
|
||||
sendApiRequest();
|
||||
```
|
||||
|
||||
## 常见响应码
|
||||
|
||||
| HTTP 状态码 | 描述 |
|
||||
|----------|---------|
|
||||
| 200 | 请求成功 |
|
||||
| 401 | 身份验证失败 |
|
||||
| 403 | 权限不足 |
|
||||
| 404 | 资源不存在 |
|
||||
| 422 | 请求参数错误 |
|
||||
| 500 | 服务器内部错误 |
|
||||
|
||||
## 安全建议
|
||||
|
||||
1. **保护您的 API 令牌**:不要在客户端代码中硬编码或公开您的 API 令牌
|
||||
2. **定期轮换令牌**:定期更改您的 API 令牌以提高安全性
|
||||
3. **配置 IP 白名单**:在生产环境中使用 IP 白名单限制访问
|
||||
|
||||
## 常见问题解答
|
||||
|
||||
### 签名验证失败
|
||||
|
||||
如果遇到签名验证失败,请检查:
|
||||
|
||||
- 确保使用了正确的 API 令牌和 ID
|
||||
- 检查客户端与服务器的时间是否准确,时间戳偏差大于 300 秒会导致验证失败
|
||||
- 确保请求体在计算签名前后没有被修改
|
||||
- 确保 URL 路径处理正确,注意规范化路径时需要移除入口前缀
|
||||
|
||||
### 请求超时
|
||||
|
||||
- 检查网络连接
|
||||
- 确认服务器状态
|
||||
- 考虑增加客户端的超时设置
|
||||
32
en/advanced/hub-mirror.md
Normal file
32
en/advanced/hub-mirror.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# 配置容器镜像加速
|
||||
|
||||
由于一些原因国内可能无法连接到 Docker Hub 拉取容器镜像,因此需要配置镜像加速。
|
||||
|
||||
## 对于 Podman
|
||||
|
||||
在面板打开 Podman 设置页面,导航到注册表配置选项卡。
|
||||
|
||||
滚动到配置文件底部,添加如下配置并保存:
|
||||
|
||||
```
|
||||
[[registry]]
|
||||
location = "docker.io"
|
||||
[[registry.mirror]]
|
||||
location = "docker.1ms.run"
|
||||
```
|
||||
|
||||
其中 docker.1ms.run 为配置的镜像加速地址,可自行参考其他教程搭建使用。
|
||||
|
||||
## 对于 Docker
|
||||
|
||||
在面板打开 Docker 设置页面,导航到配置选项卡。
|
||||
|
||||
添加如下配置并保存:
|
||||
|
||||
```
|
||||
{
|
||||
"registry-mirrors": ["https://docker.1ms.run"]
|
||||
}
|
||||
```
|
||||
|
||||
其中 https://docker.1ms.run 为配置的镜像加速地址,可自行参考其他教程搭建使用。
|
||||
5
en/advanced/proxy.md
Normal file
5
en/advanced/proxy.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 配置反向代理
|
||||
|
||||
面板 v2.4.10+ 自带反向代理配置生成器,你可以通过站点伪静态配置页面的右上角打开使用。
|
||||
|
||||
注意:如果设置反向代理后出现 CSS/JS 等静态资源无法正常加载的问题,请移除站点主配置文件中的**不记录静态文件日志**部分。
|
||||
14
en/advanced/quic.md
Normal file
14
en/advanced/quic.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# 配置 QUIC(HTTP3)
|
||||
|
||||
面板目前已支持自动 QUIC 配置,但是出于兼容性考虑,默认未添加 `Alt-Svc` 标头,浏览器在未检测到 `Alt-Svc` 标头时不会尝试使用 QUIC 连接。
|
||||
|
||||
如果你不使用 CDN,可添加下述配置到网站伪静态中即可让浏览器知晓网站支持并使用 QUIC 连接。
|
||||
|
||||
```
|
||||
add_header Alt-Svc 'h3=":$server_port"; ma=2592000';
|
||||
```
|
||||
|
||||
如果你使用 CDN 或者前端还存在代理服务器,则 QUIC 需要在 CDN / 前端开启。
|
||||
如果配置后仍不生效,请检查浏览器版本和 UDP 443 端口的可用性。
|
||||
|
||||
* 根据 Nginx 的 git 提交记录,1.25 版本下所有 QUIC 草案版本已经移除,因此 `Alt-Svc` 无需添加草案版本号。
|
||||
32
en/advanced/safe.md
Normal file
32
en/advanced/safe.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# 安全性建议
|
||||
|
||||
通过以下安全措施,几乎可以杜绝一切被黑/挂马问题。
|
||||
|
||||
### 网站方面
|
||||
|
||||
根据以往经验大多数被黑挂马都是程序漏洞造成的,与面板等环境无关,为了网站安全,你应该做到:
|
||||
|
||||
1. 不要使用盗版程序、软件,特别是在你无法确定有没有被人加过料的情况下。
|
||||
2. 定期更新网站程序和软件环境,不要怕麻烦使用过时的软件程序,它们的安全性无法保证。
|
||||
3. 网站后台禁止使用弱密码,密码强烈建议使用随机生成器生成大于 20 位的混合密码并保存在安全位置,有条件建议开启程序的多因素身份验证(2FA)。
|
||||
4. 设置定时备份全站数据,不要裸奔。
|
||||
5. PHP 默认禁用了部分高危函数,非必要请勿删除。
|
||||
|
||||
### 系统方面
|
||||
|
||||
现代系统出现严重安全漏洞的概率是很低的,但是你仍应该做到:
|
||||
|
||||
1. 定期更新系统软件。(使用 `yum update` 或 `apt upgrade`)。
|
||||
2. SSH 禁止使用弱密码和默认 22 端口,密码强烈建议使用随机生成器生成大于 20 位的混合密码并保存在安全位置,如果可以建议安装 Fail2ban 针对性保护。
|
||||
3. 不要随意给 777 权限和 www 用户的执行权限,可能造成极大安全隐患。
|
||||
4. 如果运营商提供 VNC 管理服务器,也可以考虑关闭 SSH,从源头上解决问题。
|
||||
|
||||
### 面板方面
|
||||
|
||||
面板拥有和 root 一样的权限,管理不当亦会造成严重安全问题,你应该做到:
|
||||
|
||||
1. 定期更新面板及面板安装的应用。同时推荐关注我们的频道或者群,以第一时间接收各类更新消息。
|
||||
2. 面板禁止使用弱密码和默认 8888 端口,密码强烈建议使用随机生成器生成大于 20 位的混合密码并保存在安全位置。
|
||||
3. 建议修改添加面板入口和开启面板 HTTPS,防止被扫描器扫描和中间人攻击。
|
||||
4. 防火墙无必要请不要放行内部服务的端口(Redis 6379、MySQL 3306、PostgreSQL 5432等),可能造成严重安全隐患。(网站本地连接不需要放行,连不上是程序的问题)。
|
||||
5. 对安全性要求较高的情况下,可以考虑日常停止面板的运行,按需启动(面板停止运行不会影响网站、计划任务等的运行)。
|
||||
5
en/advanced/supervisor.md
Normal file
5
en/advanced/supervisor.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 配置进程守护
|
||||
|
||||
1. 安装 Supervisor 管理器并打开。
|
||||
2. Supervisor 管理器中创建需要守护的进程(运行用户不建议使用 root)。
|
||||
3. 常见问题:[https://tom.moe/t/supervisor/3112](https://tom.moe/t/supervisor/3112)
|
||||
11
en/advanced/tls.md
Normal file
11
en/advanced/tls.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# 配置 TLSv1.1 TLSv1
|
||||
|
||||
当前面板OpenResty使用OpenSSL 3.0版本编译,默认禁用已弃用的TLSv1.1 TLSv1协议。
|
||||
|
||||
当然,如果你的业务必须要使用这两个协议的话,可以使用下述SSL配置启用。
|
||||
|
||||
```
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:@SECLEVEL=0;
|
||||
ssl_prefer_server_ciphers on;
|
||||
```
|
||||
Reference in New Issue
Block a user