security: rustdesk出库+ignore;nginx静态location补齐安全头(修add_header继承丢失);tasks.py原子写;backup.sh可选异地;拍板清单落盘
vscode-planning-ci / planning-smoke (push) Canceled after 0s

This commit is contained in:
2026-09-25 04:27:42 +08:00
parent 54422f44ba
commit 02d27401b0
14 changed files with 73 additions and 749 deletions
+3 -1
View File
@@ -6,6 +6,9 @@
(服务器数据)重要!!!禁止上传git/ (服务器数据)重要!!!禁止上传git/
*.pem *.pem
id_ed25519* id_ed25519*
# 远控配置(含中转服务器地址与永久密码哈希,绝不入库)
rustdesk-config-export/
.dsh/ .dsh/
.dsh-runtime-backup/ .dsh-runtime-backup/
.pnpm-store/ .pnpm-store/
@@ -14,7 +17,6 @@ node_modules/
android-chunyu/ android-chunyu/
chunyu_project/ chunyu_project/
chunyu_project_react/ chunyu_project_react/
node_modules/
shots/ shots/
docker.env docker.env
backups/ backups/
+15 -3
View File
@@ -21,8 +21,10 @@ from __future__ import annotations
import argparse import argparse
import json import json
import os
import re import re
import sys import sys
import tempfile
from datetime import date from datetime import date
from pathlib import Path from pathlib import Path
@@ -56,9 +58,19 @@ def load_state() -> dict:
def save_state(state: dict) -> None: def save_state(state: dict) -> None:
STATE.write_text( # 原子写:先落同目录临时文件再 os.replace,避免并发写或中断产生半截 state.json
json.dumps(state, ensure_ascii=False, indent=2) + "\n", encoding="utf-8" payload = json.dumps(state, ensure_ascii=False, indent=2) + "\n"
) fd, tmp = tempfile.mkstemp(dir=str(STATE.parent), prefix=".state-", suffix=".tmp")
try:
with os.fdopen(fd, "w", encoding="utf-8") as f:
f.write(payload)
os.replace(tmp, STATE)
except BaseException:
try:
os.unlink(tmp)
except OSError:
pass
raise
def effective_status(task: dict, state: dict) -> str: def effective_status(task: dict, state: dict) -> str:
+37
View File
@@ -0,0 +1,37 @@
# 安全遗留拍板清单 / SECURITY_DECISIONS.md
日期: 2026-09-25 · 前置: 全局检测报告(架构/安全/业务/部署四方向)
## 本轮已修(仅本仓库,不触碰线上运行)
1. **rustdesk-config-export 出库 + ignore**:远控中转服务器地址与永久密码哈希不再随 HEAD 分发(历史对象仍在,见 D1)。磁盘文件已保留。
2. **nginx-docker.conf**:4 个自带 `add_header` 的 location(/api-directory/、/assets、静态资源、/index.html)补齐 nosniff / X-Frame-Options / Referrer-Policy,修复 add_header 层级覆盖继承导致的静态响应裸奔。
- 生效条件:服务器上 `docker compose restart frontend`(pull 本仓不会热生效,线上运行不受影响)。
3. **PLANNING/tasks.py**:state.json 改为临时文件 + `os.replace` 原子写(防并发/中断写坏;多进程同时读改写的丢失更新窗口仍在,量级可接受)。
4. **infra/backup.sh**:新增可选 `BACKUP_REMOTE_DIR`(设置后自动 rsync 异地副本,不设则行为不变)。
5. **B6 产物出库复核**:当前跟踪的 png/log/tar.gz 为 0,88 项实际已在 6d22fb1 完成,SECURITY_FIX 文档该条滞后。
## 待拍板(不可逆 / 涉及线上或远程仓库,一句话确认即可执行对应项)
### D1 git 历史清理(docker.env 真实密码 + rustdesk 配置仍可从历史取回)
```bash
pip install git-filter-repo
git filter-repo --invert-paths --path docker.env --path rustdesk-config-export --force
git remote add origin https://gitea.mymoyu.top/root/vscode-workbench.git # filter-repo 会移除 remote
git push origin master --force
```
影响:改写远程历史,所有 clone 需重新拉取;需确认无其他协作者持有旧 clone。
### D2 RustDesk 服务器侧加固(需操作远控,改即生效)
- 被控端更换永久密码(客户端 设置→安全→永久密码),更换后本机 rustdesk-config-export 重新导出才会更新。
- 103.40.14.100 的 hbbs/hbbr(51116-51118)加来源 IP 白名单或防火墙限制。
### D3 线上 B1-B5(沿用 SECURITY_FIX_SPEC.md deferred 清单,均需停机/线上授权)
B1 线上密钥全量轮换(含 DJANGO_SECRET_KEY,会使现有 session 失效) / B2 生产 TLS+HSTS / B3 服务器防火墙收紧 / B5 容器 USER 非 root。
### D4 model-registry.json 分值基准(六维合计 42 vs total_max=36)
修哪边决定 grade 换算基准(dim5<50% veto 的分母含义不同),需评分口径拍板后改,未拍板前不动。
### D5 主工程源码仓库归属
chunyu_project / chunyu_project_react 不在本仓版本控制与备份范围(.gitignore),compose build 与 task-manifest 均指向本地目录。需确认是否已有独立 repo;若无,建仓并纳入备份。
## 注意事项
- 本轮提交推送后,其他机器 `git pull` 会从其工作区移除 rustdesk-config-export 目录(git rm --cached 语义);若服务器上有引用请先拷贝。
- nginx-docker.conf 属已跟踪文件,本仓修改不会自动影响线上;需在服务器手动 restart frontend 才生效。
+5
View File
@@ -12,5 +12,10 @@ sha256sum "$ARCHIVE" > "$ARCHIVE.sha256"
# 保留策略:只留最近 7 个归档 # 保留策略:只留最近 7 个归档
ls -1t "$OUT_DIR"/planning-backup-*.tar.gz 2>/dev/null | tail -n +8 | xargs -r rm -f ls -1t "$OUT_DIR"/planning-backup-*.tar.gz 2>/dev/null | tail -n +8 | xargs -r rm -f
ls -1t "$OUT_DIR"/planning-backup-*.tar.gz.sha256 2>/dev/null | tail -n +8 | xargs -r rm -f ls -1t "$OUT_DIR"/planning-backup-*.tar.gz.sha256 2>/dev/null | tail -n +8 | xargs -r rm -f
# 可选异地副本:设置 BACKUP_REMOTE_DIR(形如 user@host:/backups/planning)即同步,不设则跳过
if [ -n "${BACKUP_REMOTE_DIR:-}" ] && command -v rsync >/dev/null 2>&1; then
rsync -a "$OUT_DIR"/planning-backup-*.tar.gz "$OUT_DIR"/planning-backup-*.tar.gz.sha256 "$BACKUP_REMOTE_DIR/"
echo "offsite: $BACKUP_REMOTE_DIR"
fi
echo "backup: $ARCHIVE" echo "backup: $ARCHIVE"
cat "$ARCHIVE.sha256" cat "$ARCHIVE.sha256"
+13
View File
@@ -15,6 +15,10 @@ server {
try_files $uri =404; try_files $uri =404;
expires 1y; expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable" always; add_header Cache-Control "public, max-age=31536000, immutable" always;
# nginx 的 add_header 按层级整体覆盖继承:自带 add_header 的 location 必须重复安全头
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
types { types {
image/avif avif; image/avif avif;
image/gif gif; image/gif gif;
@@ -237,6 +241,9 @@ server {
location ~* ^/assets/.*\.(js|css|png|jpg|jpeg|gif|webp|avif|ico|svg|woff|woff2|ttf|eot)$ { location ~* ^/assets/.*\.(js|css|png|jpg|jpeg|gif|webp|avif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y; expires 1y;
add_header Cache-Control "public, immutable"; add_header Cache-Control "public, immutable";
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
access_log off; access_log off;
} }
@@ -244,12 +251,18 @@ server {
location ~* \.(png|jpg|jpeg|gif|webp|ico|svg|woff|woff2|ttf|eot)$ { location ~* \.(png|jpg|jpeg|gif|webp|ico|svg|woff|woff2|ttf|eot)$ {
expires 7d; expires 7d;
add_header Cache-Control "public"; add_header Cache-Control "public";
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
access_log off; access_log off;
} }
# index.html 不缓存,保证发版后立即生效 # index.html 不缓存,保证发版后立即生效
location = /index.html { location = /index.html {
add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
} }
# SPA 路由回退(放在最后,避免拦截上面的 API 请求) # SPA 路由回退(放在最后,避免拦截上面的 API 请求)
-112
View File
@@ -1,112 +0,0 @@
# RustDesk 配置导出包
## 导出的文件说明
### 核心配置文件(必须)
1. **RustDesk.toml** - 包含你的设备ID、密码和密钥对
- nc_id = 你的设备ID(编码形式)
- password = 你的连接密码
- salt = 密码盐值
- key_pair = 你的密钥对(公钥+私钥)
2. **RustDesk2.toml** - 包含服务器配置和连接选项
- ␍endezvous_server = 中继服务器地址(103.40.14.100:51116)
- custom-rendezvous-server = 自定义服务器地址
- pi-server = API服务器地址
- ␍elay-server = 中继服务器地址
### 连接配置文件(可选)
3. **RustDesk_local.toml** - 本地界面设置和最近连接记录
4. **peers.toml** - 连接质量默认设置
5. **peers/172260841.toml** - 连接到 debian (172260841) 的配置
6. **peers/510231016.toml** - 连接到 chunyu (510231016) 的配置
## 在另一台电脑上使用
### 方法1:直接替换配置文件(推荐)
1. **安装相同版本的RustDesk**
- 确保新电脑安装的是相同或相近版本的RustDesk
2. **完全退出RustDesk**
- 右键系统托盘中的RustDesk图标 → 退出
3. **备份原有配置**
- 在新电脑上备份原有配置:
`ash
# Windows路径
C:\Users\你的用户名\AppData\Roaming\RustDesk\config\
# 或者直接重命名config文件夹
`
4. **复制配置文件**
- 将本目录下的 config 文件夹内容复制到:
`ash
# Windows
C:\Users\你的用户名\AppData\Roaming\RustDesk\config\
# Linux
~/.config/rustdesk/
# macOS
~/Library/Preferences/com.carriez.RustDesk/
`
- ⚠️ **注意:复制前确保RustDesk已完全退出**
5. **启动RustDesk**
- 重新启动RustDesk
- 你应该会看到相同的设备ID和密码
### 方法2:手动导入关键配置
如果只想迁移设备ID和密码:
1. 启动新电脑上的RustDesk
2. 进入设置 → 网络
3. 修改以下字段:
- **ID/密码**: 使用 RustDesk.toml 中的值
- **中继服务器**: 103.40.14.100:51116
- **API服务器**: http://103.40.14.100:51114
- **中继服务器**: 103.40.14.100:51117
## 重要安全提醒
⚠️ **安全警告**
- 这个配置包包含你的**私钥**和**密码**
- **不要**公开分享这个文件夹
- **不要**上传到公共网站或云存储
- 传输完成后建议**删除**这个导出包
- 如果泄露,立即在RustDesk中生成新的ID/密钥
## 服务器配置信息
你的RustDesk使用自建服务器:
- **中继服务器**: 103.40.14.100:51116
- **API服务器**: http://103.40.14.100:51114
- **中继服务器**: 103.40.14.100:51117
确保新电脑可以访问这些服务器。
## 故障排除
### 问题1:设备ID不同
- 如果新电脑显示不同的ID,说明配置没有正确复制
- 确保复制的是 config 文件夹内的文件,而不是整个RustDesk安装目录
### 问题2:无法连接服务器
- 检查防火墙是否允许RustDesk访问 103.40.14.100
- 确保新电脑的网络可以访问该IP地址
### 问题3:密码错误
- 确保 RustDesk.toml 中的 password 字段完整复制
- 检查是否有额外的空格或换行符
### 问题4:配置文件被锁定
- 确保RustDesk完全退出后再复制
- Windows任务管理器中结束所有RustDesk进程
## 文件创建时间
导出时间:2026-08-24 22:49:06
原电脑:Windows (12914@chunyu)
-112
View File
@@ -1,112 +0,0 @@
enc_id = '00AQz/5CY35TG97g2QnrGBGFsYEPcU0mHWXNqu1mn7FhqFBYrXCDMaPV0wNEo2B2CPTHQ='
password = '01Ab4HU3UU0KEF/+Aq0tvRdzVzyFCXRBF9COn6BhmCSB1xeDu0DExLdhGg4Z/xMRhjTX8kr9Af6P1ysKG3I+p3brcTyWBaG7fcoSKhERsh9BGXilRmkdz7'
salt = 'c74xsa4r45h6eytgnezjbbfp78n4ej67'
key_pair = [
[
51,
67,
177,
97,
114,
230,
142,
216,
112,
30,
104,
178,
162,
136,
90,
176,
13,
7,
6,
132,
20,
78,
149,
84,
232,
117,
40,
16,
194,
90,
156,
7,
126,
106,
99,
23,
248,
115,
242,
178,
35,
108,
236,
135,
165,
174,
113,
178,
145,
87,
24,
12,
65,
135,
47,
136,
84,
84,
144,
222,
60,
237,
157,
100,
],
[
126,
106,
99,
23,
248,
115,
242,
178,
35,
108,
236,
135,
165,
174,
113,
178,
145,
87,
24,
12,
65,
135,
47,
136,
84,
84,
144,
222,
60,
237,
157,
100,
],
]
key_confirmed = true
[keys_confirmed]
rs-ny = true
"192.168.5.7:21116" = true
"103.40.14.100:51116" = true
"103.40.14.100:18782" = true
@@ -1,15 +0,0 @@
rendezvous_server = '103.40.14.100:51116'
nat_type = 2
serial = 0
unlock_pin = ''
trusted_devices = '00AVi/cTFeUBdZy7xkGGuk/nSSPBBH+YRKewuMGnqPwr4VKQvcyL4Eb/U69w=='
[options]
key = 'aZLZystmHh7GFNZJrGZ0gpvp9y3OyQFKdEgtfaZzO8c='
local-ip-addr = '192.168.5.14'
custom-rendezvous-server = '103.40.14.100:51116'
api-server = 'http://103.40.14.100:51114'
av1-test = 'Y'
verification-method = 'use-permanent-password'
disable-udp = 'N'
relay-server = '103.40.14.100:51117'
@@ -1,3 +0,0 @@
[options]
codec-preference = 'h264'
image_quality = 'low'
@@ -1,19 +0,0 @@
remote_id = '172260841'
kb_layout_type = ''
size = [
0,
0,
0,
0,
]
fav = []
[options]
remote-menubar-drag-right = '1.0'
remote-menubar-drag-left = '0.0'
[ui_flutter]
wm_RemoteDesktop = '{"width":1300.0,"height":740.0,"offsetWidth":874.0,"offsetHeight":384.0,"isMaximized":true,"isFullscreen":false}'
peer-tab-index = '0'
wm_Main = '{"width":800.0,"height":600.0,"offsetWidth":951.0,"offsetHeight":284.0,"isMaximized":false,"isFullscreen":false}'
peer-sorting = 'Remote ID'
-61
View File
@@ -1,61 +0,0 @@
password = []
size = [
0,
0,
0,
0,
]
size_ft = [
0,
0,
0,
0,
]
size_pf = [
0,
0,
0,
0,
]
view_style = 'original'
scroll_style = 'scrollauto'
edge_scroll_edge_thickness = 100
image_quality = 'best'
custom_image_quality = [50]
show_remote_cursor = false
lock_after_session_end = false
terminal-persistent = false
privacy_mode = false
allow_swap_key = false
port_forwards = []
direct_failures = 0
disable_audio = false
disable_clipboard = false
enable-file-copy-paste = true
show_quality_monitor = false
follow_remote_cursor = false
follow_remote_window = false
view_only = false
show_my_cursor = false
sync-init-clipboard = false
trackpad-speed = 100
[options]
codec-preference = 'av1'
collapse_toolbar = ''
custom-fps = '30'
zoom-cursor = ''
i444 = 'Y'
swap-left-right-mouse = ''
[ui_flutter]
wm_RemoteDesktop = '{"width":874.0,"height":384.0,"offsetWidth":874.0,"offsetHeight":384.0,"isMaximized":true,"isFullscreen":false}'
[info]
username = ''
hostname = ''
platform = ''
[transfer]
write_jobs = []
read_jobs = []
@@ -1,167 +0,0 @@
password = [
48,
48,
65,
89,
66,
87,
50,
75,
52,
101,
99,
89,
105,
115,
84,
116,
107,
120,
51,
83,
115,
120,
79,
51,
121,
78,
108,
74,
69,
47,
74,
65,
117,
113,
49,
52,
74,
43,
89,
89,
103,
47,
81,
115,
68,
85,
98,
48,
70,
56,
106,
71,
73,
116,
86,
82,
98,
108,
76,
75,
72,
112,
113,
107,
81,
49,
115,
48,
97,
112,
116,
65,
67,
90,
82,
113,
118,
82,
53,
120,
69,
74,
97,
84,
121,
47,
43,
108,
88,
87,
116,
98,
110,
78,
77,
105,
105,
74,
109,
65,
61,
61,
]
size = [
0,
0,
0,
0,
]
size_ft = [
0,
0,
0,
0,
]
size_pf = [
0,
0,
0,
0,
]
view_style = 'original'
scroll_style = 'scrollauto'
edge_scroll_edge_thickness = 100
image_quality = 'balanced'
custom_image_quality = [50]
show_remote_cursor = false
lock_after_session_end = false
terminal-persistent = false
privacy_mode = false
allow_swap_key = false
port_forwards = []
direct_failures = 1
disable_audio = false
disable_clipboard = false
enable-file-copy-paste = true
show_quality_monitor = false
follow_remote_cursor = false
follow_remote_window = false
keyboard_mode = 'map'
view_only = false
show_my_cursor = false
sync-init-clipboard = false
trackpad-speed = 100
[options]
zoom-cursor = ''
collapse_toolbar = ''
codec-preference = 'auto'
i444 = ''
swap-left-right-mouse = ''
custom-fps = '30'
remote-menubar-edge = 'top'
remote-menubar-frac = '0.5'
[ui_flutter]
wm_RemoteDesktop = '{"width":1300.0,"height":740.0,"offsetWidth":874.0,"offsetHeight":384.0,"isMaximized":true,"isFullscreen":false}'
[info]
username = 'chunyu'
hostname = 'debian'
platform = 'Linux'
[transfer]
write_jobs = []
read_jobs = []
@@ -1,167 +0,0 @@
password = [
48,
48,
65,
97,
47,
43,
106,
103,
47,
78,
98,
66,
87,
114,
66,
112,
76,
50,
43,
55,
84,
81,
112,
102,
54,
54,
103,
43,
105,
73,
79,
53,
68,
87,
108,
116,
101,
83,
69,
98,
121,
53,
54,
47,
105,
101,
48,
56,
56,
67,
115,
81,
100,
87,
80,
100,
57,
79,
121,
71,
102,
49,
118,
57,
73,
122,
117,
47,
108,
108,
52,
99,
49,
113,
103,
109,
76,
120,
47,
84,
48,
121,
116,
66,
66,
76,
119,
78,
120,
68,
82,
104,
106,
75,
50,
105,
111,
120,
72,
81,
61,
61,
]
size = [
0,
0,
0,
0,
]
size_ft = [
0,
0,
0,
0,
]
size_pf = [
0,
0,
0,
0,
]
view_style = 'original'
scroll_style = 'scrollauto'
edge_scroll_edge_thickness = 100
image_quality = 'low'
custom_image_quality = [50]
show_remote_cursor = false
lock_after_session_end = false
terminal-persistent = false
privacy_mode = false
allow_swap_key = false
port_forwards = []
direct_failures = 0
disable_audio = false
disable_clipboard = false
enable-file-copy-paste = true
show_quality_monitor = false
follow_remote_cursor = false
follow_remote_window = false
keyboard_mode = 'map'
view_only = false
show_my_cursor = false
sync-init-clipboard = false
trackpad-speed = 100
[options]
custom-fps = '30'
remote-menubar-frac = '0.5'
i444 = ''
collapse_toolbar = ''
zoom-cursor = ''
swap-left-right-mouse = ''
codec-preference = 'h264'
remote-menubar-edge = 'top'
[ui_flutter]
wm_RemoteDesktop = '{"width":874.0,"height":384.0,"offsetWidth":874.0,"offsetHeight":384.0,"isMaximized":true,"isFullscreen":false}'
[info]
username = '12914'
hostname = 'chunyu'
platform = 'Windows'
[transfer]
write_jobs = []
read_jobs = []
-89
View File
@@ -1,89 +0,0 @@
# RustDesk 配置自动导入脚本
# 用法:以管理员身份运行 PowerShell,然后执行此脚本
param(
[string] = "C:\Users\12914\AppData\Roaming\RustDesk\config"
)
Write-Host "RustDesk 配置导入工具" -ForegroundColor Green
Write-Host "=====================" -ForegroundColor Green
# 检查RustDesk是否在运行
$rustdeskProcess = Get-Process -Name "rustdesk" -ErrorAction SilentlyContinue
if ($rustdeskProcess) {
Write-Host "警告:RustDesk正在运行,需要先退出" -ForegroundColor Yellow
$response = Read-Host "是否强制结束RustDesk进程?(y/n)"
if ($response -eq 'y') {
Stop-Process -Name "rustdesk" -Force
Start-Sleep -Seconds 2
Write-Host "RustDesk已退出" -ForegroundColor Green
} else {
Write-Host "请手动退出RustDesk后重新运行此脚本" -ForegroundColor Red
exit 1
}
}
# 创建备份目录
$backupDir = "$RustDeskPath\..\config_backup_20260824_224929"
if (Test-Path $RustDeskPath) {
Write-Host "备份原有配置到:$backupDir" -ForegroundColor Yellow
Copy-Item -Path $RustDeskPath -Destination $backupDir -Recurse -Force
Write-Host "备份完成" -ForegroundColor Green
} else {
Write-Host "原有配置目录不存在,跳过备份" -ForegroundColor Yellow
}
# 确保目标目录存在
if (!(Test-Path $RustDeskPath)) {
New-Item -ItemType Directory -Path $RustDeskPath -Force | Out-Null
New-Item -ItemType Directory -Path "$RustDeskPath\peers" -Force | Out-Null
}
# 获取脚本所在目录(假设配置文件在同级目录)
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$sourceConfig = "$scriptDir\config"
if (!(Test-Path $sourceConfig)) {
Write-Host "错误:找不到配置文件目录 $sourceConfig" -ForegroundColor Red
Write-Host "请确保此脚本与config文件夹在同一目录" -ForegroundColor Red
exit 1
}
# 复制配置文件
Write-Host "复制配置文件..." -ForegroundColor Cyan
$files = @(
"RustDesk.toml",
"RustDesk2.toml",
"RustDesk_local.toml",
"peers.toml",
"RustDesk_default.toml"
)
foreach ($file in $files) {
$sourceFile = "$sourceConfig\$file"
$destFile = "$RustDeskPath\$file"
if (Test-Path $sourceFile) {
Copy-Item -Path $sourceFile -Destination $destFile -Force
Write-Host " ✓ $file" -ForegroundColor Green
} else {
Write-Host " ✗ $file (未找到)" -ForegroundColor Red
}
}
# 复制peers目录
$peersSource = "$sourceConfig\peers"
$peersDest = "$RustDeskPath\peers"
if (Test-Path $peersSource) {
if (!(Test-Path $peersDest)) {
New-Item -ItemType Directory -Path $peersDest -Force | Out-Null
}
Copy-Item -Path "$peersSource\*" -Destination $peersDest -Recurse -Force
Write-Host " ✓ peers目录" -ForegroundColor Green
}
Write-Host "
配置导入完成!" -ForegroundColor Green
Write-Host "现在可以启动RustDesk,应该会显示原来的设备ID和密码。" -ForegroundColor Cyan
Write-Host "
按任意键退出..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")