mirror of
https://github.com/MengMengCode/CLICD.git
synced 2026-08-08 14:34:49 +08:00
Merge branch 'main' of https://github.com/MengMengCode/CLICD
This commit is contained in:
+176
-2
@@ -10,6 +10,7 @@ README.md
|
|||||||
handlers/
|
handlers/
|
||||||
webssh.php
|
webssh.php
|
||||||
templates/
|
templates/
|
||||||
|
firewall.html
|
||||||
info.html
|
info.html
|
||||||
nat.html
|
nat.html
|
||||||
```
|
```
|
||||||
@@ -93,11 +94,12 @@ Content-Type: application/json
|
|||||||
|
|
||||||
## 客户区页面
|
## 客户区页面
|
||||||
|
|
||||||
模块提供两个客户区选项卡:
|
模块提供三个客户区选项卡:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
实例信息
|
实例信息
|
||||||
NAT转发
|
NAT转发
|
||||||
|
防火墙
|
||||||
```
|
```
|
||||||
|
|
||||||
客户区按钮提供:
|
客户区按钮提供:
|
||||||
@@ -197,6 +199,65 @@ DELETE /api/v1/containers/{id}/port-mappings/{index}
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 防火墙
|
||||||
|
|
||||||
|
防火墙是独立客户区页面,支持:
|
||||||
|
|
||||||
|
- 查看防火墙启用状态、默认动作和规则列表
|
||||||
|
- 启用 / 停用防火墙
|
||||||
|
- 设置默认动作:未匹配拒绝或未匹配放行
|
||||||
|
- 添加规则
|
||||||
|
- 编辑规则
|
||||||
|
- 删除规则
|
||||||
|
- 单独启用 / 停用某条规则
|
||||||
|
|
||||||
|
页面会先在前端修改规则列表和开关状态,点击“保存设置”后才统一同步到 CLICD。这样可以避免每次切换开关、修改默认动作或编辑规则时都立即请求后端,减少客户区卡顿。
|
||||||
|
|
||||||
|
注意:防火墙关闭时也可以保存规则;关闭只表示暂时不接管该容器流量,不代表规则必须清空。
|
||||||
|
|
||||||
|
使用的 CLICD API:
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /api/v1/containers/{id}/firewall
|
||||||
|
PUT /api/v1/containers/{id}/firewall
|
||||||
|
```
|
||||||
|
|
||||||
|
更新防火墙时必须使用 JSON 请求体,例如:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"default_action": "ACCEPT",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"network": "ipv4",
|
||||||
|
"direction": "in",
|
||||||
|
"protocol": "tcp",
|
||||||
|
"port": "22",
|
||||||
|
"source_ip": "",
|
||||||
|
"action": "ACCEPT",
|
||||||
|
"description": "Allow SSH",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
规则字段说明:
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `network` | 网络范围,常用 `ipv4`,也支持 `ipv6` / `all` |
|
||||||
|
| `direction` | 方向,`in` 入站,`out` 出站 |
|
||||||
|
| `protocol` | 协议,`tcp` 或 `udp` |
|
||||||
|
| `port` | 端口,可填写单端口、逗号分隔端口或端口段,例如 `22`、`80,443`、`8000-9000` |
|
||||||
|
| `source_ip` | 来源 IP / CIDR,留空表示任意来源 |
|
||||||
|
| `action` | 动作,`ACCEPT` 放行,`DROP` 拒绝 |
|
||||||
|
| `description` | 规则描述 |
|
||||||
|
| `enabled` | 是否启用该规则 |
|
||||||
|
|
||||||
|
IPv4 NAT 入站规则的端口按容器内部端口匹配,不是宿主机公网端口。例如公网 `22023 -> 容器 22`,防火墙规则端口应填写 `22`。
|
||||||
## WebSSH
|
## WebSSH
|
||||||
|
|
||||||
WebSSH 按钮会调用:
|
WebSSH 按钮会调用:
|
||||||
@@ -252,6 +313,8 @@ https://www.example.com
|
|||||||
| 变更资源 | `PUT /api/v1/containers/{name}/resource-limit` |
|
| 变更资源 | `PUT /api/v1/containers/{name}/resource-limit` |
|
||||||
| 变更流量 | `PUT /api/v1/containers/{name}/traffic-limit` |
|
| 变更流量 | `PUT /api/v1/containers/{name}/traffic-limit` |
|
||||||
| 同步到期 | `PUT /api/v1/containers/{name}/expiry` |
|
| 同步到期 | `PUT /api/v1/containers/{name}/expiry` |
|
||||||
|
| 查询防火墙 | `GET /api/v1/containers/{id}/firewall` |
|
||||||
|
| 更新防火墙 | `PUT /api/v1/containers/{id}/firewall` |
|
||||||
| WebSSH | `POST /api/v1/ssh-ticket` |
|
| WebSSH | `POST /api/v1/ssh-ticket` |
|
||||||
|
|
||||||
## 建议 API 权限
|
## 建议 API 权限
|
||||||
@@ -269,6 +332,7 @@ container:password
|
|||||||
container:traffic
|
container:traffic
|
||||||
container:resize
|
container:resize
|
||||||
container:port
|
container:port
|
||||||
|
container:firewall
|
||||||
task:read
|
task:read
|
||||||
ssh-ticket:create
|
ssh-ticket:create
|
||||||
```
|
```
|
||||||
@@ -316,6 +380,22 @@ curl --location --request PUT \
|
|||||||
--data-raw '{"container_port":8081,"host_port":61320,"protocol":"tcp","description":"HTTP"}'
|
--data-raw '{"container_port":8081,"host_port":61320,"protocol":"tcp","description":"HTTP"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
查询防火墙:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "X-API-Key: clicd_sk_xxxx" \
|
||||||
|
https://0.0.0.0:8999/api/v1/containers/10/firewall
|
||||||
|
```
|
||||||
|
|
||||||
|
更新防火墙:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl --location --request PUT \
|
||||||
|
"https://0.0.0.0:8999/api/v1/containers/10/firewall" \
|
||||||
|
--header "X-API-Key: clicd_sk_xxxx" \
|
||||||
|
--header "Content-Type: application/json" \
|
||||||
|
--data-raw '{"enabled":true,"default_action":"ACCEPT","rules":[{"id":"","network":"ipv4","direction":"in","protocol":"tcp","port":"22","source_ip":"","action":"ACCEPT","description":"Allow SSH","enabled":true}]}'
|
||||||
|
```
|
||||||
创建 WebSSH 票据:
|
创建 WebSSH 票据:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -336,6 +416,41 @@ curl --location --request POST \
|
|||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 防火墙获取提示“不支持的方法”
|
||||||
|
|
||||||
|
请确认模块版本已经包含防火墙页签修复。客户区防火墙列表应通过模块公开的 `firewallList` 调用,再由模块向 CLICD 发起:
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /api/v1/containers/{id}/firewall
|
||||||
|
```
|
||||||
|
|
||||||
|
如果页面或二开代码直接把读取请求改成 `POST /api/v1/containers/{id}/firewall`,CLICD 会返回“不支持的方法”。
|
||||||
|
|
||||||
|
### 防火墙保存后规则为空
|
||||||
|
|
||||||
|
请确认更新接口最终发往 CLICD 的请求体是 JSON,并且包含 `rules` 数组。防火墙关闭时也可以保存规则,`enabled: false` 不应自动清空 `rules`。
|
||||||
|
|
||||||
|
正确请求体示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"enabled": false,
|
||||||
|
"default_action": "ACCEPT",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"network": "ipv4",
|
||||||
|
"direction": "in",
|
||||||
|
"protocol": "tcp",
|
||||||
|
"port": "22",
|
||||||
|
"source_ip": "",
|
||||||
|
"action": "ACCEPT",
|
||||||
|
"description": "Allow SSH",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
### 图表刚打开只有一条横线
|
### 图表刚打开只有一条横线
|
||||||
|
|
||||||
CLICD 当前用量接口返回的是实时值,不是历史序列。页面刚打开时只有一个采样点,所以会显示当前值横线。选择 `10 秒` 自动刷新或点击“立即刷新”多采样几次后,会逐步形成折线。
|
CLICD 当前用量接口返回的是实时值,不是历史序列。页面刚打开时只有一个采样点,所以会显示当前值横线。选择 `10 秒` 自动刷新或点击“立即刷新”多采样几次后,会逐步形成折线。
|
||||||
@@ -344,7 +459,66 @@ CLICD 当前用量接口返回的是实时值,不是历史序列。页面刚
|
|||||||
|
|
||||||
旧版本只显示 GB,小流量换算后会被四舍五入成 `0 GB`。当前版本已改为智能单位,会显示 B / KB / MB / GB。
|
旧版本只显示 GB,小流量换算后会被四舍五入成 `0 GB`。当前版本已改为智能单位,会显示 B / KB / MB / GB。
|
||||||
|
|
||||||
### WebSSH 打不开或提示不安全 WebSocket
|
### 防火墙
|
||||||
|
|
||||||
|
防火墙是独立客户区页面,支持:
|
||||||
|
|
||||||
|
- 查看防火墙启用状态、默认动作和规则列表
|
||||||
|
- 启用 / 停用防火墙
|
||||||
|
- 设置默认动作:未匹配拒绝或未匹配放行
|
||||||
|
- 添加规则
|
||||||
|
- 编辑规则
|
||||||
|
- 删除规则
|
||||||
|
- 单独启用 / 停用某条规则
|
||||||
|
|
||||||
|
页面会先在前端修改规则列表和开关状态,点击“保存设置”后才统一同步到 CLICD。这样可以避免每次切换开关、修改默认动作或编辑规则时都立即请求后端,减少客户区卡顿。
|
||||||
|
|
||||||
|
注意:防火墙关闭时也可以保存规则;关闭只表示暂时不接管该容器流量,不代表规则必须清空。
|
||||||
|
|
||||||
|
使用的 CLICD API:
|
||||||
|
|
||||||
|
```text
|
||||||
|
GET /api/v1/containers/{id}/firewall
|
||||||
|
PUT /api/v1/containers/{id}/firewall
|
||||||
|
```
|
||||||
|
|
||||||
|
更新防火墙时必须使用 JSON 请求体,例如:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"default_action": "ACCEPT",
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"id": "",
|
||||||
|
"network": "ipv4",
|
||||||
|
"direction": "in",
|
||||||
|
"protocol": "tcp",
|
||||||
|
"port": "22",
|
||||||
|
"source_ip": "",
|
||||||
|
"action": "ACCEPT",
|
||||||
|
"description": "Allow SSH",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
规则字段说明:
|
||||||
|
|
||||||
|
| 字段 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `network` | 网络范围,常用 `ipv4`,也支持 `ipv6` / `all` |
|
||||||
|
| `direction` | 方向,`in` 入站,`out` 出站 |
|
||||||
|
| `protocol` | 协议,`tcp` 或 `udp` |
|
||||||
|
| `port` | 端口,可填写单端口、逗号分隔端口或端口段,例如 `22`、`80,443`、`8000-9000` |
|
||||||
|
| `source_ip` | 来源 IP / CIDR,留空表示任意来源 |
|
||||||
|
| `action` | 动作,`ACCEPT` 放行,`DROP` 拒绝 |
|
||||||
|
| `description` | 规则描述 |
|
||||||
|
| `enabled` | 是否启用该规则 |
|
||||||
|
|
||||||
|
IPv4 NAT 入站规则的端口按容器内部端口匹配,不是宿主机公网端口。例如公网 `22023 -> 容器 22`,防火墙规则端口应填写 `22`。
|
||||||
|
## WebSSH 打不开或提示不安全 WebSocket
|
||||||
|
|
||||||
请确认 CLICD 面板已经启用 HTTPS/WSS,并且魔方服务器配置使用 HTTPS:
|
请确认 CLICD 面板已经启用 HTTPS/WSS,并且魔方服务器配置使用 HTTPS:
|
||||||
|
|
||||||
|
|||||||
+28
-7
@@ -40,7 +40,7 @@ function clicd_MetaData()
|
|||||||
'DisplayName' => 'CLICD 对接模块 by 欢-Huan and ChatGPT 5.5 and DeepSeek V4',
|
'DisplayName' => 'CLICD 对接模块 by 欢-Huan and ChatGPT 5.5 and DeepSeek V4',
|
||||||
'APIVersion' => '1.1',
|
'APIVersion' => '1.1',
|
||||||
'HelpDoc' => 'https://github.com/MengMengCode/CLICD',
|
'HelpDoc' => 'https://github.com/MengMengCode/CLICD',
|
||||||
'version' => '1.0.5',
|
'version' => '1.0.11',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +365,10 @@ function clicd_webssh_url($params, $ticket, $containerName)
|
|||||||
$host = parse_url($baseUrl, PHP_URL_HOST);
|
$host = parse_url($baseUrl, PHP_URL_HOST);
|
||||||
$port = parse_url($baseUrl, PHP_URL_PORT);
|
$port = parse_url($baseUrl, PHP_URL_PORT);
|
||||||
$wsBase = $scheme . '://' . $host . ($port ? ':' . $port : '');
|
$wsBase = $scheme . '://' . $host . ($port ? ':' . $port : '');
|
||||||
$wsUrl = $wsBase . '/api/ssh?container=' . rawurlencode((string)$containerName);
|
$wsUrl = $wsBase
|
||||||
|
. '/api/ssh?container=' . rawurlencode((string)$containerName)
|
||||||
|
. '&container_name=' . rawurlencode((string)$containerName)
|
||||||
|
. '&ticket=' . rawurlencode((string)$ticket);
|
||||||
|
|
||||||
$siteScheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
$siteScheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
||||||
$siteHost = $_SERVER['HTTP_HOST'] ?? '';
|
$siteHost = $_SERVER['HTTP_HOST'] ?? '';
|
||||||
@@ -374,6 +377,7 @@ function clicd_webssh_url($params, $ticket, $containerName)
|
|||||||
return $handler
|
return $handler
|
||||||
. '?ws=' . rawurlencode($wsUrl)
|
. '?ws=' . rawurlencode($wsUrl)
|
||||||
. '&protocol=' . rawurlencode('clicd-ticket.' . (string)$ticket)
|
. '&protocol=' . rawurlencode('clicd-ticket.' . (string)$ticket)
|
||||||
|
. '&ticket=' . rawurlencode((string)$ticket)
|
||||||
. '&container=' . rawurlencode((string)$containerName);
|
. '&container=' . rawurlencode((string)$containerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,9 +630,24 @@ function clicd_request_value($key, $default = '')
|
|||||||
|
|
||||||
function clicd_json_input()
|
function clicd_json_input()
|
||||||
{
|
{
|
||||||
|
$input = [];
|
||||||
|
if (!empty($_POST) && is_array($_POST)) {
|
||||||
|
$input = $_POST;
|
||||||
|
}
|
||||||
|
|
||||||
$raw = file_get_contents('php://input');
|
$raw = file_get_contents('php://input');
|
||||||
$data = json_decode((string)$raw, true);
|
$data = json_decode((string)$raw, true);
|
||||||
return is_array($data) ? $data : [];
|
if (is_array($data)) {
|
||||||
|
return array_merge($input, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form = [];
|
||||||
|
parse_str((string)$raw, $form);
|
||||||
|
if (!empty($form) && is_array($form)) {
|
||||||
|
return array_merge($input, $form);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $input;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clicd_param_value($data, $key, $default = '')
|
function clicd_param_value($data, $key, $default = '')
|
||||||
@@ -1404,7 +1423,13 @@ function clicd_ClientButton($params)
|
|||||||
|
|
||||||
function clicd_webssh($params)
|
function clicd_webssh($params)
|
||||||
{
|
{
|
||||||
|
$container = [];
|
||||||
$containerName = clicd_container_name($params);
|
$containerName = clicd_container_name($params);
|
||||||
|
clicd_container_api_id($params, $container);
|
||||||
|
if (!empty($container['name'])) {
|
||||||
|
$containerName = (string)$container['name'];
|
||||||
|
}
|
||||||
|
|
||||||
$res = clicd_request($params, '/api/v1/ssh-ticket', ['container_name' => $containerName], 'POST', 30);
|
$res = clicd_request($params, '/api/v1/ssh-ticket', ['container_name' => $containerName], 'POST', 30);
|
||||||
if (!clicd_success($res)) {
|
if (!clicd_success($res)) {
|
||||||
return ['status' => 'error', 'msg' => clicd_message($res, 'WebSSH ticket create failed')];
|
return ['status' => 'error', 'msg' => clicd_message($res, 'WebSSH ticket create failed')];
|
||||||
@@ -1689,7 +1714,3 @@ function clicd_ClientAreaOutput($params, $key)
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,13 @@
|
|||||||
$ws = isset($_GET['ws']) ? (string)$_GET['ws'] : (isset($_GET['amp;ws']) ? (string)$_GET['amp;ws'] : '');
|
$ws = isset($_GET['ws']) ? (string)$_GET['ws'] : (isset($_GET['amp;ws']) ? (string)$_GET['amp;ws'] : '');
|
||||||
$protocol = isset($_GET['protocol']) ? (string)$_GET['protocol'] : (isset($_GET['amp;protocol']) ? (string)$_GET['amp;protocol'] : '');
|
$protocol = isset($_GET['protocol']) ? (string)$_GET['protocol'] : (isset($_GET['amp;protocol']) ? (string)$_GET['amp;protocol'] : '');
|
||||||
$container = isset($_GET['container']) ? (string)$_GET['container'] : (isset($_GET['amp;container']) ? (string)$_GET['amp;container'] : '');
|
$container = isset($_GET['container']) ? (string)$_GET['container'] : (isset($_GET['amp;container']) ? (string)$_GET['amp;container'] : '');
|
||||||
|
$ticket = isset($_GET['ticket']) ? (string)$_GET['ticket'] : (isset($_GET['amp;ticket']) ? (string)$_GET['amp;ticket'] : '');
|
||||||
|
|
||||||
if ($ws === '' || $protocol === '') {
|
if ($protocol === '' && $ticket !== '') {
|
||||||
|
$protocol = 'clicd-ticket.' . $ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ws === '') {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
header('Content-Type: text/plain; charset=utf-8');
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
echo "Missing WebSSH parameters\n";
|
echo "Missing WebSSH parameters\n";
|
||||||
@@ -64,6 +69,7 @@ if ($ws === '' || $protocol === '') {
|
|||||||
(function(){
|
(function(){
|
||||||
var wsUrl = <?php echo json_encode($ws, JSON_UNESCAPED_SLASHES); ?>;
|
var wsUrl = <?php echo json_encode($ws, JSON_UNESCAPED_SLASHES); ?>;
|
||||||
var protocol = <?php echo json_encode($protocol, JSON_UNESCAPED_SLASHES); ?>;
|
var protocol = <?php echo json_encode($protocol, JSON_UNESCAPED_SLASHES); ?>;
|
||||||
|
var ticket = <?php echo json_encode($ticket, JSON_UNESCAPED_SLASHES); ?>;
|
||||||
var term = document.getElementById('term');
|
var term = document.getElementById('term');
|
||||||
var state = document.getElementById('state');
|
var state = document.getElementById('state');
|
||||||
var modeSelect = document.getElementById('send-mode');
|
var modeSelect = document.getElementById('send-mode');
|
||||||
@@ -189,8 +195,17 @@ if ($ws === '' || $protocol === '') {
|
|||||||
iostat.textContent = 'S' + sentCount + ' R' + recvCount + ' ' + stateText;
|
iostat.textContent = 'S' + sentCount + ' R' + recvCount + ' ' + stateText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function websocketProtocolValue(value) {
|
||||||
|
value = String(value || '');
|
||||||
|
return /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(value) ? value : '';
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
socket = new WebSocket(wsUrl, protocol);
|
var protocolValue = websocketProtocolValue(protocol);
|
||||||
|
if (!protocolValue && ticket) {
|
||||||
|
append('[WebSSH] 票据已通过 URL 参数传递,当前浏览器不会发送子协议。\n');
|
||||||
|
}
|
||||||
|
socket = protocolValue ? new WebSocket(wsUrl, protocolValue) : new WebSocket(wsUrl);
|
||||||
socket.binaryType = 'arraybuffer';
|
socket.binaryType = 'arraybuffer';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setState('err', '\nWebSocket 创建失败:' + e.message + '\n');
|
setState('err', '\nWebSocket 创建失败:' + e.message + '\n');
|
||||||
|
|||||||
+1059
-447
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=============================="
|
||||||
|
echo " Certbot (Snap) Auto Installer"
|
||||||
|
echo "=============================="
|
||||||
|
|
||||||
|
# 检测系统
|
||||||
|
if [ -f /etc/os-release ]; then
|
||||||
|
. /etc/os-release
|
||||||
|
OS=$ID
|
||||||
|
VER=$VERSION_ID
|
||||||
|
else
|
||||||
|
echo "无法识别系统版本"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "检测到系统: $OS"
|
||||||
|
|
||||||
|
install_snap_debian() {
|
||||||
|
apt update -y
|
||||||
|
apt install -y snapd
|
||||||
|
systemctl enable --now snapd.socket || true
|
||||||
|
|
||||||
|
# 修复 snap 路径
|
||||||
|
ln -sf /var/lib/snapd/snap /snap
|
||||||
|
|
||||||
|
# 安装 certbot
|
||||||
|
snap install --classic certbot
|
||||||
|
|
||||||
|
# 软链
|
||||||
|
ln -sf /snap/bin/certbot /usr/bin/certbot
|
||||||
|
}
|
||||||
|
|
||||||
|
install_snap_rhel() {
|
||||||
|
# 启用 EPEL(部分系统需要)
|
||||||
|
if command -v dnf >/dev/null 2>&1; then
|
||||||
|
dnf install -y epel-release || true
|
||||||
|
dnf install -y snapd
|
||||||
|
systemctl enable --now snapd.socket || true
|
||||||
|
else
|
||||||
|
yum install -y epel-release || true
|
||||||
|
yum install -y snapd
|
||||||
|
systemctl enable --now snapd.socket || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# snap 经典路径
|
||||||
|
ln -sf /var/lib/snapd/snap /snap
|
||||||
|
|
||||||
|
# 安装 certbot
|
||||||
|
snap install --classic certbot
|
||||||
|
|
||||||
|
# 软链
|
||||||
|
ln -sf /snap/bin/certbot /usr/bin/certbot
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$OS" in
|
||||||
|
ubuntu|debian)
|
||||||
|
install_snap_debian
|
||||||
|
;;
|
||||||
|
centos|rhel|almalinux|rocky)
|
||||||
|
install_snap_rhel
|
||||||
|
;;
|
||||||
|
fedora)
|
||||||
|
dnf install -y snapd
|
||||||
|
systemctl enable --now snapd.socket || true
|
||||||
|
ln -sf /var/lib/snapd/snap /snap
|
||||||
|
snap install --classic certbot
|
||||||
|
ln -sf /snap/bin/certbot /usr/bin/certbot
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "不支持的系统: $OS"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "=============================="
|
||||||
|
echo "安装完成!验证版本:"
|
||||||
|
certbot --version || true
|
||||||
|
echo "=============================="
|
||||||
Reference in New Issue
Block a user