├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── CHANGELOG.md ├── License ├── README.md ├── back ├── README.md ├── README_EN.md ├── back.md ├── buildv6only.sh ├── fix_apparmor.sh ├── fixed_windows.sh ├── get_images.py ├── install_iso.sh ├── install_pve7.sh ├── old_fuction.sh ├── pve6_to_pve7.sh ├── rebuild.sh ├── rebuild_qcow2.sh └── uninstallpve.sh ├── dockerfiles ├── Dockerfile_aarch64_7 ├── Dockerfile_x86_64_7 └── build.sh ├── extra_scripts ├── check-dns.service ├── check-dns.sh ├── clear_interface_route_cache.service ├── clear_interface_route_cache.sh ├── configure_macos.sh ├── configure_network.service ├── configure_network.sh ├── ifupdown2-install.service ├── install_ifupdown2.sh └── ndpresponder.service ├── gpg ├── README.md ├── proxmox-release-bullseye.gpg └── proxmox-release-buster.gpg └── scripts ├── build_backend.sh ├── build_nat_network.sh ├── buildct.sh ├── buildct_onlyv6.sh ├── buildvm.sh ├── buildvm_extra_ip.sh ├── buildvm_fullnat_ip.sh ├── buildvm_macos.sh ├── buildvm_manual_ip.sh ├── buildvm_onlyv6.sh ├── check_kernal.sh ├── create_ct.sh ├── create_vm.sh ├── default_ct_config.sh ├── default_vm_config.sh ├── install_macos_images.sh ├── install_pve.sh ├── pve_delete.sh ├── ssh_bash.sh └── ssh_sh.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: spiritlhl 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: ['https://github.com/user-attachments/assets/0fd5d0c9-837f-45c1-8a56-f23f3d0bdbe7'] 14 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build and Upload ndpresponder 2 | 3 | on: 4 | repository_dispatch: 5 | types: 6 | - ndpresponder_update 7 | schedule: 8 | - cron: '0 12 * * *' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build-and-upload: 13 | name: Build, Upload ndpresponder, and Push Docker Image 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | architecture: [amd64, arm64] 18 | steps: 19 | - name: Checkout ndpresponder repository 20 | uses: actions/checkout@v3 21 | with: 22 | repository: yoursunny/ndpresponder 23 | 24 | - name: Set up Go 1.23 25 | uses: actions/setup-go@v4 26 | with: 27 | go-version: 1.23 28 | 29 | - name: Build ndpresponder 30 | run: | 31 | mkdir -p build 32 | CGO_ENABLED=0 GOARCH=${{ matrix.architecture }} GOOS=linux go build -o build/ndpresponder . 33 | 34 | - name: Upload to GitHub Releases 35 | uses: softprops/action-gh-release@v1 36 | with: 37 | files: build/ndpresponder 38 | tag_name: ${{ matrix.architecture == 'amd64' && 'ndpresponder_x86' || 'ndpresponder_aarch64' }} 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | 42 | - name: Build Docker Image 43 | run: | 44 | arch_suffix=$([[ "${{ matrix.architecture }}" == "amd64" ]] && echo "x86" || echo "aarch64") 45 | docker build -t spiritlhl/ndpresponder_${arch_suffix}:latest . 46 | 47 | - name: Log in to Docker Hub 48 | uses: docker/login-action@v2 49 | with: 50 | username: ${{ secrets.DOCKER_USERNAME }} 51 | password: ${{ secrets.DOCKER_PASSWORD }} 52 | 53 | - name: Push Docker Image 54 | run: | 55 | arch_suffix=$([[ "${{ matrix.architecture }}" == "amd64" ]] && echo "x86" || echo "aarch64") 56 | docker push spiritlhl/ndpresponder_${arch_suffix}:latest 57 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 更新日志 2 | 3 | 2025.05.17 4 | 5 | - 修复CDN抽风情况下,重复尝试ndp配置文件下载,重复尝试默认的设置文件下载 6 | - 虚拟机开设的时候,强制指定OS Type 7 | - 针对CPU的Type,采取更细化的分类设置,兼容额外的默认设置 8 | - 修复ARM架构下老的上游配置的使用 9 | - ndp的配置设置修改适配至少/112大小的IPV6子网 10 | 11 | 2025.05.10 12 | 13 | - 对ARM架构上PVE安装的上游更新适配 14 | - 放宽PVE安装过程中的主IP识别逻辑,尝试支持物理机 15 | - PVE主体安装流程模块化函数化,方便后续维护 16 | 17 | 2025.05.09 18 | 19 | - 修复ARM架构上开设虚拟机的BUG,添加额外的固件检测和安装 20 | - 优化虚拟机系统模糊匹配机制,优先使用版本较新的镜像 21 | - 添加ARM架构虚拟机系统可用的参数提示,添加Debian系列的ARM镜像,添加模糊匹配 22 | - 增强VMID/CTID检测避免同ID实体创建报错 23 | - 模块化容器CT的创建流程,提取公共函数,方便后续维护 24 | 25 | 2025.05.08 26 | 27 | - 添加MACOS虚拟机开设文件 28 | 29 | 2025.05.07 30 | 31 | - 添加MACOS镜像预下载shell文件 32 | - 删除无效文件 33 | 34 | 2025.04.21 35 | 36 | - 修复ndpresponder.service在前置条件不完全正确时依然启动的问题 37 | 38 | 2025.04.20 39 | 40 | - 修复环境检测的提示和自加载KVM的逻辑,当嵌套虚拟化不可用时,提示可使用QEMU的TCG开设虚拟机 41 | - 修复虚拟机自动开设的所有脚本,支持自动检测自动判断使用host类型还是qemu类型的CPU开设虚拟机 42 | - 调整CDN轮询顺序为随机顺序,避免单个CDN节点压力过大 43 | - 提取公共代码,减少重复逻辑,模块化代码方便维护 44 | 45 | 2025.04.12 46 | 47 | - 修复DigitalOcean的debian12存在不同的debian系统发行版本存在默认命令路径不同的问题 48 | 49 | 2025.03.29 50 | 51 | - 修复Hits徽章访问量统计,使用 https://github.com/oneclickvirt/hitscounter 52 | 53 | 2025.02.23 54 | 55 | - 去除对 cip.cc 的依赖 56 | 57 | 2025.01.23 58 | 59 | - 增加ndpresponder的自动编译,及时更新上游更新。 60 | 61 | 2024.12.31 62 | 63 | - 去除无效的源替换,增加PVE国内镜像源的尝试列表不仅限于清华源 64 | 65 | 2024.12.14 66 | 67 | - 进一步限制ctid和vmid在100到256范围内,避免使用错误的id导致开始失败 68 | 69 | 2024.12.06 70 | 71 | - 增加对web端口映射的检测,如果对应参数设置为0,则不进行公网端口映射 72 | 73 | 2024.12.01 74 | 75 | - 修复可能输入无效的VMID/CTID的问题,开设之前检测有效性 76 | 77 | 2024.11.16 78 | 79 | - 修复自动识别导入后image所在盘路径的位置,避免指定非local盘时路径失效 80 | 81 | 2024.11.15 82 | 83 | - 支持自定义hostname,留空回车时默认为PVE 84 | 85 | 2024.10.27 86 | 87 | - 完全重构删除VM和CT的脚本,考虑更多可能导致删除错误的情况,增加错误排查机制和日志记录 88 | 89 | 2024.10.20 90 | 91 | - 修复在开设NAT的KVM/LXC时,未显式指定bridge的问题,导致的潜在的端口访问问题 92 | 93 | 2024.10.04 94 | 95 | - 修复在ARM的宿主机上进行容器创建时引导检测失败的问题 96 | 97 | 2024.08.11 98 | 99 | - 修复自动修复apt源的代码 100 | 101 | 2024.06.05 102 | 103 | - 更新SSH设置规则,适配Ubuntu24 104 | 105 | 2024.05.19 106 | 107 | - 修复开设PVE的ARM架构的LXC容器时指定系统版本检索错误的问题 108 | - 优化手动指定IPV4地址时,自动识别是否在同子网内,若在同一子网时也进行开设,使用不同的网关配置 109 | - 新增NAT全端口映射的KVM开设虚拟机的脚本 110 | 111 | 2024.05.15 112 | 113 | - 指定额外子网的IPV4地址生成虚拟机时,强制指定虚拟机内的IPV4的子网掩码为/32 114 | 115 | 2024.05.13 116 | 117 | - 较新的Ubuntu/Debian系统可能自带密码禁用验证策略,修复开放密码验证策略 118 | 119 | 2024.04.25 120 | 121 | - 修复 hostname 可能设置失败的问题 122 | - 修复面板地址可能绑定到IPV6上的问题,强制监听IPV4的端口 123 | - 修复KVM开设独立IPV4的虚拟机时,可能遇到的查询宿主机IP区间和网关地址失败的问题 124 | - 优化独立IPV4地址的虚拟机开设的脚本,强制要求手动附加地址的时候附加IP非同子网,而自动附加时需要附加IP同子网 125 | - 优化手动附加地址的时候附加IPV4可指定MAC地址 126 | - 上述脚本错选时增加更换脚本的提示 127 | 128 | 2024.03.12 129 | 130 | - 迁移仓库至于组织仓库,方便协同维护 131 | 132 | 2024.02.21 133 | 134 | - 增加LXC容器开设的自修补镜像源:https://github.com/oneclickvirt/lxc_amd64_images 135 | - LXC容器开设的源优先级:自修补 > 手动修补 > 官方源 136 | 137 | 2024.02.20 138 | 139 | - 优化环境检测脚本,IPV6不可用时不检测SLAAC配置 140 | 141 | 2024.02.19 142 | 143 | - 优化CT创建的系统选择判断,支持宽泛的无指定具体版本的系统 144 | 145 | 2024.02.18 146 | 147 | - 修复低版本PVE删除非企业订阅弹窗可能失效的问题 148 | - 使用 https://github.com/oneclickvirt/lxc_arm_images 归档的ARM架构的LXC镜像以支持可开设更多不同系统的ARM容器镜像,不再局限于Ubuntu和Debian系统 149 | - 修复ARM架构下纯IPV6的容器开设有问题 150 | 151 | 2024.02.17 152 | 153 | - 修复X86架构下,debian9安装的PVE无法解压zst格式的LXC容器预制模板的问题 154 | - 初步修复x86架构下批量重启容器会有内网IP错误重载的问题,在预制容器模板的过程中使用非平凡的net0名字以避免网络在后续使用过程中不自重设的问题 155 | 156 | 2024.02.16 157 | 158 | - 修复IPV6配置后的可用性检测,增加提示信息 159 | - 修复ARM的服务器安装PVE时遇到的部分镜像链接不可用的情况,使用ping和curl双重检测确保链接的可用性 160 | - 修复ARM架构下无法安装PVE6.x以及更低版本的问题,切换使用另一个项目的源进行低版本的pve安装 161 | 162 | 2024.02.09 163 | 164 | - 尝试适配 devuan opensuse 待适配 gentoo 165 | - 修复使用GitHub的API的Release存在缓存延迟的问题,切换使用raw链接获取文件名字 166 | 167 | 2024.02.08 168 | 169 | - LXC模板构建自定义的模板提前初始化好部分内容,避免原始模板过于干净导致初始化时间过长,优先级:自修补镜像 > 官方镜像 170 | - 已预先安装设置模板的容器仓库:[https://github.com/oneclickvirt/pve_lxc_images](https://github.com/oneclickvirt/pve_lxc_images) 171 | - 修复镜像在下载后重复使用可能重复下载的问题 172 | - 增加适配 alpine fedora archlinux 待适配 devuan opensuse gentoo 173 | - 大幅缩短LXC容器开设时间(在使用自修补镜像时) 174 | - 增加下载镜像失败时的错误处理 175 | 176 | 2024.02.04 177 | 178 | - 增加IPV6的子网掩码识别的精确度 179 | - 修复部分提示信息避免误导 180 | 181 | 2024.02.02 182 | 183 | - 修复网关自动配置的时候,可能出现IPV6网络配置未加载的情况,增加预先的请求加载配置 184 | 185 | 2024.01.31 186 | 187 | - 增添每日实时更新的KVM镜像源,实现自动修补原始镜像进行初始化 188 | - 修改部分提示和描述 189 | 190 | 2024.01.09 191 | 192 | - 增加初始的DNS配置备份 193 | - 增加SLAAC处理逻辑,若存在SLAAC机制分配的IPV6地址,则需要用户选择是否使用最大IPV6子网范围 194 | 195 | 2023.12.31 196 | 197 | - 增加一个删除对应容器/虚拟机的脚本,避免在删除过程中删除了非对应的映射规则 198 | - 修复批量开设时最后一个循环后续没有要等待的内容还去等待的问题 199 | 200 | 2023.12.27 201 | 202 | - 修复部分宿主机安装过程中可能存在```firmware-ath9k-htc```需要移除的情况 203 | 204 | 2023.12.21 205 | 206 | - 修复部分宿主机和LXC容器不含定时任务机制的问题 207 | 208 | 2023.12.20 209 | 210 | - 增加IPV6网络保活的定时任务,避免长期不使用导致V6的ndp广播缓存失效 211 | 212 | 2023.12.08 213 | 214 | - 有的商家双重配置有别名的网卡,实际没有使用但会卡脚本,现自动删除无效网卡 215 | 216 | 2023.12.03 217 | 218 | - 修复可能的hostname的自动修改导致网络不通的问题,尝试解决默认V4为内网V4的情况 219 | - 修复限定仅在子网前缀识别出现问题时才重构ipv6地址匹配前缀 220 | - 适配宿主机的IPV6子网可能是/48甚至更大的情况 221 | 222 | 2023.11.27 223 | 224 | - 完善IPV6检测机制,尽量保证识别出准确的IPV6子网掩码和子网前缀,且尽量保证ndp可用 225 | 226 | 2023.11.26 227 | 228 | - 重新计算IPV6子网的前缀,适配宿主机虽然给了子网但默认的IPV6地址是随机给的,非```::1```或```::a```结尾的情况 229 | - 重新设计虚拟机/容器的IPV6网关配置逻辑,适配宿主机的IPV6的gateway可能恰好是该子网第一个IPV6的情况 230 | 231 | 2023.11.25 232 | 233 | - 放松IPV6的gateway判断条件,部分宿主机的gateway直接通过```ip -6 route add default via```加上去的还行,此时虽然fe80地址不通,但不影响v6使用 234 | 235 | 2023.11.24 236 | 237 | - 修复特殊的宿主机绑定了非IPV6子网内的额外的IPV6地址,且此额外的IPV6地址不能直接删除(会导致虚拟机网关不通),需要接在vmbr0上,IPV6子网的IPV6需要接到vmbr2上,已修复 238 | 239 | 2023.11.22 240 | 241 | - 修复可能检测私网IPV6失灵的情况,完善检测逻辑 242 | - 修复宿主机内可能绑定不止一个IPV6地址的情况,只测试地址最长的公网IPV6地址 243 | - 修复DNS可能在添加PVE的证书后被重置为空的问题 244 | 245 | 2023.11.18 246 | 247 | - 修复适配宿主机本身IPV6环境可能fe80地址未加白的情况 248 | - 修复部分宿主机本身绑定了两个IPV6地址,且二者的子网掩码大小不同的情况,使用其中范围更大的子网掩码 249 | - 修复了部分宿主机网关抽风自动识别MAC地址错误,导致重启后丢失网络的情况,给物理网关绑定死了MAC地址 250 | 251 | 2023.11.05 252 | 253 | - 修复创建CT时可能存在的IPV6的分配问题 254 | 255 | 2023.11.02 256 | 257 | - 尝试在网关构建过程中支持IPV6隧道做为虚拟网关之一,失败了 258 | - 尝试在网关构建过程中支持IPV6隧道做为被桥接的网关,测试已成功 259 | - 去除至少/64子网大小的限制 260 | 261 | 2023.10.22 262 | 263 | - 设置LXC容器开设后,重启不再覆写更改后的配置 264 | 265 | 2023.10.09 266 | 267 | - 修复国内宿主机开设容器时,由于网络与官方的包管理源链接非常不通畅,使用第三方镜像地址加速 268 | 269 | 2023.10.03 270 | 271 | - 修复开设带IPV6地址的虚拟机时,网关顺序错配的问题 272 | - 更新开设出的虚拟机的nameserver和searchdomain设置,避免某些机器在解析域名时出错 273 | 274 | 2023.09.16 275 | 276 | - 修复DNS修补过程中可能存在的判断漏洞 277 | - 修复可能存在的ndp的sysctl设置问题 278 | - 修复可能存在的网关的路由缓存问题,增加自动修复的守护进程 279 | 280 | 2023.09.15 281 | 282 | - 迁移了KVM镜像中Centos8-Stream的所在地址 283 | 284 | 2023.09.07 285 | 286 | - 修复默认的物理接口如果带altname时自动检测部分别名是否可附加,如果不可附加自动删除 287 | 288 | 2023.09.01 289 | 290 | - 前置的环境检测增加是否是debian系统的检测,优化部分检测逻辑 291 | - 主体安装的脚本前移二次环境检测,避免已修改了部分内容还去检测是否能安装,此时再判断已经迟了 292 | - 优化IPV4地址检测,增加对 RFC 6598 地址的判断 293 | - 前置的环境检测优化内存的检测,如果Swap不为0则包含Swap 294 | 295 | 2023.08.29 296 | 297 | - 判断IPV6是否未dhcp类型,如果是则检测是否已分配IPV6地址,如果未分配则删除对应配置避免冲突 298 | 299 | 2023.08.27 300 | 301 | - 将删除物理网关的操作移动到创建vmbr0之前,而不是原来的创建vmbr1之前 302 | 303 | 2023.08.26 304 | 305 | - 修复开设NAT网关时,vmbr0如果不存在就去补全时的漏洞,尝试支持第一第二步安装PVE不使用本仓库脚本的PVE创建NAT网关 306 | - 给IPV6的识别增加ping检测,如果ping不通当作IPV6不通,避免有的商家设置的IPV6本身就有问题根本用不了还配置,导致安装出问题 307 | 308 | 2023.08.23 309 | 310 | - ndppd增加ARM架构的支持 311 | - 修复了非独立IPV6的CT重启失败的问题,独立IPV6的CT可能还是会重启失败无法启动(目前测试ARM会重启出问题,X86的全部无问题) 312 | - 选择开设带独立IPV6地址的服务前增加ndppd的检测,如果其状态异常则自动退出脚本 313 | 314 | 2023.08.22 315 | 316 | - 使用ndppd进行独立IPV6映射解决MAC校验的问题,实测成功 317 | 318 | 2023.08.20 319 | 320 | - iptables的映射存在重复的问题,已尝试修复 321 | - 尝试在写入NAT网关的同时删除无用的原有网关配置,避免可能存在的问题 322 | 323 | 2023.08.15 324 | 325 | - 尝试增加Docker安装PVE的方法以适配宿主机不是Debian的情况,实测支持Ubuntu了,其他支持Docker的宿主机系统应该也没问题 326 | 327 | 2023.08.11 328 | 329 | - 修复部分上次更新导致的新BUG,测试无误了 330 | 331 | 2023.08.09 332 | 333 | - 判断是否有IPV6网络,如果没有则宿主机就不添加对应的V6的DNS 334 | - 判断是否有IPV6网络,如果没有则开设的虚拟机和容器就不添加对应的V6的DNS 335 | - 修复带IPV6环境的容器设置ssh时,需要重启容器才能保证容器内网络联通的问题,自动判断是否需要自重启容器 336 | 337 | 2023.08.04 338 | 339 | - 开设独立IPV4地址的虚拟机时,尝试增加自动附加IPV6地址的功能 340 | - 增加一键开设纯IPV6虚拟机、纯IPV6容器的脚本 341 | - 修复可能的dns-nameservers存在多行导致网络异常的问题 342 | - 特殊处理没有ifupdown的宿主机 343 | 344 | 2023.08.03 345 | 346 | - 尝试增加了IPV6的支持,暂时只是支持了IPV6网关的设置,暂时未适配一键开设,明日适配 347 | - 简化IPV4和IPV6地址的查询,避免重复查询 348 | - 修复可能的grub更新错误 349 | - 网络配置文件备份修改顺序,避免重复备份 350 | - 增加已修改过的文件的备份 351 | - KVM虚拟机增加centos8-stream镜像源 352 | 353 | 2023.08.02 354 | 355 | - 更新KVM虚拟机镜像源,支持更多系统 356 | - 修复部分虚拟机开设时自定义硬盘大小失败的问题,增加设置间隔,避免硬盘IO爆炸 357 | 358 | 2023.07.31 359 | 360 | - 修复部分机器重启机器后失联的情况(Hetzner、Azure) 361 | - 增加自动时间校准功能 362 | - 加速系统熵计算 363 | - 修复创建虚拟机和容器的一键脚本分别适配ARM和X86_64的情况 364 | - 重构物理接口检测函数,保证检测到的接口顺序与```ip addr show```一致 365 | - 优化ssh.sh文件,如果开设debian的LXC容器时,遇到中国宿主机,对应自动替换apt源 366 | 367 | 2023.07.30 368 | 369 | - 适配了ARM架构且已在hz的ARM机器上测试(Debian11及其更旧的系统)无问题,感谢[Proxmox-Arm64](https://github.com/jiangcuo/Proxmox-Arm64)提供的第三方补丁,本项目目前支持X86_64架构和ARM架构了 370 | - 修改部分附加文件的存储位置至于```/usr/local/bin/```目录下 371 | - CN的IP检测增加一个检测源,对CN的特殊处理增加对APT源的特殊处理 372 | - 有些奇葩机器的apt源老有问题,增加自动修复的函数 373 | 374 | 2023.07.28 375 | 376 | - 部分原生的厂商给的apt源有问题,不是官方源,比如Azure需要进行特殊处理,特转换archive为官方源以支持pve的安装,已修复该问题 377 | - 整合网络修改部分的代码到同一个函数中,方便后续维护 378 | 379 | 2023.07.24 380 | 381 | - 增强ssh.sh脚本开设ssh服务的能力,避免默认配置了cloudinit导致的问题 382 | 383 | 2023.07.06 384 | 385 | - 增加公网私网IPV4地址的检测 386 | 387 | 2023.07.02 388 | 389 | - 修复部分机器的网络配置重复行的部分空格数量不一致导致未识别出重复的问题 390 | - 修复部分网络配置的子目录有写但不使用的情况,这种情况就不需要合并配置文件了,修复了之前强制合并的问题 391 | - 修复适配部分机器默认防火墙是开启且屏蔽了部分端口的情况 392 | 393 | 2023.06.30 394 | 395 | - 修复部分机器的网络配置是热加载,写在/run/network/interfaces.d/文件夹下的问题 396 | - 修复安装proxmox-ve时grub-pc配置可能冲突的问题 397 | 398 | 2023.06.29 399 | 400 | - 规整输出,所有输出修改为中英双语 401 | - 修改部分脚本提示避免脚本被小白重复执行 402 | 403 | 2023.06.26 404 | 405 | - 修复ipv6网络是SLAAC动态分配时vmbr0的设置导致宿主机没有V6网络的问题 406 | 407 | 2023.06.25 408 | 409 | - 特化修复在Hetzner上安装需要DD系统再安装的问题,现在安装原生debian系统也支持了 410 | - 修复部分机器使用浮动IP,没有/etc/network/interfaces文件的问题,自动生成对应文件 411 | - 修复部分机器启动后,DNS检测失败的问题,确保在网关添加后必自动检测一次保证DNS无问题 412 | 413 | 2023.06.24 414 | 415 | - 修复部分机器是IPV6子网前缀识别失效的问题 416 | - 更新Debian12安装的PVE版本为stable 417 | - 修复部分机器ifconfig命令不存在的问题 418 | - 适配部分机器ipv6网络是SLAAC动态分配的情况 419 | - 适配pve8.0的非订阅用户弹窗进行删除 420 | 421 | 2023.06.23 422 | 423 | - 网关配置修改使用新结构,以便于适配大多数机器 424 | - 调整安装的流程,升级软件包后需要重启一次系统,详见脚本的运行提示 425 | - 解决了ifupdown2的安装问题,支持在更多商家的服务器上安装 426 | 427 | 2023.06.22 428 | 429 | - PVE安装修复部分机器网络设置不立即重新加载的问题,增加网络设置备份 430 | - 部分机器的IPV6物理接口使用auto类型,无法安装PVE,修改为static类型并重写配置 431 | - 由于上面这条修复,已支持在Linode平台安装PVE了 432 | - 修复低版本PVE还安装ifupdown2的问题,7.x以下版本使用ifupdown也足够了 433 | - 由于上面这条修复,Hetzner的Debian10系统可以安装PVE了,但只可安装主体,不能自动设置网关 434 | 435 | 2023.06.21 436 | 437 | - 增加手动指定IPV4地址的脚本 438 | - PVE安装修复部分系统原生网络设置有问题的情况 439 | - 修复NAT网关自动设置时部分机器的物理接口存在别名的情况,已自动识别替换别名 440 | 441 | 2023.06.14 442 | 443 | - 修改ssh.sh文件以适配不同的系统启用SSH端口和服务 444 | - 修改create_ct.sh文件及相关文件,增加适配支持开设centos系的系统的CT容器 445 | 446 | 2023.06.13 447 | 448 | - 修改部分提示,避免错误的操作流程 449 | - 增加对apparmor的依赖修复,避免安装apparmor在部分模板上卡死未成功安装 450 | - 适配Debian12系统 451 | - 重新整合组件安装部分的代码,优化代码结构 452 | - 修改网关写入的文件,判断物理接口是否允许桥接,无配置的设置为允许 453 | 454 | 2023.06.06 455 | 456 | - 修复检测过程中遇到系统盘以TB为单位的识别问题,适配系统盘以TB或GB单位计算 457 | - 修复安装过程中部分奇葩模板可能出现的依赖问题,使用--fix-missing命令修复依赖 458 | 459 | 2023.06.05 460 | 461 | - 创建IPV4的NAT网关时检测IPV4的方法改为使用ip addr show检测,不再使用ip route,这样可以带上IP区间不写死 462 | - 自动检测IPV6子网是否存在,如果存在则绑定vmbr0,不存在则只绑定IPV4 463 | - 更改部分说明的描述 464 | 465 | 2023.06.04 466 | 467 | - 增加一键开设独立IPV4虚拟机的脚本,支持一键生成独立IPV4的KVM虚拟化的虚拟机 468 | - 修改创建KVM虚拟机过程中预下载镜像前CDN检测的逻辑问题,如果已存在镜像则不再检测CDN有效性,因为无需通过CDN下载镜像文件 469 | - 更新中文文档部分说明 470 | 471 | 2023.06.03 472 | 473 | - 更新支持自动修复apt源缺失公钥的问题,不再需要手动修复 474 | - 暂时移除静态动态地址转换,默认的是DHCP或静态的IP不再进行识别和转换,后续替换为别的方式解决 475 | - NAT网络构建前检测lshw包是否存在是否需要下载,保证物理设备的识别能成功(部分奇葩的系统模板不自带该工具包) 476 | - 更新中文文档部分说明 477 | 478 | 2023.05.30 479 | 480 | - 更新支持批量开设的虚拟机或容器自定义系统,默认留空为debian11系统 481 | - 更新修复使用批量开设前检测挂载盘系统盘的问题,暂时移除检测,后续修复检测 482 | - 更新中文文档部分说明 483 | 484 | 2023.05.29 485 | 486 | - 增加自动写入NOTE的功能,开出的容器和虚拟机将自带对应的配置信息,不用再在命令行中使用cat查看了(但原有的配置信息文件还将存在,否则无法使用批量命令批量创建) 487 | 488 | 2023.05.20 489 | 490 | - 增加支持开设的虚拟机和容器可自定义开设在挂载盘还是系统盘,默认留空使用系统盘local 491 | 492 | 2023.04.24 493 | 494 | - 更新支持国内腾讯云阿里云的Debian系安装PVE和开设LXC容器,由于国内机器非独服基本不开嵌套虚拟化支持,所以只能开LXC 495 | - 更新支持创建vmbr0,母鸡允许addr和gateway为内网IP或外网IP,已自动识别替换 496 | 497 | 2023.04.23 498 | 499 | - 支持一键生成LXC或KVM虚拟化的NAT服务器 500 | - 支持批量开设,多次运行批量开设LXC或KVM虚拟化的NAT服务器,重复运行继承配置 501 | - 开出的容器和虚拟机都自带IPV4内外网端口转发 502 | 503 | 2023.04.11 504 | 505 | - 更新支持一键生成单个KVM虚拟化的NAT服务器(自带内外网映射) 506 | - 更新PVE自修改qcow2文件,已预开启安装cloudinit,开启SSH登陆,预设值SSH监听V4和V6的22端口,开启允许密码验证登陆,开启允许ROOT登陆 507 | 508 | 2023.04.04 509 | 510 | - 开发了基于PVE的 [ConvoyPanel](https://github.com/ConvoyPanel/panel) 一键安装脚本 511 | - PVE一键安装是ConvoyPanel一键安装的前提,创建NAT网关不是 512 | - 修复PVE在VPS上一键安装可能遇到的各种BUG 513 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 spiritLHLS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PVE 2 | 3 | [![Hits](https://hits.spiritlhl.net/pve.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false)](https://hits.spiritlhl.net) 4 | 5 | 感谢 Proxmox VE 的免费订阅支持 6 | 7 | 如果有未适配的商家或机器欢迎联系[@spiritlhl_bot](https://t.me/spiritlhl_bot),有空会尝试支持一下 8 | 9 | ## 更新 10 | 11 | 2025.05.18 12 | 13 | - 修复CDN抽风情况下,重复尝试获取镜像列表和下载 14 | 15 | [更新日志](CHANGELOG.md) 16 | 17 | ## 说明文档 18 | 19 | 国内(China Docs): 20 | 21 | [https://virt.spiritlhl.net/](https://virt.spiritlhl.net/) 22 | 23 | 国际(English Docs): 24 | 25 | [https://www.spiritlhl.net/en/](https://www.spiritlhl.net/en/) 26 | 27 | 说明文档中 Proxmox VE 分区内容 28 | 29 | 自修补虚拟机镜像源: 30 | 31 | [https://github.com/oneclickvirt/pve_kvm_images](https://github.com/oneclickvirt/pve_kvm_images) 32 | 33 | [https://github.com/oneclickvirt/kvm_images](https://github.com/oneclickvirt/kvm_images) 34 | 35 | [https://github.com/oneclickvirt/macos](https://github.com/oneclickvirt/macos) 36 | 37 | 自修补容器镜像源: 38 | 39 | [https://github.com/oneclickvirt/lxc_amd64_images](https://github.com/oneclickvirt/lxc_amd64_images) 40 | 41 | [https://github.com/oneclickvirt/pve_lxc_images](https://github.com/oneclickvirt/pve_lxc_images) 42 | 43 | [https://github.com/oneclickvirt/lxc_arm_images](https://github.com/oneclickvirt/lxc_arm_images) 44 | 45 | ## Introduce 46 | 47 | English Docs: 48 | 49 | [https://www.spiritlhl.net/en/](https://www.spiritlhl.net/en/) 50 | 51 | Description of the **Proxmox VE** partition contents in the documentation 52 | 53 | Self-patching VM image sources: 54 | 55 | [https://github.com/oneclickvirt/pve_kvm_images](https://github.com/oneclickvirt/pve_kvm_images) 56 | 57 | [https://github.com/oneclickvirt/kvm_images](https://github.com/oneclickvirt/kvm_images) 58 | 59 | [https://github.com/oneclickvirt/macos](https://github.com/oneclickvirt/macos) 60 | 61 | Self-patching container image source: 62 | 63 | [https://github.com/oneclickvirt/lxc_amd64_images](https://github.com/oneclickvirt/lxc_amd64_images) 64 | 65 | [https://github.com/oneclickvirt/pve_lxc_images](https://github.com/oneclickvirt/pve_lxc_images) 66 | 67 | [https://github.com/oneclickvirt/lxc_arm_images](https://github.com/oneclickvirt/lxc_arm_images) 68 | 69 | ## 友链 70 | 71 | VPS融合怪测评脚本 72 | 73 | https://github.com/oneclickvirt/ecs 74 | 75 | https://github.com/spiritLHLS/ecs 76 | 77 | ## Stargazers over time 78 | 79 | [![Stargazers over time](https://starchart.cc/oneclickvirt/pve.svg)](https://github.com/oneclickvirt/ecs) 80 | -------------------------------------------------------------------------------- /back/README_EN.md: -------------------------------------------------------------------------------- 1 | # PVE 2 | 3 | Thanks to Proxmox VE for the free subscription support 4 | 5 | [中文](README.md) | [English](README_EN.md) 6 | 7 | ### Preface 8 | 9 | It is recommended that debian use the latest system as much as possible before using it 10 | 11 | Non-debian11 can use [debian one-click upgrade](https://github.com/spiritLHLS/one-click-installation-script#%E4%B8%80%E9%94%AE%E5%8D%87%E7%BA%A7%E4%BD%8E%E7%89%88%E6%9C%ACdebian%E4%B8%BAdebian11) to upgrade the system 12 | 13 | Of course, it's okay not to use the latest debian system, but it's not officially supported 14 | 15 | **Please make sure that the machine can be reinstalled before use, there is no guarantee that this script will not cause any bugs!!! ** 16 | 17 | **If the server is a VPS and not a dedicated server , there may be various bugs, please be ready to reinstall the server if the deployment fails!!!! ** 18 | 19 | ### Configuration and system requirements 20 | 21 | Only Debian system (non-Debian can not be installed through the APT source, the official only gave the image of Debian, other systems can only use ISO installation) 22 | 23 | System requirements: Debian 8+ 24 | 25 | Minimum hardware requirements: 2 cores 2G RAM x86_64 architecture server hard disk at least 20G 26 | 27 | PS: If the hardware requirements are not met, you can use LXD batch open LXC [jump](https://github.com/spiritLHLS/lxc) 28 | 29 | Hardware requirements to open KVM: VM-X or AMD-V support - (part of the VPS and all unique service support) 30 | 31 | ### Checking hardware environment 32 | 33 | - This script must be executed before the execution of this repository script to test the environment, if it does not meet the requirements for the installation of PVE, then the subsequent scripts cannot be used 34 | - Check if the hardware configuration meets the minimum requirements 35 | - Check if the hardware environment can be nested with a virtualized KVM type server 36 | - Check if the system environment can be nested with a virtualized KVM type server 37 | - Non-nestable virtualized KVM type servers can also run LXC virtualized servers 38 | 39 | ``` 40 | bash <(wget -qO- --no-check-certificate https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/check_kernal.sh) 41 | ``` 42 | 43 | ### PVE base installation (one-click PVE installation) 44 | 45 | - The installation is the latest PVE for the current apt source 46 | - For example, debian10 is pve6.4, debian11 is pve7.x 47 | - Modified /etc/hosts file (fixed incorrectly set merchant hostname and added required content for PVE) 48 | - Set ``chattr -i /etc/hosts`` to read-only mode to avoid overwriting the file after reboot, if you want to modify it, please use ``chattr -i /etc/hosts`` to cancel the read-only lock, after modification, please execute ``chattr +i /etc/hosts`` to read-only lock 49 | - Check if it is Chinese IP, if it is Chinese IP use Tsinghua mirror source, otherwise use official source 50 | - Install the necessary toolkit needed for PVE to open the virtual machine 51 | - Replace the enterprise subscription in the apt source with the community source 52 | - Print query if Linux kernel and PVE kernel are installed 53 | - Check if the network configuration is a dhcp configured V4 network, if so convert to static address to avoid dhcp failure after reboot, has been set to read-only mode, if you need to modify please use ``chattr -i /etc/network/interfaces.d/50-cloud-init`` to cancel the read-only lock, after modification please execute `` ``chattr +i /etc/network/interfaces.d/50-cloud-init`` read-only lock 54 | - Check if ``/etc/resolv.conf`` is empty, if it is, set the systemd service to detect ``8.8.8.8``'s boot up and add DNS 55 | - After adding the APT source link for PVE, download PVE and print out the login information 56 | - After configuration, you need to reboot the system to load the new kernel 57 | 58 | ``` 59 | curl -L https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/scripts/install_pve.sh -o install_pve.sh && chmod +x install_pve.sh && bash install_pve.sh 60 | ``` 61 | 62 | - The installation process may exit the installation, you need to repair the apt source manually, as shown in the following figure after the repair is completed, execute this script again 63 | 64 | ![图片](https://user-images.githubusercontent.com/103393591/220104992-9eed2601-c170-46b9-b8b7-de141eeb6da4.png) 65 | 66 | ![图片](https://user-images.githubusercontent.com/103393591/220105032-72623188-4c44-43c0-b3f1-7ce267163687.png) 67 | 68 | ### Pre-configured environment 69 | 70 | - Create resource pool mypool 71 | - Remove subscription pop-ups 72 | - Attempt to enable hardware passthrough 73 | - Detect AppArmor module and attempt to install 74 | 75 | ``` 76 | bash <(wget -qO- --no-check-certificate https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/build_backend.sh) 77 | ``` 78 | 79 | ### Auto-configure NAT gateway for IPV4 80 | 81 | - **Please ensure that the server has been restarted and PVE is working properly on the WEB side before using it** 82 | - Create vmbr0 83 | - Create vmbr1 (NAT gateway) 84 | - Use ``172.16.1.1`` for the gateway (IPV4) and ``172.16.1.x/24`` for IPV4/CIDR when opening the NAT virtual machine, the x here cannot be 1 85 | - You may need to manually click the web side to apply the configuration button to apply a 86 | - To see the complete settings, you can execute ``cat /etc/network/interfaces`` to see 87 | - Load iptables and set back to source and allow NAT port forwarding 88 | 89 | ``` 90 | bash <(wget -qO- --no-check-certificate https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/build_nat_network.sh) 91 | ``` 92 | 93 | ## Generate a NAT server for KVM virtualization with one click 94 | 95 | Remember to **execute the first command in this repository before using it, the one that detects the hardware environment**, as shown below 96 | 97 | ![图片](https://user-images.githubusercontent.com/103393591/231160050-79945d07-b3d0-4e8d-9315-74e4fbb24f9d.png) 98 | 99 | To query the above, just use the following one-click script to create the virtual machine automatically, no need to manually modify the WEB-side settings again 100 | 101 | ![图片](https://user-images.githubusercontent.com/103393591/231160070-c317607c-8b0c-4aa4-bfa2-e75ec6626b24.png) 102 | 103 | Query as above after using the subsequent script to create a virtual machine, you need to manually modify the WEB side settings, you need to turn off the hardware nested virtualization corresponding to each virtual machine, as follows 104 | 105 | ![图片](https://user-images.githubusercontent.com/103393591/231160449-82911a57-4b49-47ec-8fad-2100c6059017.png) 106 | 107 | First stop the virtual machine and then modify it, and then turn it on after modifying it in order to use NOVNC, not closing it may cause this virtual machine to have bugs that can't be used 108 | 109 | ### Generate separate VMs for KVM virtualization 110 | 111 | - Automatically open a NAT server, the default is to use the Debian10 image, because it takes up the least amount of space. 112 | - can be customized in the command need to use the mirror, here are given a good configuration of the mirror, the mirror comes with space is 2G hard disk, so at least need to set the hard disk to 3G in the command 113 | - custom memory size recommended 512MB memory, it should be noted that the mother hen memory remember to open some swap to avoid machine bombing [open SWAP point me to jump](https://github.com/spiritLHLS/addswap) 114 | - Automatic internal and external network port mapping, including 22, 80, 443 ports and other 25 internal and external network port number the same port 115 | - After generation, you need to wait for a period of time to configure the network and login information by cloudinit inside the virtual machine, it takes about 5 minutes 116 | 117 | ``` 118 | curl -L https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/buildvm.sh -o buildvm.sh && chmod +x buildvm.sh 119 | ``` 120 | 121 | #### How to use 122 | 123 | - System support: See the systems listed in [Jump](https://github.com/spiritLHLS/Images/releases/tag/v1.0) for details, when using just write the file name, no need for the .qcow2 suffix 124 | - **Note that the username here can not be a pure number, it will cause problems with cloudinit, it is best to start with plain English or English** 125 | 126 | ``` 127 | ./buildvm.sh VMID UserName Password CPU Cores Memory HardDisk SSHPort 80Port 443Port ExtranetPortStart ExtranetPortStop System 128 | ``` 129 | 130 | #### Example 131 | 132 | Test opening a NAT server 133 | 134 | The following example opens a virtual machine with VMID 102, username test1, password 1234567, CPU 1 core, memory 512MB, hard disk 5G, SSH port 40001, port 80 40002, port 443 40003. 135 | 136 | Meanwhile, the internal and external network mapping ports are in the same range of 50000 to 50025, and the system is using ubuntu20 137 | 138 | ``` 139 | ./buildvm.sh 102 test1 1234567 1 512 5 40001 40002 40003 50000 50025 ubuntu20 140 | ``` 141 | 142 | Executable after opening 143 | 144 | ``` 145 | cat vm102 146 | ``` 147 | 148 | View Information 149 | 150 | #### Deletion Example 151 | 152 | - Delete port mapping delete test machine 153 | 154 | ``` 155 | qm stop 102 156 | qm destroy 102 157 | iptables -t nat -F 158 | iptables -t filter -F 159 | service networking restart 160 | systemctl restart networking.service 161 | rm -rf vm102 162 | ``` 163 | 164 | #### Related qcow2 mirrors 165 | 166 | - Pre-installed to open cloudinit, open SSH login, preset value SSH listening to V4 and V6 port 22, open to allow password authentication login, open to allow ROOT login 167 | 168 | https://github.com/spiritLHLS/Images/releases/tag/v1.0 169 | 170 | ### Batch opening of KVM virtualized VMs for NAT 171 | 172 | - **You need to ensure that there is no virtual machine without any port mapping before using PVE for the first time, otherwise there may be bugs** 173 | - **Please use screen to suspend the execution before opening to avoid batch running SSH instability which leads to interruption in the middle of execution** 174 | - You can run the batch generation VM multiple times, but you need to pay attention to the hen memory remember to open some swap to avoid machine bombing [open SWAP point me to jump](https://github.com/spiritLHLS/addswap) 175 | - automatically open NAT server, the default use of Debian10 image, because the image occupies the smallest 176 | - Automatic internal and external network port mapping, including 22, 80, 443 ports and other 25 internal and external network port number the same port 177 | - After generation, you need to wait for a while for cloudinit inside the virtual machine to configure the network and login information, which takes about 5 minutes. 178 | - The default network configuration of the batch virtual machine is: 22, 80, 443 ports and a 25-port internal and external network mapping 179 | - You can customize the number of cores, memory size and hard disk size for batch opening, remember to calculate the free resources for opening 180 | 181 | ``` 182 | curl -L https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/create_vm.sh -o create_vm.sh && chmod +x create_vm.sh && bash create_vm.sh 183 | ``` 184 | 185 | Executable after running 186 | 187 | ``` 188 | cat vmlog 189 | ``` 190 | 191 | View Information 192 | 193 | #### Delete all virtual machines 194 | 195 | ``` 196 | for vmid in $(qm list | awk '{if(NR>1) print $1}'); do qm stop $vmid; qm destroy $vmid; rm -rf /var/lib/vz/images/$vmid*; done 197 | iptables -t nat -F 198 | iptables -t filter -F 199 | service networking restart 200 | systemctl restart networking.service 201 | rm -rf vmlog 202 | ``` 203 | 204 | ### Cautions 205 | 206 | PVE modify the configuration of the virtual machine before you have to stop first, then modify the configuration, modify and then start, so as not to have a configuration reload error 207 | 208 | ## Acknowledgements 209 | 210 | https://blog.ilolicon.com/archives/615 211 | 212 | https://github.com/Ella-Alinda/somescripts/blob/main/nat.sh 213 | 214 | https://pve.proxmox.com/pve-docs/qm.1.html 215 | 216 | https://down.idc.wiki/Image/realServer-Template/ 217 | 218 | https://mirrors.tuna.tsinghua.edu.cn/proxmox/ 219 | 220 | https://github.com/roacn/pve/blob/main/pve.sh 221 | 222 | Thanks to [@Ella-Alinda](https://github.com/Ella-Alinda) for the PVE guide 223 | 224 | ## Friendly Links 225 | 226 | VPS Fusion Monster Measurement Script 227 | 228 | https://github.com/spiritLHLS/ecs 229 | -------------------------------------------------------------------------------- /back/back.md: -------------------------------------------------------------------------------- 1 | - 安装过程中可能会退出安装,需要手动修复apt源,如下图所示修复完毕后再次执行本脚本 2 | 3 | ![图片](https://user-images.githubusercontent.com/103393591/220104992-9eed2601-c170-46b9-b8b7-de141eeb6da4.png) 4 | 5 | ![图片](https://user-images.githubusercontent.com/103393591/220105032-72623188-4c44-43c0-b3f1-7ce267163687.png) 6 | 7 | - 查询网络配置是否为dhcp配置的V4网络,如果是则转换为静态地址避免重启后dhcp失效,已设置为只读模式,如需修改请使用```chattr -i /etc/network/interfaces.d/50-cloud-init```取消只读锁定,修改完毕请执行```chattr +i /etc/network/interfaces.d/50-cloud-init```只读锁定 8 | -------------------------------------------------------------------------------- /back/buildv6only.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2024.03.12 5 | 6 | # ./buildv6only.sh VMID 用户名 密码 CPU核数 内存 硬盘 系统 7 | # ./buildv6only.sh 103 test2 1234567 1 512 5 debian11 8 | 9 | cd /root >/dev/null 2>&1 10 | # 创建容器 11 | vm_num="${1:-103}" 12 | user="${2:-test2}" 13 | password="${3:-1234567}" 14 | core="${4:-1}" 15 | memory="${5:-512}" 16 | disk="${6:-5}" 17 | system="${7:-debian11}" 18 | rm -rf "vm$name" 19 | 20 | if [ ! -d "qcow" ]; then 21 | mkdir qcow 22 | fi 23 | systems=("centos7" "debian10" "debian11" "debian9" "ubuntu18" "ubuntu20" "ubuntu22" "centos9-stream" "centos8-stream") 24 | for sys in ${systems[@]}; do 25 | if [[ "$system" == "$sys" ]]; then 26 | file_path="/root/qcow/${system}.qcow2" 27 | break 28 | fi 29 | done 30 | if [[ -z "$file_path" ]]; then 31 | red "无法安装对应系统,仅支持 debian9 debian10 debian11 ubuntu18 ubuntu20 ubuntu22 centos9-stream centos8-stream centos7" 32 | exit 1 33 | fi 34 | url="https://github.com/spiritLHLS/Images/releases/download/v1.0/${system}.qcow2" 35 | if [ ! -f "$file_path" ]; then 36 | curl -L -o "$file_path" "$url" 37 | fi 38 | 39 | SUBNET_PREFIX=$(curl ipv6.ip.sb) 40 | first_digit=${vm_num:0:1} 41 | second_digit=${vm_num:1:1} 42 | third_digit=${vm_num:2:1} 43 | if [ $first_digit -le 2 ]; then 44 | if [ $second_digit -eq 0 ]; then 45 | num=$third_digit 46 | else 47 | num=$second_digit$third_digit 48 | fi 49 | else 50 | num=$((first_digit - 2))$second_digit$third_digit 51 | fi 52 | 53 | qm create $vm_num --agent 1 --scsihw virtio-scsi-single --serial0 socket --cores $core --sockets 1 --cpu host --net0 virtio,bridge=vmbr0,firewall=0 54 | qm importdisk $vm_num /root/qcow/${system}.qcow2 local 55 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 local:${vm_num}/vm-${vm_num}-disk-0.raw 56 | qm set $vm_num --bootdisk scsi0 57 | qm set $vm_num --boot order=scsi0 58 | qm set $vm_num --memory $memory 59 | # --swap 256 60 | qm set $vm_num --ide2 local:cloudinit 61 | qm set $vm_num --nameserver 2602:fc23:18::7 62 | qm set $vm_num --searchdomain dns.google 63 | user_ip="${SUBNET_PREFIX}.${num}" 64 | qm set $vm_num --ipconfig0 ip=${user_ip}/64,gw=${SUBNET_PREFIX}.1 65 | qm set $vm_num --cipassword $password --ciuser $user 66 | qm resize $vm_num scsi0 ${disk}G 67 | qm start $vm_num 68 | echo "$vm_num $user $password $core $memory $disk $system" >> "vm${vm_num}" 69 | cat "vm${vm_num}" 70 | -------------------------------------------------------------------------------- /back/fix_apparmor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | apt install -y bison flex swig autoconf automake libtool gettext git python3-setuptools 4 | git clone https://gitlab.com/apparmor/apparmor.git /tmp/apparmor || true 5 | cd /tmp/apparmor 6 | git checkout apparmor-3.0 7 | export PYTHON=/usr/bin/python3 8 | export PYTHON_VERSION=3 9 | export PYTHON_VERSIONS=python3 10 | export PYTHONPATH=$(realpath libraries/libapparmor/swig/python) 11 | cd ./libraries/libapparmor 12 | sh ./autogen.sh 13 | sh ./configure --prefix=/usr --with-perl --with-python 14 | make 15 | #make check 16 | make install 17 | cd ../../parser 18 | make 19 | #make check 20 | make install 21 | apparmor_parser --version 22 | echo $? 23 | -------------------------------------------------------------------------------- /back/fixed_windows.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2024.03.12 5 | 6 | cd /root >/dev/null 2>&1 7 | _red() { echo -e "\033[31m\033[01m$@\033[0m"; } 8 | _green() { echo -e "\033[32m\033[01m$@\033[0m"; } 9 | _yellow() { echo -e "\033[33m\033[01m$@\033[0m"; } 10 | _blue() { echo -e "\033[36m\033[01m$@\033[0m"; } 11 | reading() { read -rp "$(_green "$1")" "$2"; } 12 | utf8_locale=$(locale -a 2>/dev/null | grep -i -m 1 -E "UTF-8|utf8") 13 | if [[ -z "$utf8_locale" ]]; then 14 | echo "No UTF-8 locale found" 15 | else 16 | export LC_ALL="$utf8_locale" 17 | export LANG="$utf8_locale" 18 | export LANGUAGE="$utf8_locale" 19 | echo "Locale set to $utf8_locale" 20 | fi 21 | if [ ! -d /usr/local/bin ]; then 22 | mkdir -p /usr/local/bin 23 | fi 24 | if [ ! -d /usr/local/bin/Geco-Cloudbase-Init ]; then 25 | mkdir -p /usr/local/bin/Geco-Cloudbase-Init 26 | fi 27 | 28 | check_china() { 29 | _yellow "IP area being detected ......" 30 | if [[ -z "${CN}" ]]; then 31 | if [[ $(curl -m 6 -s https://ipapi.co/json | grep 'China') != "" ]]; then 32 | _yellow "根据ipapi.co提供的信息,当前IP可能在中国" 33 | read -e -r -p "是否选用中国镜像完成相关组件安装? ([y]/n) " input 34 | case $input in 35 | [yY][eE][sS] | [yY]) 36 | echo "使用中国镜像" 37 | CN=true 38 | ;; 39 | [nN][oO] | [nN]) 40 | echo "不使用中国镜像" 41 | ;; 42 | *) 43 | echo "使用中国镜像" 44 | CN=true 45 | ;; 46 | esac 47 | fi 48 | fi 49 | } 50 | 51 | check_cdn() { 52 | local o_url=$1 53 | local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) # 打乱数组顺序 54 | for cdn_url in "${shuffled_cdn_urls[@]}"; do 55 | if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then 56 | export cdn_success_url="$cdn_url" 57 | return 58 | fi 59 | sleep 0.5 60 | done 61 | export cdn_success_url="" 62 | } 63 | 64 | 65 | check_cdn_file() { 66 | check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" 67 | if [ -n "$cdn_success_url" ]; then 68 | _yellow "CDN available, using CDN" 69 | else 70 | _yellow "No CDN available, no use CDN" 71 | fi 72 | } 73 | 74 | apt-get install unzip patch -y 75 | 76 | # ChinaIP检测 77 | check_china 78 | 79 | # cdn检测 80 | cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn3.spiritlhl.net/" "http://cdn1.spiritlhl.net/" "https://ghproxy.com/" "http://cdn2.spiritlhl.net/") 81 | check_cdn_file 82 | 83 | # 提取大版本号 84 | version=$(pveversion) 85 | if [[ $version =~ /([0-9]+)\.[0-9]+- ]]; then 86 | major_version="${BASH_REMATCH[1]}" 87 | _green "Running kernel version: $major_version" 88 | else 89 | _yellow "Unable to recognize Proxmox VE version number" 90 | exit 1 91 | fi 92 | 93 | # 克隆修复补丁的仓库 94 | # https://github.com/GECO-IT/Geco-Cloudbase-Init 95 | # https://forum.proxmox.com/threads/howto-scripts-to-make-cloudbase-work-like-cloudinit-for-your-windows-based-instances.103375/ 96 | if [[ -z "${CN}" || "${CN}" != true ]]; then 97 | wget https://github.com/GECO-IT/Geco-Cloudbase-Init/archive/refs/heads/master.zip 98 | unzip master.zip 99 | mv Geco-Cloudbase-Init-master/* /usr/local/bin/Geco-Cloudbase-Init/ 100 | else 101 | wget "${cdn_success_url}https://github.com/GECO-IT/Geco-Cloudbase-Init/archive/refs/heads/master.zip" 102 | unzip master.zip 103 | mv Geco-Cloudbase-Init-master/* /usr/local/bin/Geco-Cloudbase-Init/ 104 | fi 105 | rm -rf master.zip Geco-Cloudbase-Init-master 106 | 107 | # 识别是否可替换对应版本镜像 108 | if [ "$major_version" == "7" ]; then 109 | patch --force --forward --backup -p0 --directory / --input "/usr/local/bin/Geco-Cloudbase-Init/qemu-server-7.1-4/Cloudinit.pm.patch" --dry-run && patch_result1="You can apply patch" || patch_result1="Can't apply patch!" 110 | patch --force --forward --backup -p0 --directory / --input "/usr/local/bin/Geco-Cloudbase-Init/qemu-server-7.1-4/Qemu.pm.patch" --dry-run && patch_result2="You can apply patch" || patch_result2="Can't apply patch!" 111 | if [ "$patch_result1" == "You can apply patch" ] && [ "$patch_result2" == "You can apply patch" ]; then 112 | _green "Can apply both patches." 113 | patch --force --forward --backup -p0 --directory / --input "/usr/local/bin/Geco-Cloudbase-Init/qemu-server-7.1-4/Cloudinit.pm.patch" 114 | patch --force --forward --backup -p0 --directory / --input "/usr/local/bin/Geco-Cloudbase-Init/qemu-server-7.1-4/Qemu.pm.patch" 115 | else 116 | _yellow "Can't apply one or both patches!" 117 | exit 1 118 | fi 119 | elif [ "$major_version" == "6" ]; then 120 | patch --force --forward --backup -p0 --directory / --input "/usr/local/bin/Geco-Cloudbase-Init/qemu-server-6.4-2/Cloudinit.pm.patch" --dry-run && patch_result1="You can apply patch" || patch_result1="Can't apply patch!" 121 | patch --force --forward --backup -p0 --directory / --input "/usr/local/bin/Geco-Cloudbase-Init/qemu-server-6.4-2/Qemu.pm.patch" --dry-run && patch_result2="You can apply patch" || patch_result2="Can't apply patch!" 122 | if [ "$patch_result1" == "You can apply patch" ] && [ "$patch_result2" == "You can apply patch" ]; then 123 | _green "Can apply both patches." 124 | patch --force --forward --backup -p0 --directory / --input "/usr/local/bin/Geco-Cloudbase-Init/qemu-server-6.4-2/Cloudinit.pm.patch" 125 | patch --force --forward --backup -p0 --directory / --input "/usr/local/bin/Geco-Cloudbase-Init/qemu-server-6.4-2/Qemu.pm.patch" 126 | else 127 | _yellow "Can't apply one or both patches!" 128 | exit 1 129 | fi 130 | else 131 | _yellow "Unsupported major version: $major_version" 132 | exit 1 133 | fi 134 | systemctl restart pvedaemon.service 135 | 136 | 137 | # https://foxi.buduanwang.vip/windows/1789.html/ 138 | -------------------------------------------------------------------------------- /back/get_images.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup 3 | 4 | url = 'https://down.idc.wiki/Image/realServer-Template/' 5 | 6 | response = requests.get(url) 7 | soup = BeautifulSoup(response.text, 'html.parser') 8 | 9 | for li in soup.find_all('li', {'class': 'item file'}): 10 | link = li.find('a')['href'].replace("cdn-backblaze.down.idc.wiki//Image/realServer-Template/", "").replace("//", "") 11 | print(link,end=" ") 12 | -------------------------------------------------------------------------------- /back/install_iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #from https://github.com/oneclickvirt/pve 3 | 4 | 5 | ## China_IP 6 | if [[ -z "${CN}" ]]; then 7 | if [[ $(curl -m 10 -s https://ipapi.co/json | grep 'China') != "" ]]; then 8 | _yellow "根据ipapi.co提供的信息,当前IP可能在中国" 9 | read -e -r -p "是否选用中国镜像完成安装? [Y/n] " input 10 | case $input in 11 | [yY][eE][sS] | [yY]) 12 | echo "使用中国镜像" 13 | CN=true 14 | ;; 15 | [nN][oO] | [nN]) 16 | echo "不使用中国镜像" 17 | ;; 18 | *) 19 | echo "使用中国镜像" 20 | CN=true 21 | ;; 22 | esac 23 | fi 24 | fi 25 | 26 | echo "请选择要加载到的模板目录:" 27 | echo "1. Proxmox VE 的模板目录(/var/lib/vz/template/iso/)" 28 | echo "2. LXC 的模板目录(/var/lib/vz/template/cache/)" 29 | echo "3. 全都要" 30 | read -p "请输入选项编号(1或2): " choice 31 | 32 | # 将镜像文件移动到Proxmox VE的模板目录中 33 | case "$choice" in 34 | 1) 35 | if [[ -n "${CN}" ]]; then 36 | wget -P /root/ https://ghproxy.com/https://github.com/spiritLHLS/realServer-Template/releases/download/1.1/ubuntu20.qcow2 37 | wget -P /root/ https://ghproxy.com/https://github.com/spiritLHLS/realServer-Template/releases/download/1.0/debian11.qcow2 38 | else 39 | wget -P /root/ https://github.com/spiritLHLS/realServer-Template/releases/download/1.1/ubuntu20.qcow2 40 | wget -P /root/ https://github.com/spiritLHLS/realServer-Template/releases/download/1.0/debian11.qcow2 41 | fi 42 | qemu-img convert -f qcow2 -O raw /root/ubuntu20.qcow2 /root/ubuntu20.raw 43 | mkisofs -o /root/ubuntu20.iso /root/ubuntu20.raw 44 | mv /root/ubuntu20.iso /var/lib/vz/template/iso/ 45 | qemu-img convert -f qcow2 -O raw /root/debian11.qcow2 /root/debian11.raw 46 | mkisofs -o /root/debian11.iso /root/debian11.raw 47 | mv /root/debian11.iso /var/lib/vz/template/iso/ 48 | rm -rf /root/ubuntu20.qcow2 /root/debian11.qcow2 /root/debian11.raw /root/ubuntu20.raw 49 | echo "已将镜像文件加载到 Proxmox VE 的模板目录" 50 | ;; 51 | 2) 52 | if [[ -n "${CN}" ]]; then 53 | wget -P /root/ https://mirrors.tuna.tsinghua.edu.cn/proxmox/images/system/debian-11-standard_11.3-0_amd64.tar.gz 54 | wget -P /root/ https://mirrors.tuna.tsinghua.edu.cn/proxmox/images/system/ubuntu-20.10-standard_20.10-1_amd64.tar.gz 55 | wget -P /root/ https://mirrors.tuna.tsinghua.edu.cn/proxmox/images/system/debian-10-standard_10.7-1_amd64.tar.gz 56 | else 57 | wget -P /root/ http://download.proxmox.com/images/system/ubuntu-20.10-standard_20.10-1_amd64.tar.gz 58 | wget -P /root/ http://download.proxmox.com/images/system/debian-11-standard_11.3-0_amd64.tar.gz 59 | wget -P /root/ http://download.proxmox.com/images/system/debian-10-standard_10.7-1_amd64.tar.gz 60 | fi 61 | mv /root/ubuntu-20.10-standard_20.10-1_amd64.tar.gz /var/lib/vz/template/cache/ 62 | mv /root/debian-11-standard_11.3-0_amd64.tar.gz /var/lib/vz/template/cache/ 63 | mv /root/debian-10-standard_10.7-1_amd64.tar.gz /var/lib/vz/template/cache/ 64 | echo "已将镜像文件移动到 LXC 的模板目录" 65 | ;; 66 | 3) 67 | 68 | if [[ -n "${CN}" ]]; then 69 | wget -P /root/ https://ghproxy.com/https://github.com/spiritLHLS/realServer-Template/releases/download/1.1/ubuntu20.qcow2 70 | wget -P /root/ https://ghproxy.com/https://github.com/spiritLHLS/realServer-Template/releases/download/1.0/debian11.qcow2 71 | wget -P /root/ https://mirrors.tuna.tsinghua.edu.cn/proxmox/images/system/debian-11-standard_11.3-0_amd64.tar.gz 72 | wget -P /root/ https://mirrors.tuna.tsinghua.edu.cn/proxmox/images/system/ubuntu-20.10-standard_20.10-1_amd64.tar.gz 73 | else 74 | wget -P /root/ https://github.com/spiritLHLS/realServer-Template/releases/download/1.1/ubuntu20.qcow2 75 | wget -P /root/ https://github.com/spiritLHLS/realServer-Template/releases/download/1.0/debian11.qcow2 76 | wget -P /root/ http://download.proxmox.com/images/system/ubuntu-20.10-standard_20.10-1_amd64.tar.gz 77 | wget -P /root/ http://download.proxmox.com/images/system/debian-11-standard_11.3-0_amd64.tar.gz 78 | fi 79 | qemu-img convert -f qcow2 -O raw /root/ubuntu20.qcow2 /root/ubuntu20.raw 80 | mkisofs -o /root/ubuntu20.iso /root/ubuntu20.raw 81 | mv /root/ubuntu20.iso /var/lib/vz/template/iso/ 82 | qemu-img convert -f qcow2 -O raw /root/debian11.qcow2 /root/debian11.raw 83 | mkisofs -o /root/debian11.iso /root/debian11.raw 84 | mv /root/debian11.iso /var/lib/vz/template/iso/ 85 | rm -rf /root/ubuntu20.qcow2 /root/debian11.qcow2 /root/debian11.raw /root/ubuntu20.raw 86 | mv /root/ubuntu-20.10-standard_20.10-1_amd64.tar.gz /var/lib/vz/template/cache/ 87 | mv /root/debian-11-standard_11.3-0_amd64.tar.gz /var/lib/vz/template/cache/ 88 | echo "已全部加载" 89 | ;; 90 | *) 91 | echo "无效的选项,程序退出" 92 | ;; 93 | esac 94 | -------------------------------------------------------------------------------- /back/install_pve7.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #from https://github.com/oneclickvirt/pve 3 | # pve 7 4 | 5 | 6 | # 打印信息 7 | _red() { echo -e "\033[31m\033[01m$@\033[0m"; } 8 | _green() { echo -e "\033[32m\033[01m$@\033[0m"; } 9 | _yellow() { echo -e "\033[33m\033[01m$@\033[0m"; } 10 | _blue() { echo -e "\033[36m\033[01m$@\033[0m"; } 11 | 12 | # 前置环境安装 13 | if [ "$(id -u)" != "0" ]; then 14 | echo "This script must be run as root" 1>&2 15 | exit 1 16 | fi 17 | apt-get update -y 18 | if ! command -v wget > /dev/null 2>&1; then 19 | apt-get install -y wget 20 | fi 21 | if ! command -v curl > /dev/null 2>&1; then 22 | apt-get install -y curl 23 | fi 24 | curl -L https://raw.githubusercontent.com/spiritLHLS/one-click-installation-script/main/check_sudo.sh -o check_sudo.sh && chmod +x check_sudo.sh && bash check_sudo.sh > /dev/null 2>&1 25 | # sysctl -w net.ipv6.conf.all.disable_ipv6=1 26 | # sysctl -w net.ipv6.conf.default.disable_ipv6=1 27 | 28 | ## China_IP 29 | if [[ -z "${CN}" ]]; then 30 | if [[ $(curl -m 10 -s https://ipapi.co/json | grep 'China') != "" ]]; then 31 | _yellow "根据ipapi.co提供的信息,当前IP可能在中国" 32 | read -e -r -p "是否选用中国镜像完成安装? [Y/n] " input 33 | case $input in 34 | [yY][eE][sS] | [yY]) 35 | echo "使用中国镜像" 36 | CN=true 37 | ;; 38 | [nN][oO] | [nN]) 39 | echo "不使用中国镜像" 40 | ;; 41 | *) 42 | echo "使用中国镜像" 43 | CN=true 44 | ;; 45 | esac 46 | fi 47 | fi 48 | 49 | # 修改 /etc/hosts 50 | hostnamectl set-hostname pve 51 | ip=$(curl -s ipv4.ip.sb) 52 | echo "127.0.0.1 localhost.localdomain localhost" | tee -a /etc/hosts 53 | echo "${ip} pve.proxmox.com pve" | tee -a /etc/hosts 54 | 55 | # 再次预检查 56 | apt-get install gnupg -y 57 | if ! nc -z localhost 7789; then 58 | iptables -A INPUT -p tcp --dport 7789 -j ACCEPT 59 | iptables-save > /etc/iptables.rules 60 | fi 61 | if [ $(uname -m) != "x86_64" ] || [ ! -f /etc/debian_version ] || [ $(grep MemTotal /proc/meminfo | awk '{print $2}') -lt 2000000 ] || [ $(grep -c ^processor /proc/cpuinfo) -lt 2 ] || [ $(ping -c 3 google.com > /dev/null 2>&1; echo $?) -ne 0 ]; then 62 | _red "Error: This system does not meet the minimum requirements for Proxmox VE installation." 63 | exit 1 64 | else 65 | _green "The system meets the minimum requirements for Proxmox VE installation." 66 | fi 67 | 68 | # 新增pve源 69 | version=$(lsb_release -cs) 70 | case $version in 71 | jessie|stretch|buster|bullseye) 72 | repo_url="deb http://download.proxmox.com/debian/pve ${version} pve-no-subscription" 73 | if [[ -n "${CN}" ]]; then 74 | repo_url="deb https://mirrors.tuna.tsinghua.edu.cn/proxmox/debian/pve ${version} pve-no-subscription" 75 | fi 76 | ;; 77 | *) 78 | _red "Error: Unsupported Debian version" 79 | exit 1 80 | ;; 81 | esac 82 | wget http://download.proxmox.com/debian/proxmox-release-bullseye.gpg -O /etc/apt/trusted.gpg.d/proxmox-release-bullseye.gpg 83 | apt-key add /etc/apt/trusted.gpg.d/proxmox-release-bullseye.gpg 84 | echo "$repo_url" >> /etc/apt/sources.list 85 | 86 | # 下载pve 87 | apt-get update && apt-get full-upgrade 88 | if [ $? -ne 0 ]; then 89 | apt-get install debian-keyring debian-archive-keyring -y 90 | apt-get update && apt-get full-upgrade 91 | fi 92 | apt-get -y install postfix open-iscsi 93 | apt-get -y install proxmox-ve 94 | 95 | # 打印安装后的信息 96 | url="https://${ip}:8006/" 97 | _green "安装完毕,请打开HTTPS网页 $url" 98 | _green "用户名、密码就是服务器所使用的用户名、密码" 99 | 100 | 101 | -------------------------------------------------------------------------------- /back/old_fuction.sh: -------------------------------------------------------------------------------- 1 | complete_ipv6_parts() { 2 | local ipv6_address=$1 3 | IFS=":" read -r -a parts <<< "$ipv6_address" 4 | local all_parts_full=true 5 | for part in "${parts[@]}"; do 6 | local length=${#part} 7 | if (( length < 4 )); then 8 | all_parts_full=false 9 | break 10 | fi 11 | done 12 | if $all_parts_full; then 13 | echo "$ipv6_address" 14 | return 15 | fi 16 | for i in "${!parts[@]}"; do 17 | local part="${parts[$i]}" 18 | local length=${#part} 19 | if (( length < 4 )); then 20 | local num_zeros=$(( 4 - length )) 21 | parts[$i]=$(printf "%0${num_zeros}d%s" 0 "$part") 22 | fi 23 | done 24 | local result=$(IFS=:; echo "${parts[*]}") 25 | echo "$result" 26 | } 27 | 28 | extract_origin_ipv6() { 29 | input_string="$1" 30 | num_characters="$2" 31 | # 拼接整体 32 | IFS=':' read -r -a array <<< "$input_string" 33 | origin="" 34 | for part in "${array[@]}"; do 35 | len=${#part} 36 | if ((len <= 4)); then 37 | origin+="$part" 38 | else 39 | for ((i = 0; i < len; i += 4)); do 40 | origin+="${part:$i:4}" 41 | if ((i + 4 < len)); then 42 | origin+=":" 43 | fi 44 | done 45 | fi 46 | done 47 | # 是不是被4整除,不整除则多一位做子网前缀 48 | max_quotient=$((${num_characters} / 4)) 49 | temp_remainder=$((${num_characters} % 4)) 50 | if [ $temp_remainder -ne 0 ]; then 51 | max_quotient=$((max_quotient + 1)) 52 | fi 53 | temp_result=$(echo "${origin:0:$max_quotient}") 54 | # 非4整除补全 55 | length=${#temp_result} 56 | remainder=$((length % 4)) 57 | zeros_to_add=$((4 - remainder)) 58 | if [ $remainder -ne 0 ]; then 59 | for ((i = 0; i < $zeros_to_add; i++)); do 60 | temp_result+="0" 61 | done 62 | fi 63 | # 插入:符号 64 | result=$(echo $temp_result | sed 's/.\{4\}/&:/g;s/:$//') 65 | colon_count=$(grep -o ":" <<< "$result" | wc -l) 66 | if [ "$colon_count" -lt 7 ]; then 67 | additional_colons=$((7 - colon_count)) 68 | for ((i=0; i([^<]+)' | sed -E 's/([^<]+)/\1 \2/') 149 | # sorted_links=$(echo "$folder_links_dates" | sort -k2 -r) 150 | # latest_folder_link=$(echo "$sorted_links" | head -n 1 | awk '{print $1}') 151 | # latest_folder_url="${URL}${latest_folder_link}" 152 | # if [ ! -f "/var/lib/vz/template/cache/${en_system}-arm64-${version}-cloud.tar.xz" ]; then 153 | # curl -o "/var/lib/vz/template/cache/${en_system}-arm64-${version}-cloud.tar.xz" "${latest_folder_url}/rootfs.tar.xz" 154 | # fi 155 | # fi 156 | 157 | # LXC容器通过API获取镜像地址 158 | # response=$(curl -sSL -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/oneclickvirt/pve_lxc_images/releases/tags/${en_system}" | grep -oP '"name": "\K[^"]+\.zst' | awk 'NR%2==1') 159 | # # 如果 https://api.github.com/ 请求失败,则使用 https://githubapi.spiritlhl.workers.dev/ ,此时可能宿主机无IPV4网络 160 | # if [ -z "$response" ]; then 161 | # response=$(curl -sSL -H "Accept: application/vnd.github.v3+json" "https://githubapi.spiritlhl.workers.dev/repos/oneclickvirt/pve_lxc_images/releases/tags/${en_system}" | grep -oP '"name": "\K[^"]+\.zst' | awk 'NR%2==1') 162 | # fi 163 | # # 如果 https://githubapi.spiritlhl.workers.dev/ 请求失败,则使用 https://githubapi.spiritlhl.top/ ,此时可能宿主机在国内 164 | # if [ -z "$response" ]; then 165 | # response=$(curl -sSL -H "Accept: application/vnd.github.v3+json" "https://githubapi.spiritlhl.top/repos/oneclickvirt/pve_lxc_images/releases/tags/${en_system}" | grep -oP '"name": "\K[^"]+\.zst' | awk 'NR%2==1') 166 | # fi -------------------------------------------------------------------------------- /back/pve6_to_pve7.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #from https://github.com/oneclickvirt/pve 3 | # pve 6 to pve 7 4 | 5 | # 打印信息 6 | _red() { echo -e "\033[31m\033[01m$@\033[0m"; } 7 | _green() { echo -e "\033[32m\033[01m$@\033[0m"; } 8 | _yellow() { echo -e "\033[33m\033[01m$@\033[0m"; } 9 | _blue() { echo -e "\033[36m\033[01m$@\033[0m"; } 10 | 11 | # 检查是否为 root 用户 12 | if [ "$(id -u)" != "0" ]; then 13 | echo "请使用 root 用户执行脚本" 14 | exit 1 15 | fi 16 | 17 | # 删除企业源 18 | rm -rf /etc/apt/sources.list.d/pve-enterprise.list 19 | 20 | # 卸载无关内核 21 | current_kernel=$(uname -r) 22 | _yellow "当前内核: $current_kernel" 23 | installed_kernels=$(dpkg -l | grep -E 'linux-(image|headers)-[0-9]+' | grep -v "$current_kernel" | awk '{print $2}') 24 | if [ -z "$installed_kernels" ]; then 25 | _blue "无闲置的内核" 26 | else 27 | _yellow "卸载闲置内核..." 28 | for kernel in $installed_kernels; do 29 | _yellow "卸载内核: $kernel" 30 | dpkg --purge --force-remove-essential $kernel 31 | apt-get remove -y $kernel > /dev/null 2>&1 32 | done 33 | _green "闲置内核已卸载完毕" 34 | fi 35 | 36 | # 升级为debian11系统 37 | curl -L https://raw.githubusercontent.com/spiritLHLS/one-click-installation-script/main/todebian11.sh -o todebian11.sh && chmod +x todebian11.sh && bash todebian11.sh 38 | 39 | # 检查 PVE 是否可以升级 40 | if ! pve6to7 | grep -q "FAILURES: 0"; then 41 | _red "检测到 PVE 升级存在问题,请先解决问题后再执行升级" 42 | exit 1 43 | fi 44 | 45 | # 检查 PVE 版本是否为最新版本 46 | if ! [ "$(pveversion -v)" = "$(pveversion -r)" ]; then 47 | _yellow "当前 PVE 版本不是最新版本,尝试升级到最新版本" 48 | apt-get update && apt-get dist-upgrade -y 49 | if [ $? -ne 0 ]; then 50 | _red "升级 PVE 失败,请检查网络或源配置" 51 | exit 1 52 | else 53 | _green "PVE 升级到最新版本成功" 54 | exit 1 55 | fi 56 | fi 57 | -------------------------------------------------------------------------------- /back/rebuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from https://github.com/oneclickvirt/pve 3 | 4 | apt-get install -y libguestfs-tools rng-tools curl 5 | apt-get install -y libguestfs-tools rng-tools curl --fix-missing 6 | curl -o rebuild_qcow2.sh https://raw.githubusercontent.com/oneclickvirt/pve/main/back/rebuild_qcow2.sh 7 | chmod 777 rebuild_qcow2.sh 8 | for image in ubuntu18.qcow2 ubuntu20.qcow2 ubuntu22.qcow2 debian11.qcow2 debian12.qcow2 centos9-stream.qcow2 centos8-stream.qcow2 centos7.qcow2 almalinux8.qcow2 almalinux9.qcow2 alpinelinux_edge.qcow2 alpinelinux_stable.qcow2 rockylinux8.qcow2 rockylinux9.qcow2; do 9 | curl -o $image "https://down.idc.wiki/Image/realServer-Template/current/qcow2/$image" 10 | # curl -o $image "https://github.com/spiritLHLS/Images/releases/download/v1.0/$image" 11 | chmod 777 $image 12 | done 13 | for image in ubuntu18.qcow2 ubuntu20.qcow2 ubuntu22.qcow2 debian11.qcow2 debian12.qcow2 centos9-stream.qcow2 centos8-stream.qcow2 centos7.qcow2 almalinux8.qcow2 almalinux9.qcow2 alpinelinux_edge.qcow2 alpinelinux_stable.qcow2 rockylinux8.qcow2 rockylinux9.qcow2; do 14 | ./rebuild_qcow2.sh $image 15 | done 16 | -------------------------------------------------------------------------------- /back/rebuild_qcow2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from https://github.com/oneclickvirt/pve 3 | 4 | if ! command -v virt-customize &> /dev/null 5 | then 6 | echo "virt-customize not found, installing libguestfs-tools" 7 | sudo apt-get update 8 | sudo apt-get install -y libguestfs-tools 9 | sudo apt-get install -y libguestfs-tools --fix-missing 10 | fi 11 | if ! command -v rngd &> /dev/null 12 | then 13 | echo "rng-tools not found, installing rng-tools" 14 | sudo apt-get update 15 | sudo apt-get install -y rng-tools 16 | sudo apt-get install -y rng-tools --fix-missing 17 | fi 18 | qcow_file=$1 19 | echo "转换文件$qcow_file中......" 20 | if [[ "$qcow_file" == *"debian"* || "$qcow_file" == *"ubuntu"* || "$qcow_file" == *"arch"* ]]; then 21 | virt-customize -a $qcow_file --run-command "sed -i 's/ssh_pwauth:[[:space:]]*0/ssh_pwauth: 1/g' /etc/cloud/cloud.cfg" 22 | virt-customize -a $qcow_file --run-command "echo '' > /etc/motd" 23 | virt-customize -a $qcow_file --run-command "echo 'Modified from https://github.com/oneclickvirt/kvm_images' >> /etc/motd" 24 | virt-customize -a $qcow_file --run-command "echo 'Related repo https://github.com/oneclickvirt/pve' >> /etc/motd" 25 | virt-customize -a $qcow_file --run-command "echo '--by https://t.me/spiritlhl' >> /etc/motd" 26 | echo "启用SSH功能..." 27 | virt-customize -a $qcow_file --run-command "systemctl enable ssh" 28 | virt-customize -a $qcow_file --run-command "systemctl start ssh" 29 | virt-customize -a $qcow_file --run-command "systemctl enable sshd" 30 | virt-customize -a $qcow_file --run-command "systemctl start sshd" 31 | echo "启用root登录..." 32 | virt-customize -a $qcow_file --run-command "sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config" 33 | virt-customize -a $qcow_file --run-command "sed -i 's/#PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config" 34 | virt-customize -a $qcow_file --run-command "sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config" 35 | virt-customize -a $qcow_file --run-command "sed -i 's/#Port 22/Port 22/g' /etc/ssh/sshd_config" 36 | virt-customize -a $qcow_file --run-command "sed -i 's/#AddressFamily any/AddressFamily any/g' /etc/ssh/sshd_config" 37 | virt-customize -a $qcow_file --run-command "sed -i 's/#ListenAddress 0.0.0.0/ListenAddress 0.0.0.0/g' /etc/ssh/sshd_config" 38 | virt-customize -a $qcow_file --run-command "sed -i 's/#ListenAddress ::/ListenAddress ::/g' /etc/ssh/sshd_config" 39 | virt-customize -a $qcow_file --run-command "service ssh restart" 40 | virt-customize -a $qcow_file --run-command "service sshd restart" 41 | virt-customize -a $qcow_file --run-command "systemctl restart sshd" 42 | virt-customize -a $qcow_file --run-command "systemctl restart ssh" 43 | if [[ "$qcow_file" == *"debian"* || "$qcow_file" == *"ubuntu"* ]]; then 44 | virt-customize -a $qcow_file --run-command "apt-get update -y && apt-get install qemu-guest-agent -y" 45 | virt-customize -a $qcow_file --run-command "systemctl start qemu-guest-agent" 46 | fi 47 | elif [[ "$qcow_file" == *"alpine"* ]]; then 48 | virt-customize -a $qcow_file --run-command "apk update" 49 | virt-customize -a $qcow_file --run-command "apk add --no-cache wget curl openssh-server sshpass" 50 | echo "启用SSH功能..." 51 | virt-customize -a $qcow_file --run-command "cd /etc/ssh" 52 | virt-customize -a $qcow_file --run-command "ssh-keygen -A" 53 | echo "启用root登录..." 54 | virt-customize -a $qcow_file --edit '/etc/cloud/cloud.cfg:s/preserve_hostname: *false/preserve_hostname: true/' 55 | virt-customize -a $qcow_file --edit '/etc/cloud/cloud.cfg:s/disable_root: *true/disable_root: false/' 56 | virt-customize -a $qcow_file --edit '/etc/ssh/sshd_config:s/PasswordAuthentication no/PasswordAuthentication yes/' 57 | virt-customize -a $qcow_file --edit '/etc/ssh/sshd_config:s/^#?\(Port\).*/\1 22/' 58 | virt-customize -a $qcow_file --edit '/etc/ssh/sshd_config:s/^#PermitRootLogin\|PermitRootLogin/c PermitRootLogin yes/' 59 | virt-customize -a $qcow_file --edit '/etc/ssh/sshd_config:s/^#AddressFamily\|AddressFamily/c AddressFamily any/' 60 | virt-customize -a $qcow_file --edit '/etc/ssh/sshd_config:s/^#ListenAddress\|ListenAddress/c ListenAddress 0.0.0.0/' 61 | virt-customize -a $qcow_file --run-command "/usr/sbin/sshd" 62 | elif [[ "$qcow_file" == *"almalinux9"* || "$qcow_file" == *"rockylinux"* ]]; then 63 | virt-customize -a $qcow_file --run-command "sed -i 's/ssh_pwauth:[[:space:]]*0/ssh_pwauth: 1/g' /etc/cloud/cloud.cfg" 64 | virt-customize -a $qcow_file --run-command "echo '' > /etc/motd" 65 | virt-customize -a $qcow_file --run-command "echo 'Modified from https://github.com/oneclickvirt/kvm_images' >> /etc/motd" 66 | virt-customize -a $qcow_file --run-command "echo 'Related repo https://github.com/oneclickvirt/pve' >> /etc/motd" 67 | virt-customize -a $qcow_file --run-command "echo '--by https://t.me/spiritlhl' >> /etc/motd" 68 | echo "启用SSH功能..." 69 | virt-customize -a $qcow_file --run-command "systemctl enable ssh" 70 | virt-customize -a $qcow_file --run-command "systemctl start ssh" 71 | virt-customize -a $qcow_file --run-command "systemctl enable sshd" 72 | virt-customize -a $qcow_file --run-command "systemctl start sshd" 73 | echo "启用root登录..." 74 | virt-customize -a $qcow_file --run-command "sed -i 's/ssh_pwauth:[[:space:]]*0/ssh_pwauth: 1/g' /etc/cloud/cloud.cfg" 75 | virt-customize -a $qcow_file --run-command "sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config" 76 | virt-customize -a $qcow_file --run-command "sed -i 's/#PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config" 77 | virt-customize -a $qcow_file --run-command "sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config" 78 | virt-customize -a $qcow_file --run-command "sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g' /etc/ssh/sshd_config" 79 | virt-customize -a $qcow_file --run-command "sed -i 's/^ChallengeResponseAuthentication no/ChallengeResponseAuthentication yes/g' /etc/ssh/sshd_config.d/50-redhat.conf" 80 | virt-customize -a $qcow_file --run-command "sed -i 's/#Port 22/Port 22/g' /etc/ssh/sshd_config" 81 | virt-customize -a $qcow_file --run-command "sed -i 's/#AddressFamily any/AddressFamily any/g' /etc/ssh/sshd_config" 82 | virt-customize -a $qcow_file --run-command "sed -i 's/#ListenAddress 0.0.0.0/ListenAddress 0.0.0.0/g' /etc/ssh/sshd_config" 83 | virt-customize -a $qcow_file --run-command "sed -i 's/#ListenAddress ::/ListenAddress ::/g' /etc/ssh/sshd_config" 84 | virt-customize -a $qcow_file --run-command "service ssh restart" 85 | virt-customize -a $qcow_file --run-command "service sshd restart" 86 | virt-customize -a $qcow_file --run-command "systemctl restart sshd" 87 | virt-customize -a $qcow_file --run-command "systemctl restart ssh" 88 | virt-customize -a $qcow_file --run-command "yum update -y && yum install qemu-guest-agent -y" 89 | virt-customize -a $qcow_file --run-command "systemctl start qemu-guest-agent" 90 | elif [[ "$qcow_file" == *"almalinux8"* || "$qcow_file" == *"centos9-stream"* || "$qcow_file" == *"centos8-stream"* || "$qcow_file" == *"centos7"* ]]; then 91 | virt-customize -a $qcow_file --run-command "sed -i 's/ssh_pwauth:[[:space:]]*0/ssh_pwauth: 1/g' /etc/cloud/cloud.cfg" 92 | virt-customize -a $qcow_file --run-command "echo '' > /etc/motd" 93 | virt-customize -a $qcow_file --run-command "echo 'Modified from https://github.com/oneclickvirt/kvm_images' >> /etc/motd" 94 | virt-customize -a $qcow_file --run-command "echo 'Related repo https://github.com/oneclickvirt/pve' >> /etc/motd" 95 | virt-customize -a $qcow_file --run-command "echo '--by https://t.me/spiritlhl' >> /etc/motd" 96 | echo "启用SSH功能..." 97 | virt-customize -a $qcow_file --run-command "systemctl enable ssh" 98 | virt-customize -a $qcow_file --run-command "systemctl start ssh" 99 | virt-customize -a $qcow_file --run-command "systemctl enable sshd" 100 | virt-customize -a $qcow_file --run-command "systemctl start sshd" 101 | echo "启用root登录..." 102 | virt-customize -a $qcow_file --run-command "sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config" 103 | virt-customize -a $qcow_file --run-command "sed -i 's/#PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config" 104 | virt-customize -a $qcow_file --run-command "sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config" 105 | virt-customize -a $qcow_file --run-command "sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g' /etc/ssh/sshd_config" 106 | virt-customize -a $qcow_file --run-command "sed -i 's/^ChallengeResponseAuthentication no/ChallengeResponseAuthentication yes/g' /etc/ssh/sshd_config.d/50-redhat.conf" 107 | virt-customize -a $qcow_file --run-command "sed -i 's/#Port 22/Port 22/g' /etc/ssh/sshd_config" 108 | virt-customize -a $qcow_file --run-command "sed -i 's/#AddressFamily any/AddressFamily any/g' /etc/ssh/sshd_config" 109 | virt-customize -a $qcow_file --run-command "sed -i 's/#ListenAddress 0.0.0.0/ListenAddress 0.0.0.0/g' /etc/ssh/sshd_config" 110 | virt-customize -a $qcow_file --run-command "sed -i 's/#ListenAddress ::/ListenAddress ::/g' /etc/ssh/sshd_config" 111 | virt-customize -a $qcow_file --run-command "service ssh restart" 112 | virt-customize -a $qcow_file --run-command "service sshd restart" 113 | virt-customize -a $qcow_file --run-command "systemctl restart sshd" 114 | virt-customize -a $qcow_file --run-command "systemctl restart ssh" 115 | virt-customize -a $qcow_file --run-command "yum update -y && yum install qemu-guest-agent -y" 116 | virt-customize -a $qcow_file --run-command "systemctl start qemu-guest-agent" 117 | else 118 | virt-customize -a $qcow_file --run-command "sed -i 's/disable_root:[[:space:]]*1/disable_root: 0/g' /etc/cloud/cloud.cfg" 119 | virt-customize -a $qcow_file --run-command "sed -i 's/ssh_pwauth:[[:space:]]*0/ssh_pwauth: 1/g' /etc/cloud/cloud.cfg" 120 | virt-customize -a $qcow_file --run-command "echo '' > /etc/motd" 121 | virt-customize -a $qcow_file --run-command "echo 'Modified from https://github.com/oneclickvirt/kvm_images' >> /etc/motd" 122 | virt-customize -a $qcow_file --run-command "echo 'Related repo https://github.com/oneclickvirt/pve' >> /etc/motd" 123 | virt-customize -a $qcow_file --run-command "echo '--by https://t.me/spiritlhl' >> /etc/motd" 124 | echo "启用SSH功能..." 125 | virt-customize -a $qcow_file --run-command "systemctl enable ssh" 126 | virt-customize -a $qcow_file --run-command "systemctl start ssh" 127 | virt-customize -a $qcow_file --run-command "systemctl enable sshd" 128 | virt-customize -a $qcow_file --run-command "systemctl start sshd" 129 | echo "启用root登录..." 130 | virt-customize -a $qcow_file --run-command "sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config" 131 | virt-customize -a $qcow_file --run-command "sed -i 's/#PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config" 132 | virt-customize -a $qcow_file --run-command "sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config" 133 | virt-customize -a $qcow_file --run-command "sed -i 's/#Port 22/Port 22/g' /etc/ssh/sshd_config" 134 | virt-customize -a $qcow_file --run-command "sed -i 's/#AddressFamily any/AddressFamily any/g' /etc/ssh/sshd_config" 135 | virt-customize -a $qcow_file --run-command "sed -i 's/#ListenAddress 0.0.0.0/ListenAddress 0.0.0.0/g' /etc/ssh/sshd_config" 136 | virt-customize -a $qcow_file --run-command "sed -i 's/#ListenAddress ::/ListenAddress ::/g' /etc/ssh/sshd_config" 137 | virt-customize -a $qcow_file --run-command "service ssh restart" 138 | virt-customize -a $qcow_file --run-command "service sshd restart" 139 | virt-customize -a $qcow_file --run-command "systemctl restart sshd" 140 | virt-customize -a $qcow_file --run-command "systemctl restart ssh" 141 | fi 142 | echo "创建备份..." 143 | cp $qcow_file ${qcow_file}.bak 144 | echo "复制新文件..." 145 | cp $qcow_file ${qcow_file}.tmp 146 | echo "覆盖原文件..." 147 | mv ${qcow_file}.tmp $qcow_file 148 | rm -rf *.bak 149 | echo "$qcow_file修改完成" 150 | -------------------------------------------------------------------------------- /back/uninstallpve.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #from https://github.com/oneclickvirt/pve 3 | 4 | for vmid in $(qm list | awk '{if(NR>1) print $1}'); do qm stop $vmid; qm destroy $vmid; rm -rf /var/lib/vz/images/$vmid*; done 5 | iptables -t nat -F 6 | iptables -t filter -F 7 | service networking restart 8 | systemctl restart networking.service 9 | rm -rf vm* 10 | pct list | awk 'NR>1{print $1}' | xargs -I {} sh -c 'pct stop {}; pct destroy {}' 11 | rm -rf ct* 12 | iptables -t nat -F 13 | iptables -t filter -F 14 | service networking restart 15 | systemctl restart networking.service 16 | systemctl stop pve-cluster.service 17 | systemctl stop pvedaemon.service 18 | systemctl stop pvestatd.service 19 | systemctl stop pveproxy.service 20 | # apt-get remove --purge -y proxmox-ve 21 | apt-get remove --purge -y pve-manager 22 | apt-get remove --purge -y pve-kernel-4.15 23 | apt-get remove --purge -y pve-kernel-5.11 24 | apt-get remove --purge -y postfix 25 | apt-get remove --purge -y open-iscsi 26 | touch '/please-remove-proxmox-ve' 27 | apt-get purge proxmox-ve -y 28 | apt-get autoremove -y 29 | sudo dpkg --configure -a 30 | sudo apt-get install -f 31 | sudo dpkg --remove --force-remove-reinstreq initramfs-tools 32 | sudo apt-get purge initramfs-tools 33 | 34 | if [ -f /etc/network/interfaces.d/50-cloud-init.bak ]; then 35 | chattr -i /etc/network/interfaces.d/50-cloud-init 36 | mv /etc/network/interfaces.d/50-cloud-init.bak /etc/network/interfaces.d/50-cloud-init 37 | chattr +i /etc/network/interfaces.d/50-cloud-init 38 | fi 39 | systemctl stop check-dns.service 40 | systemctl disable check-dns.service 41 | rm /usr/local/bin/check-dns.sh 42 | rm /etc/systemd/system/check-dns.service 43 | if [ -f /etc/resolv.conf.bak ]; then 44 | chattr -i /etc/resolv.conf 45 | mv /etc/resolv.conf.bak /etc/resolv.conf 46 | chattr +i /etc/resolv.conf 47 | fi 48 | systemctl daemon-reload 49 | systemctl restart networking 50 | sed -i '/^deb.*pve-no-subscription/d' /etc/apt/sources.list 51 | rm -f /etc/apt/trusted.gpg.d/proxmox-ve-release-6.x.gpg 52 | rm -f /etc/apt/trusted.gpg.d/proxmox-release-bullseye.gpg 53 | rm -rf /etc/pve/ 54 | rm -rf /var/lib/vz/ 55 | rm -rf /var/lib/mysql/ 56 | rm -rf /var/log/pve/ 57 | rm -rf /var/log/mysql/ 58 | rm -rf /var/spool/postfix/ 59 | apt-get autoremove -y 60 | # reboot 61 | -------------------------------------------------------------------------------- /dockerfiles/Dockerfile_aarch64_7: -------------------------------------------------------------------------------- 1 | FROM debian:11 2 | 3 | # Set official Debian sources 4 | RUN rm /etc/apt/sources.list && 5 | echo "deb http://deb.debian.org/debian/ bullseye main contrib non-free" >>/etc/apt/sources.list && 6 | echo "deb http://deb.debian.org/debian/ bullseye-updates main contrib non-free" >>/etc/apt/sources.list && 7 | echo "deb http://deb.debian.org/debian/ bullseye-backports main contrib non-free" >>/etc/apt/sources.list && 8 | echo "deb http://security.debian.org/debian-security bullseye-security main contrib" >>/etc/apt/sources.list 9 | 10 | # Install base packages 11 | RUN apt-get update && 12 | apt-get install wget systemd nano vim curl gnupg ca-certificates -y 13 | 14 | #add proxmox repo 15 | #use jiangcuo_proxmox_arm64 https://github.com/jiangcuo/Proxmox-Arm64 16 | RUN echo "deb https://global.mirrors.apqa.cn/proxmox/ pvearm main" >/etc/apt/sources.list.d/foxi.list && 17 | wget --no-check-certificate https://global.mirrors.apqa.cn/proxmox/gpg.key -O /etc/apt/trusted.gpg.d/gpg.key && 18 | chmod +r /etc/apt/trusted.gpg.d/gpg.key 19 | 20 | #intall proxmox-ve without recommends. 21 | RUN apt-get update && 22 | DEBIAN_FRONTEND=noninteractiv apt-get -y --no-install-recommends install proxmox-ve || echo ok 23 | 24 | #set passwd for root 25 | RUN echo "root:root" | chpasswd 26 | 27 | #clean 28 | RUN rm -rf /var/lib/apt/lists/* /*.deb 29 | 30 | #use setup.sh to start proxmox service 31 | STOPSIGNAL SIGINT 32 | CMD [ "/lib/systemd/systemd", "log-level=info", "unit=sysinit.target"] 33 | -------------------------------------------------------------------------------- /dockerfiles/Dockerfile_x86_64_7: -------------------------------------------------------------------------------- 1 | FROM debian:11 2 | 3 | # Set official Debian sources 4 | RUN rm /etc/apt/sources.list && 5 | echo "deb http://deb.debian.org/debian/ bullseye main contrib non-free" >>/etc/apt/sources.list && 6 | echo "deb http://deb.debian.org/debian/ bullseye-updates main contrib non-free" >>/etc/apt/sources.list && 7 | echo "deb http://deb.debian.org/debian/ bullseye-backports main contrib non-free" >>/etc/apt/sources.list && 8 | echo "deb http://security.debian.org/debian-security bullseye-security main contrib" >>/etc/apt/sources.list 9 | 10 | # Install base packages 11 | RUN apt-get update && 12 | apt-get install wget systemd nano vim curl gnupg ca-certificates -y 13 | 14 | #add proxmox repo 15 | RUN echo "deb http://mirrors.ustc.edu.cn/proxmox/debian/ bullseye pve-no-subscription" >>/etc/apt/sources.list && 16 | wget --no-check-certificate https://mirrors.ustc.edu.cn/proxmox/debian/proxmox-release-bullseye.gpg -O /etc/apt/trusted.gpg.d/proxmox-release-bullseye.gpg && 17 | chmod +r /etc/apt/trusted.gpg.d/proxmox-release-bullseye.gpg 18 | 19 | #repacked proxmox-ve 20 | RUN wget https://mirrors.ustc.edu.cn/proxmox/debian/dists/bullseye/pve-no-subscription/binary-amd64/proxmox-ve_7.2-1_all.deb && 21 | mkdir /tmp/pve && 22 | dpkg -X proxmox-ve_7.2-1_all.deb /tmp/pve/ && 23 | dpkg -e proxmox-ve_7.2-1_all.deb /tmp/pve/DEBIAN && 24 | sed -i "s/pve-kernel-helper,//g" /tmp/pve/DEBIAN/control && 25 | sed -i "s/pve-kernel-5.15,//g" /tmp/pve/DEBIAN/control && 26 | dpkg-deb -Zxz -b /tmp/pve/ /tmp/ 27 | 28 | #repacked pve-manager 29 | RUN wget https://mirrors.ustc.edu.cn/proxmox/debian/dists/bullseye/pve-no-subscription/binary-amd64/pve-manager_7.2-7_amd64.deb && 30 | mkdir /tmp/pve-manager && 31 | dpkg -X pve-manager_7.2-7_amd64.deb /tmp/pve-manager/ && 32 | dpkg -e pve-manager_7.2-7_amd64.deb /tmp/pve-manager/DEBIAN && 33 | sed -i "s/ifupdown2 (>= 2.0.1-1+pve8) | ifenslave (>= 2.6),//g" /tmp/pve-manager/DEBIAN/control && 34 | dpkg-deb -Zxz -b /tmp/pve-manager/ /tmp 35 | 36 | #intall proxmox-ve without recommends. ifupdown2 will install failed but ok 37 | RUN apt-get update && 38 | DEBIAN_FRONTEND=noninteractiv apt-get -y --no-install-recommends install proxmox-ve || echo ok 39 | 40 | ##install again 41 | RUN dpkg -i /tmp/*.deb || echo ok 42 | 43 | #set passwd for root 44 | RUN echo "root:root" | chpasswd 45 | 46 | #clean 47 | RUN rm -rf /var/lib/apt/lists/* /*.deb 48 | 49 | #use setup.sh to start proxmox service 50 | STOPSIGNAL SIGINT 51 | CMD [ "/lib/systemd/systemd", "log-level=info", "unit=sysinit.target"] 52 | -------------------------------------------------------------------------------- /dockerfiles/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2024.03.12 5 | 6 | ########## 预设部分输出和部分中间变量 7 | 8 | cd /root >/dev/null 2>&1 9 | _red() { echo -e "\033[31m\033[01m$@\033[0m"; } 10 | _green() { echo -e "\033[32m\033[01m$@\033[0m"; } 11 | _yellow() { echo -e "\033[33m\033[01m$@\033[0m"; } 12 | _blue() { echo -e "\033[36m\033[01m$@\033[0m"; } 13 | reading() { read -rp "$(_green "$1")" "$2"; } 14 | export DEBIAN_FRONTEND=noninteractive 15 | utf8_locale=$(locale -a 2>/dev/null | grep -i -m 1 -E "UTF-8|utf8") 16 | if [[ -z "$utf8_locale" ]]; then 17 | echo "No UTF-8 locale found" 18 | else 19 | export LC_ALL="$utf8_locale" 20 | export LANG="$utf8_locale" 21 | export LANGUAGE="$utf8_locale" 22 | echo "Locale set to $utf8_locale" 23 | fi 24 | temp_file_apt_fix="/tmp/apt_fix.txt" 25 | 26 | ########## 定义部分需要使用的函数 27 | 28 | install_package() { 29 | package_name=$1 30 | if command -v $package_name >/dev/null 2>&1; then 31 | _green "$package_name already installed" 32 | _green "$package_name 已经安装" 33 | else 34 | apt-get install -o Dpkg::Options::="--force-confnew" -y $package_name 35 | if [ $? -ne 0 ]; then 36 | apt_output=$(apt-get install -y $package_name --fix-missing 2>&1) 37 | fi 38 | if [ $? -ne 0 ]; then 39 | if echo "$apt_output" | grep -qE 'DEBIAN_FRONTEND=dialog dpkg --configure grub-pc' && 40 | echo "$apt_output" | grep -qE 'dpkg --configure -a' && 41 | echo "$apt_output" | grep -qE 'dpkg: error processing package grub-pc \(--configure\):'; then 42 | # 手动选择 43 | # DEBIAN_FRONTEND=dialog dpkg --configure grub-pc 44 | # 设置debconf的选择 45 | echo "grub-pc grub-pc/install_devices multiselect /dev/sda" | sudo debconf-set-selections 46 | # 配置grub-pc并自动选择第一个选项确认 47 | sudo DEBIAN_FRONTEND=noninteractive dpkg --configure grub-pc 48 | dpkg --configure -a 49 | if [ $? -ne 0 ]; then 50 | _green "$package_name tried to install but failed, exited the program" 51 | _green "$package_name 已尝试安装但失败,退出程序" 52 | exit 1 53 | fi 54 | apt-get install -y $package_name --fix-missing 55 | fi 56 | fi 57 | if [ $? -ne 0 ]; then 58 | _green "$package_name tried to install but failed, exited the program" 59 | _green "$package_name 已尝试安装但失败,退出程序" 60 | exit 1 61 | fi 62 | _green "$package_name tried to install" 63 | _green "$package_name 已尝试安装" 64 | fi 65 | } 66 | 67 | check_haveged() { 68 | _yellow "checking haveged" 69 | if ! command -v haveged >/dev/null 2>&1; then 70 | apt-get install -o Dpkg::Options::="--force-confnew" -y haveged 71 | fi 72 | if which systemctl >/dev/null 2>&1; then 73 | systemctl disable --now haveged 74 | systemctl enable --now haveged 75 | else 76 | service haveged stop 77 | service haveged start 78 | fi 79 | } 80 | 81 | check_time_zone() { 82 | _yellow "adjusting the time" 83 | systemctl stop ntpd 84 | service ntpd stop 85 | if ! command -v chronyd >/dev/null 2>&1; then 86 | apt-get install -o Dpkg::Options::="--force-confnew" -y chrony 87 | fi 88 | if which systemctl >/dev/null 2>&1; then 89 | systemctl stop chronyd 90 | chronyd -q 91 | systemctl start chronyd 92 | else 93 | service chronyd stop 94 | chronyd -q 95 | service chronyd start 96 | fi 97 | sleep 0.5 98 | } 99 | 100 | check_cdn() { 101 | local o_url=$1 102 | local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) # 打乱数组顺序 103 | for cdn_url in "${shuffled_cdn_urls[@]}"; do 104 | if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then 105 | export cdn_success_url="$cdn_url" 106 | return 107 | fi 108 | sleep 0.5 109 | done 110 | export cdn_success_url="" 111 | } 112 | 113 | 114 | check_cdn_file() { 115 | check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" 116 | if [ -n "$cdn_success_url" ]; then 117 | _yellow "CDN available, using CDN" 118 | else 119 | _yellow "No CDN available, no use CDN" 120 | fi 121 | } 122 | 123 | is_private_ipv4() { 124 | local ip_address=$1 125 | local ip_parts 126 | if [[ -z $ip_address ]]; then 127 | return 0 # 输入为空 128 | fi 129 | IFS='.' read -r -a ip_parts <<<"$ip_address" 130 | # 检查IP地址是否符合内网IP地址的范围 131 | # 去除 回环,REC 1918,多播 地址 132 | if [[ ${ip_parts[0]} -eq 10 ]] || 133 | [[ ${ip_parts[0]} -eq 172 && ${ip_parts[1]} -ge 16 && ${ip_parts[1]} -le 31 ]] || 134 | [[ ${ip_parts[0]} -eq 192 && ${ip_parts[1]} -eq 168 ]] || 135 | [[ ${ip_parts[0]} -eq 127 ]] || 136 | [[ ${ip_parts[0]} -eq 0 ]] || 137 | [[ ${ip_parts[0]} -ge 224 ]]; then 138 | return 0 # 是内网IP地址 139 | else 140 | return 1 # 不是内网IP地址 141 | fi 142 | } 143 | 144 | check_ipv4() { 145 | IPV4=$(ip -4 addr show | grep global | awk '{print $2}' | cut -d '/' -f1 | head -n 1) 146 | if is_private_ipv4 "$IPV4"; then # 由于是内网IPV4地址,需要通过API获取外网地址 147 | IPV4="" 148 | local API_NET=("ipv4.ip.sb" "ipget.net" "ip.ping0.cc" "https://ip4.seeip.org" "https://api.my-ip.io/ip" "https://ipv4.icanhazip.com" "api.ipify.org") 149 | for p in "${API_NET[@]}"; do 150 | response=$(curl -s4m8 "$p") 151 | sleep 1 152 | if [ $? -eq 0 ] && ! echo "$response" | grep -q "error"; then 153 | IP_API="$p" 154 | IPV4="$response" 155 | break 156 | fi 157 | done 158 | fi 159 | export IPV4 160 | } 161 | 162 | statistics_of_run_times() { 163 | COUNT=$(curl -4 -ksm1 "https://hits.spiritlhl.net/pve?action=hit&title=Hits&title_bg=%23555555&count_bg=%2324dde1&edge_flat=false" 2>/dev/null || 164 | curl -6 -ksm1 "https://hits.spiritlhl.net/pve?action=hit&title=Hits&title_bg=%23555555&count_bg=%2324dde1&edge_flat=false" 2>/dev/null) 165 | TODAY=$(echo "$COUNT" | grep -oP '"daily":\s*[0-9]+' | sed 's/"daily":\s*\([0-9]*\)/\1/') 166 | TOTAL=$(echo "$COUNT" | grep -oP '"total":\s*[0-9]+' | sed 's/"total":\s*\([0-9]*\)/\1/') 167 | } 168 | 169 | get_system_arch() { 170 | local sysarch="$(uname -m)" 171 | if [ "${sysarch}" = "unknown" ] || [ "${sysarch}" = "" ]; then 172 | local sysarch="$(arch)" 173 | fi 174 | # 根据架构信息设置系统位数并下载文件,其余 * 包括了 x86_64 175 | case "${sysarch}" in 176 | "i386" | "i686" | "x86_64") 177 | system_arch="x86" 178 | ;; 179 | "armv7l" | "armv8" | "armv8l" | "aarch64") 180 | system_arch="arch" 181 | ;; 182 | *) 183 | system_arch="" 184 | ;; 185 | esac 186 | } 187 | 188 | check_china() { 189 | _yellow "IP area being detected ......" 190 | if [[ -z "${CN}" ]]; then 191 | if [[ $(curl -m 6 -s https://ipapi.co/json | grep 'China') != "" ]]; then 192 | _yellow "根据ipapi.co提供的信息,当前IP可能在中国" 193 | read -e -r -p "是否选用中国镜像完成相关组件安装? ([y]/n) " input 194 | case $input in 195 | [yY][eE][sS] | [yY]) 196 | echo "使用中国镜像" 197 | CN=true 198 | ;; 199 | [nN][oO] | [nN]) 200 | echo "不使用中国镜像" 201 | ;; 202 | *) 203 | echo "使用中国镜像" 204 | CN=true 205 | ;; 206 | esac 207 | fi 208 | fi 209 | } 210 | 211 | ########## 前置环境检测和组件安装 212 | 213 | # 更改网络优先级为IPV4优先 214 | sed -i 's/.*precedence ::ffff:0:0\/96.*/precedence ::ffff:0:0\/96 100/g' /etc/gai.conf && systemctl restart networking 215 | 216 | install_package curl 217 | install_package sudo 218 | 219 | # cdn检测 220 | cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn3.spiritlhl.net/" "http://cdn1.spiritlhl.net/" "https://ghproxy.com/" "http://cdn2.spiritlhl.net/") 221 | check_cdn_file 222 | 223 | if ! command -v docker >/dev/null 2>&1; then 224 | _yellow "Installing docker" 225 | curl -sSL https://get.docker.com/ | sh 226 | fi 227 | if ! command -v docker-compose >/dev/null 2>&1; then 228 | _yellow "Installing docker-compose" 229 | curl -Lk "https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$(uname -m)" -o /usr/local/bin/docker-compose 230 | chmod +x /usr/local/bin/docker-compose 231 | docker-compose --version 232 | fi 233 | 234 | # Dockerfile_x86_64_7 235 | # Dockerfile_aarch64_7 236 | tag="x86_64_7" 237 | docker_file_name="Dockerfile_x86_64_7" 238 | curl -Lk "${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/dockerfiles/${docker_file_name}" -o /root/Dockerfile 239 | docker build -t "spiritlhl/proxmoxve:${tag}" -f /root/Dockerfile . 240 | -------------------------------------------------------------------------------- /extra_scripts/check-dns.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Check DNS service 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/local/bin/check-dns.sh 7 | 8 | [Install] 9 | WantedBy=multi-user.target 10 | -------------------------------------------------------------------------------- /extra_scripts/check-dns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2024.03.12 5 | 6 | sleep 3 7 | DNS_SERVER="8.8.8.8" 8 | RESOLV_CONF="/etc/resolv.conf" 9 | ipv6_address=$(cat /usr/local/bin/pve_check_ipv6) 10 | ipv6_prefixlen=$(cat /usr/local/bin/pve_ipv6_prefixlen) 11 | ipv6_gateway=$(cat /usr/local/bin/pve_ipv6_gateway) 12 | grep -q "^nameserver ${DNS_SERVER}$" ${RESOLV_CONF} 13 | if [ $? -eq 0 ]; then 14 | echo "DNS server ${DNS_SERVER} already exists in ${RESOLV_CONF}." 15 | else 16 | echo "Adding DNS server ${DNS_SERVER} to ${RESOLV_CONF}..." 17 | if [ -z "$ipv6_address" ] || [ -z "$ipv6_prefixlen" ] || [ -z "$ipv6_gateway" ]; then 18 | echo -e "\nnameserver 8.8.8.8\nnameserver 8.8.4.4\n" >>${RESOLV_CONF} 19 | else 20 | echo -e "\nnameserver 8.8.8.8\nnameserver 8.8.4.4\nnameserver 2606:4700:4700::1111\nnameserver 2001:4860:4860::8888\nnameserver 2001:4860:4860::8844" >>${RESOLV_CONF} 21 | fi 22 | fi 23 | sleep 3 24 | if grep -q "vmbr0" "/etc/network/interfaces"; then 25 | resolvconf -a vmbr0 < ${RESOLV_CONF} 26 | fi 27 | -------------------------------------------------------------------------------- /extra_scripts/clear_interface_route_cache.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Clear interface route cache on next reboot 3 | After=network.target 4 | 5 | [Service] 6 | Type=oneshot 7 | ExecStart=/usr/local/bin/clear_interface_route_cache.sh 8 | ExecStartPost=/usr/bin/env reboot 9 | RemainAfterExit=yes 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /extra_scripts/clear_interface_route_cache.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2024.03.12 5 | 6 | # 清理路由缓存 7 | sleep 5 8 | systemctl stop networking.service 9 | ip addr flush dev eth0 10 | systemctl start networking.service 11 | 12 | # 删除Systemd服务 13 | systemctl disable clear_interface_route_cache.service 14 | rm /etc/systemd/system/clear_interface_route_cache.service 15 | 16 | # 删除自身 17 | rm $0 18 | -------------------------------------------------------------------------------- /extra_scripts/configure_macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from https://github.com/oneclickvirt/pve 3 | # 2025.05.08 4 | 5 | echo 1 | tee /sys/module/kvm/parameters/ignore_msrs 6 | grep -Fxq 'options kvm ignore_msrs=Y' /etc/modprobe.d/kvm.conf || echo 'options kvm ignore_msrs=Y' >> /etc/modprobe.d/kvm.conf && update-initramfs -k all -u 7 | if [ "$(lscpu | grep -i 'Vendor ID' | grep -i amd | wc -l)" -eq 1 ]; then 8 | CPU_TYPE="AMD" 9 | else 10 | CPU_TYPE="INTEL" 11 | fi 12 | if [ ! -e /etc/pve/qemu-server/.macos_preset ]; then 13 | echo "未检测到预设配置,正在安装依赖与配置系统(Installing prerequisites and configuring system)..." 14 | apt update && apt install -y vim sysstat parted iptraf 15 | if [ $? -ne 0 ]; then 16 | echo "软件包安装失败(Package installation failed)" 17 | exit 1 18 | fi 19 | echo "set mouse-=a" > ~/.vimrc 20 | sed -i 's/GRUB_TIMEOUT=5/GRUB_TIMEOUT=0/g' /etc/default/grub 21 | PVE_VERSION=$(pveversion) 22 | if [ "$CPU_TYPE" == "AMD" ]; then 23 | if echo "$PVE_VERSION" | grep -qE '7\.[2-4]|8\.[0-4]'; then 24 | CMDLINE='quiet amd_iommu=on iommu=pt video=vesafb:off video=efifb:off initcall_blacklist=sysfb_init' 25 | else 26 | CMDLINE='quiet amd_iommu=on iommu=pt video=vesafb:off video=efifb:off' 27 | fi 28 | echo "options kvm-amd nested=1" > /etc/modprobe.d/kvm-amd.conf 29 | else 30 | if echo "$PVE_VERSION" | grep -qE '7\.[2-4]|8\.[0-4]'; then 31 | CMDLINE='quiet intel_iommu=on iommu=pt video=vesafb:off video=efifb:off initcall_blacklist=sysfb_init' 32 | else 33 | CMDLINE='quiet intel_iommu=on iommu=pt video=vesafb:off video=efifb:off' 34 | fi 35 | echo "options kvm-intel nested=Y" > /etc/modprobe.d/kvm-intel.conf 36 | fi 37 | sed -i "s/GRUB_CMDLINE_LINUX_DEFAULT=\"quiet\"/GRUB_CMDLINE_LINUX_DEFAULT=\"$CMDLINE\"/g" /etc/default/grub 38 | cat <> /etc/modules 39 | vfio 40 | vfio_iommu_type1 41 | vfio_pci 42 | vfio_virqfd 43 | EOF 44 | cat <> /etc/modprobe.d/pve-blacklist.conf 45 | blacklist nouveau 46 | blacklist nvidia 47 | blacklist snd_hda_codec_hdmi 48 | blacklist snd_hda_intel 49 | blacklist snd_hda_codec 50 | blacklist snd_hda_core 51 | blacklist radeon 52 | blacklist amdgpu 53 | EOF 54 | echo "options kvm ignore_msrs=Y report_ignored_msrs=0" > /etc/modprobe.d/kvm.conf 55 | echo "options vfio_iommu_type1 allow_unsafe_interrupts=1" > /etc/modprobe.d/iommu_unsafe_interrupts.conf 56 | sed -i.backup -z "s/res === null || res === undefined || \!res || res\n\t\t\t.data.status.toLowerCase() \!== 'active'/false/g" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js 57 | touch /etc/pve/qemu-server/.macos_preset 58 | update-grub 59 | echo "配置完成,15 秒后重启(Configuration completed. Rebooting in 15 seconds)..." 60 | sleep 15 && reboot 61 | fi 62 | -------------------------------------------------------------------------------- /extra_scripts/configure_network.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Configure Network 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/bin/bash /usr/local/bin/configure_network.sh 7 | 8 | [Install] 9 | WantedBy=default.target 10 | -------------------------------------------------------------------------------- /extra_scripts/configure_network.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2024.03.12 5 | 6 | # 检查是否存在 "iface eth0 inet6 auto" 行 7 | if ! grep -q "iface eth0 inet6 auto" /etc/network/interfaces; then 8 | # 追加 "iface eth0 inet6 auto" 行到文件末尾 9 | chattr -i /etc/network/interfaces 10 | echo "iface eth0 inet6 auto" >>/etc/network/interfaces 11 | chattr +i /etc/network/interfaces 12 | fi 13 | 14 | if ! grep -q "pre-up echo 2 > /proc/sys/net/ipv6/conf/all/accept_ra" /etc/network/interfaces; then 15 | # 追加 "pre-up echo 2 > /proc/sys/net/ipv6/conf/all/accept_ra" 行到文件末尾 16 | chattr -i /etc/network/interfaces 17 | echo "pre-up echo 2 > /proc/sys/net/ipv6/conf/all/accept_ra" >>/etc/network/interfaces 18 | chattr +i /etc/network/interfaces 19 | fi 20 | 21 | # 重新加载网络配置 22 | ifreload -ad 23 | -------------------------------------------------------------------------------- /extra_scripts/ifupdown2-install.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Install ifupdown2 on next reboot 3 | After=network.target 4 | 5 | [Service] 6 | Type=oneshot 7 | ExecStartPre=/usr/bin/env sleep 5 8 | ExecStart=/usr/local/bin/install_ifupdown2.sh 9 | ExecStartPost=/usr/bin/env reboot 10 | RemainAfterExit=yes 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /extra_scripts/install_ifupdown2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2024.03.12 5 | 6 | # 安装ifupdown2 7 | apt-get install -y ifupdown2 8 | echo "1" >"/usr/local/bin/ifupdown2_installed.txt" 9 | 10 | # 删除Systemd服务 11 | systemctl disable ifupdown2-install.service 12 | rm /etc/systemd/system/ifupdown2-install.service 13 | 14 | # 删除自身 15 | rm $0 16 | -------------------------------------------------------------------------------- /extra_scripts/ndpresponder.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=NDPPD Daemon 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/local/bin/ndpresponder -i vmbr0 -n [IPv6前缀]/[IPV6子网掩码] 7 | Restart=on-failure 8 | RestartSec=6 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /gpg/README.md: -------------------------------------------------------------------------------- 1 | # gpg归档 2 | -------------------------------------------------------------------------------- /gpg/proxmox-release-bullseye.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oneclickvirt/pve/3a878def51ea775f4334fbb4390991b0f53998b6/gpg/proxmox-release-bullseye.gpg -------------------------------------------------------------------------------- /gpg/proxmox-release-buster.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oneclickvirt/pve/3a878def51ea775f4334fbb4390991b0f53998b6/gpg/proxmox-release-buster.gpg -------------------------------------------------------------------------------- /scripts/build_backend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2025.05.09 5 | 6 | # 打印信息 7 | _red() { echo -e "\033[31m\033[01m$@\033[0m"; } 8 | _green() { echo -e "\033[32m\033[01m$@\033[0m"; } 9 | _yellow() { echo -e "\033[33m\033[01m$@\033[0m"; } 10 | _blue() { echo -e "\033[36m\033[01m$@\033[0m"; } 11 | reading() { read -rp "$(_green "$1")" "$2"; } 12 | utf8_locale=$(locale -a 2>/dev/null | grep -i -m 1 -E "UTF-8|utf8") 13 | if [[ -z "$utf8_locale" ]]; then 14 | echo "No UTF-8 locale found" 15 | else 16 | export LC_ALL="$utf8_locale" 17 | export LANG="$utf8_locale" 18 | export LANGUAGE="$utf8_locale" 19 | echo "Locale set to $utf8_locale" 20 | fi 21 | 22 | if [ -f "/usr/local/bin/build_backend_pve.txt" ]; then 23 | _green "You have already executed this script, if you have already rebooted your system, please execute the subsequent script commands to automatically configure the gateway, if you have not rebooted your system, please reboot your system" 24 | _green "Do not run this script repeatedly" 25 | _green "你已执行过本脚本,如果已重启过系统,请执行后续的自动配置网关的脚本命令,如果未重启过系统,请重启系统" 26 | _green "不要重复运行本脚本" 27 | exit 1 28 | fi 29 | 30 | # 创建资源池 31 | POOL_ID="mypool" 32 | if pvesh get /pools/$POOL_ID >/dev/null 2>&1; then 33 | _green "Resource pool $POOL_ID already exists!" 34 | _green "资源池 $POOL_ID 已经存在!" 35 | else 36 | # 如果不存在则创建 37 | _green "Creating resource pool $POOL_ID..." 38 | _green "正在创建资源池 $POOL_ID..." 39 | pvesh create /pools --poolid $POOL_ID 40 | _green "Resource pool $POOL_ID has been created!" 41 | _green "资源池 $POOL_ID 已创建!" 42 | fi 43 | 44 | # 移除订阅弹窗 45 | pve_version=$(dpkg-query -f '${Version}' -W proxmox-ve 2>/dev/null | cut -d'-' -f1) 46 | cp -rf /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.bak 47 | sed -Ezi.bak "s/(Ext.Msg.show\(\{\s+title: gettext\('No valid sub)/void\(\{ \/\/\1/g" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js 48 | sed -i.bak "s/data.status !== 'Active'/false/g" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js 49 | 50 | # 开启硬件直通 51 | if [ $(dmesg | grep -e DMAR -e IOMMU | wc -l) = 0 ]; then 52 | _yellow "Hardware does not support passthrough" 53 | _yellow "硬件不支持直通" 54 | else 55 | if [ $(cat /proc/cpuinfo | grep Intel | wc -l) = 0 ]; then 56 | iommu="amd_iommu=on" 57 | else 58 | iommu="intel_iommu=on" 59 | fi 60 | if [ $(grep $iommu /etc/default/grub | wc -l) = 0 ]; then 61 | sed -i 's|quiet|quiet '$iommu'|' /etc/default/grub 62 | update-grub 63 | if [ $(grep "vfio" /etc/modules | wc -l) = 0 ]; then 64 | echo 'vfio 65 | vfio_iommu_type1 66 | vfio_pci 67 | vfio_virqfd' >>/etc/modules 68 | fi 69 | else 70 | _green "Hardware passthrough is set" 71 | _green "已设置硬件直通" 72 | fi 73 | fi 74 | 75 | # 检测固件安装 76 | arch=$(uname -m) 77 | if [[ "$arch" == "arm"* || "$arch" == "aarch64" ]]; then 78 | pve_version=$(pveversion | cut -d '/' -f 2) 79 | major=$(echo "$pve_version" | cut -d '.' -f 1) 80 | minor=$(echo "$pve_version" | cut -d '.' -f 2 | cut -d '-' -f 1) 81 | version=$(echo "$major.$minor" | bc) 82 | _green "Detected architecture: $arch" 83 | _green "Detected Proxmox VE version: $version" 84 | if (($(echo "$version < 8.1" | bc -l))); then 85 | _green "Installing pve-edk2-firmware for Proxmox VE < 8.1..." 86 | apt download pve-edk2-firmware=3.20220526-1 87 | dpkg -i pve-edk2-firmware_3.20220526-1_all.deb 88 | else 89 | _green "Installing pve-edk2-firmware-aarch64 for Proxmox VE >= 8.1..." 90 | apt download pve-edk2-firmware-aarch64=3.20220526-rockchip 91 | dpkg -i pve-edk2-firmware-aarch64_3.20220526-rockchip_all.deb 92 | fi 93 | fi 94 | 95 | # 检测AppArmor模块 96 | if ! dpkg -s apparmor >/dev/null 2>&1; then 97 | _green "AppArmor is being installed..." 98 | _green "正在安装 AppArmor..." 99 | apt-get update 100 | apt-get install -y apparmor 101 | fi 102 | if [ $? -ne 0 ]; then 103 | apt-get install -y apparmor --fix-missing 104 | fi 105 | if ! systemctl is-active --quiet apparmor.service; then 106 | _green "Starting the AppArmor service..." 107 | _green "启动 AppArmor 服务..." 108 | systemctl enable apparmor.service 109 | systemctl start apparmor.service 110 | fi 111 | if ! lsmod | grep -q apparmor; then 112 | _green "Loading AppArmor kernel module..." 113 | _green "正在加载 AppArmor 内核模块..." 114 | modprobe apparmor 115 | fi 116 | sleep 3 117 | installed_kernels=($(dpkg -l 'pve-kernel-*' | awk '/^ii/ {print $2}' | cut -d'-' -f3- | sort -V)) 118 | if [ ${#installed_kernels[@]} -gt 0 ]; then 119 | latest_kernel=${installed_kernels[-1]} 120 | _green "PVE latest kernel: $latest_kernel" 121 | _yellow "Please execute reboot to reboot the system to load the PVE kernel." 122 | _yellow "请执行 reboot 重新启动系统加载PVE内核" 123 | else 124 | _yellow "The current kernel is already a PVE kernel, no need to reboot the system to update the kernel" 125 | _yellow "当前内核已是PVE内核,无需重启系统更新内核" 126 | _yellow "However, a reboot will ensure that some of the hidden settings are loaded successfully, so be sure to reboot the server once if you are in a position to do so." 127 | _yellow "但重启可以保证部分隐藏设置加载成功,有条件务必重启一次服务器" 128 | fi 129 | echo "1" >"/usr/local/bin/build_backend_pve.txt" 130 | -------------------------------------------------------------------------------- /scripts/buildct.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2025.05.17 5 | 6 | # ./buildct.sh CTID 密码 CPU核数 内存 硬盘 SSH端口 80端口 443端口 外网端口起 外网端口止 系统 存储盘 独立IPV6 7 | # ./buildct.sh 102 1234567 1 512 5 20001 20002 20003 30000 30025 debian11 local N 8 | 9 | cd /root >/dev/null 2>&1 10 | 11 | init() { 12 | CTID="${1:-102}" 13 | password="${2:-123456}" 14 | core="${3:-1}" 15 | memory="${4:-512}" 16 | disk="${5:-5}" 17 | sshn="${6:-20001}" 18 | web1_port="${7:-20002}" 19 | web2_port="${8:-20003}" 20 | port_first="${9:-29975}" 21 | port_last="${10:-30000}" 22 | system_ori="${11:-debian11}" 23 | storage="${12:-local}" 24 | independent_ipv6="${13:-N}" 25 | independent_ipv6=$(echo "$independent_ipv6" | tr '[:upper:]' '[:lower:]') 26 | rm -rf "ct$name" 27 | en_system=$(echo "$system_ori" | sed 's/[0-9]*//g; s/\.$//') 28 | num_system=$(echo "$system_ori" | sed 's/[a-zA-Z]*//g') 29 | system="$en_system-$num_system" 30 | } 31 | 32 | check_cdn() { 33 | local o_url=$1 34 | local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) 35 | for cdn_url in "${shuffled_cdn_urls[@]}"; do 36 | if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then 37 | export cdn_success_url="$cdn_url" 38 | return 39 | fi 40 | sleep 0.5 41 | done 42 | export cdn_success_url="" 43 | } 44 | 45 | check_cdn_file() { 46 | check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" 47 | if [ -n "$cdn_success_url" ]; then 48 | echo "CDN available, using CDN" 49 | else 50 | echo "No CDN available, no use CDN" 51 | fi 52 | } 53 | 54 | download_with_retry() { 55 | local url="$1" 56 | local output="$2" 57 | local max_attempts=5 58 | local attempt=1 59 | local delay=1 60 | while [ $attempt -le $max_attempts ]; do 61 | wget -q "$url" -O "$output" && return 0 62 | echo "Download failed: $url, try $attempt, wait $delay seconds and retry..." 63 | echo "下载失败:$url,尝试第 $attempt 次,等待 $delay 秒后重试..." 64 | sleep $delay 65 | attempt=$((attempt + 1)) 66 | delay=$((delay * 2)) 67 | [ $delay -gt 30 ] && delay=30 68 | done 69 | echo -e "\e[31mDownload failed: $url, maximum number of attempts exceeded ($max_attempts)\e[0m" 70 | echo -e "\e[31m下载失败:$url,超过最大尝试次数 ($max_attempts)\e[0m" 71 | return 1 72 | } 73 | 74 | load_default_config() { 75 | local config_url="${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/default_ct_config.sh" 76 | local config_file="default_ct_config.sh" 77 | if download_with_retry "$config_url" "$config_file"; then 78 | . "./$config_file" 79 | else 80 | echo -e "\e[31mUnable to load default configuration, script terminated.\e[0m" 81 | echo -e "\e[31m无法加载默认配置,脚本终止。\e[0m" 82 | exit 1 83 | fi 84 | } 85 | 86 | create_container() { 87 | user_ip="172.16.1.${CTID}" 88 | if [ "$fixed_system" = true ]; then 89 | pct create $CTID /var/lib/vz/template/cache/${system_name} -cores $core -cpuunits 1024 -memory $memory -swap 128 -rootfs ${storage}:${disk} -onboot 1 -password $password -features nesting=1 90 | else 91 | pct create $CTID ${storage}:vztmpl/${system_name} -cores $core -cpuunits 1024 -memory $memory -swap 128 -rootfs ${storage}:${disk} -onboot 1 -password $password -features nesting=1 92 | fi 93 | pct start $CTID 94 | sleep 5 95 | pct set $CTID --hostname $CTID 96 | } 97 | 98 | configure_networking() { 99 | independent_ipv6_status="N" 100 | if [ "$independent_ipv6" == "y" ]; then 101 | if [ ! -z "$host_ipv6_address" ] && [ ! -z "$ipv6_prefixlen" ] && [ ! -z "$ipv6_gateway" ] && [ ! -z "$ipv6_address_without_last_segment" ]; then 102 | if grep -q "vmbr2" /etc/network/interfaces; then 103 | pct set $CTID --net0 name=eth0,ip=${user_ip}/24,bridge=vmbr1,gw=172.16.1.1 104 | pct set $CTID --net1 name=eth1,ip6="${ipv6_address_without_last_segment}${CTID}/128",bridge=vmbr2,gw6="${host_ipv6_address}" 105 | pct set $CTID --nameserver 1.1.1.1 106 | pct set $CTID --searchdomain local 107 | independent_ipv6_status="Y" 108 | fi 109 | fi 110 | fi 111 | if [ "$independent_ipv6_status" == "N" ]; then 112 | pct set $CTID --net0 name=eth0,ip=${user_ip}/24,bridge=vmbr1,gw=172.16.1.1 113 | pct set $CTID --nameserver 1.1.1.1 114 | pct set $CTID --searchdomain local 115 | fi 116 | sleep 3 117 | } 118 | 119 | change_mirrors() { 120 | pct exec $CTID -- curl -lk https://gitee.com/SuperManito/LinuxMirrors/raw/main/ChangeMirrors.sh -o ChangeMirrors.sh 121 | pct exec $CTID -- chmod 777 ChangeMirrors.sh 122 | pct exec $CTID -- ./ChangeMirrors.sh --source mirrors.tuna.tsinghua.edu.cn --web-protocol http --intranet false --close-firewall true --backup true --updata-software false --clean-cache false --ignore-backup-tips > /dev/null 123 | pct exec $CTID -- rm -rf ChangeMirrors.sh 124 | } 125 | 126 | install_packages() { 127 | local pkg_manager=$1 128 | local packages=$2 129 | if [[ -z "${CN}" || "${CN}" != true ]]; then 130 | pct exec $CTID -- $pkg_manager update -y 131 | pct exec $CTID -- $pkg_manager install -y $packages 132 | else 133 | if [[ "$packages" == *"curl"* ]]; then 134 | pct exec $CTID -- $pkg_manager install -y curl 135 | fi 136 | change_mirrors 137 | pct exec $CTID -- $pkg_manager install -y $packages 138 | fi 139 | } 140 | 141 | setup_ssh() { 142 | local system_type=$1 143 | if echo "$system_type" | grep -qiE "alpine|archlinux|gentoo|openwrt" >/dev/null 2>&1; then 144 | pct exec $CTID -- curl -L ${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/ssh_sh.sh -o ssh_sh.sh 145 | pct exec $CTID -- chmod 777 ssh_sh.sh 146 | pct exec $CTID -- dos2unix ssh_sh.sh 147 | pct exec $CTID -- bash ssh_sh.sh 148 | else 149 | pct exec $CTID -- curl -L ${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/ssh_bash.sh -o ssh_bash.sh 150 | pct exec $CTID -- chmod 777 ssh_bash.sh 151 | pct exec $CTID -- dos2unix ssh_bash.sh 152 | pct exec $CTID -- bash ssh_bash.sh 153 | fi 154 | } 155 | 156 | check_network() { 157 | public_network_check_res=$(pct exec $CTID -- curl -lk -m 6 ${cdn_success_url}https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test) 158 | if [[ $public_network_check_res == *"success"* ]]; then 159 | echo "network is public" 160 | else 161 | echo "nameserver 8.8.8.8" | pct exec $CTID -- tee -a /etc/resolv.conf 162 | sleep 1 163 | pct exec $CTID -- curl -lk -m 6 ${cdn_success_url}https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test 164 | fi 165 | } 166 | 167 | restart_ssh() { 168 | ssh_check_res=$(pct exec $CTID -- lsof -i:22) 169 | if [[ $ssh_check_res == *"ssh"* ]]; then 170 | echo "ssh config correct" 171 | else 172 | pct exec $CTID -- service ssh restart 173 | pct exec $CTID -- service sshd restart 174 | sleep 2 175 | pct exec $CTID -- systemctl restart sshd 176 | pct exec $CTID -- systemctl restart ssh 177 | fi 178 | } 179 | 180 | configure_os() { 181 | if [ "$fixed_system" = true ]; then 182 | if [[ "${CN}" == true ]]; then 183 | change_mirrors 184 | fi 185 | sleep 2 186 | check_network 187 | sleep 2 188 | restart_ssh 189 | else 190 | if echo "$system" | grep -qiE "centos|almalinux|rockylinux" >/dev/null 2>&1; then 191 | install_packages "yum" "dos2unix curl" 192 | elif echo "$system" | grep -qiE "fedora" >/dev/null 2>&1; then 193 | install_packages "dnf" "dos2unix curl" 194 | elif echo "$system" | grep -qiE "opensuse" >/dev/null 2>&1; then 195 | install_packages "zypper --non-interactive" "dos2unix curl" 196 | elif echo "$system" | grep -qiE "alpine|archlinux" >/dev/null 2>&1; then 197 | if [[ "${CN}" == true ]]; then 198 | pct exec $CTID -- wget https://gitee.com/SuperManito/LinuxMirrors/raw/main/ChangeMirrors.sh 199 | pct exec $CTID -- chmod 777 ChangeMirrors.sh 200 | pct exec $CTID -- ./ChangeMirrors.sh --source mirrors.tuna.tsinghua.edu.cn --web-protocol http --intranet false --close-firewall true --backup true --updata-software false --clean-cache false --ignore-backup-tips > /dev/null 201 | pct exec $CTID -- rm -rf ChangeMirrors.sh 202 | fi 203 | elif echo "$system" | grep -qiE "ubuntu|debian|devuan" >/dev/null 2>&1; then 204 | if [[ -z "${CN}" || "${CN}" != true ]]; then 205 | pct exec $CTID -- apt-get update -y 206 | pct exec $CTID -- dpkg --configure -a 207 | pct exec $CTID -- apt-get update 208 | pct exec $CTID -- apt-get install dos2unix curl -y 209 | else 210 | pct exec $CTID -- apt-get install curl -y --fix-missing 211 | change_mirrors 212 | pct exec $CTID -- apt-get install dos2unix -y 213 | fi 214 | fi 215 | setup_ssh "$system" 216 | fi 217 | } 218 | 219 | configure_container_extras() { 220 | if [ "$independent_ipv6_status" == "Y" ]; then 221 | pct exec $CTID -- echo '*/1 * * * * curl -m 6 -s ipv6.ip.sb && curl -m 6 -s ipv6.ip.sb' | crontab - 222 | fi 223 | pct exec $CTID -- rm -rf /etc/network/.pve-ignore.interfaces 224 | pct exec $CTID -- touch /etc/.pve-ignore.resolv.conf 225 | pct exec $CTID -- touch /etc/.pve-ignore.hosts 226 | pct exec $CTID -- touch /etc/.pve-ignore.hostname 227 | } 228 | 229 | setup_port_forwarding() { 230 | iptables -t nat -A PREROUTING -i vmbr0 -p tcp --dport ${sshn} -j DNAT --to-destination ${user_ip}:22 231 | if [ "${web1_port}" -ne 0 ]; then 232 | iptables -t nat -A PREROUTING -i vmbr0 -p tcp -m tcp --dport ${web1_port} -j DNAT --to-destination ${user_ip}:80 233 | fi 234 | if [ "${web2_port}" -ne 0 ]; then 235 | iptables -t nat -A PREROUTING -i vmbr0 -p tcp -m tcp --dport ${web2_port} -j DNAT --to-destination ${user_ip}:443 236 | fi 237 | if [ "${port_first}" -ne 0 ] && [ "${port_last}" -ne 0 ]; then 238 | iptables -t nat -A PREROUTING -i vmbr0 -p tcp -m tcp --dport ${port_first}:${port_last} -j DNAT --to-destination ${user_ip}:${port_first}-${port_last} 239 | iptables -t nat -A PREROUTING -i vmbr0 -p udp -m udp --dport ${port_first}:${port_last} -j DNAT --to-destination ${user_ip}:${port_first}-${port_last} 240 | fi 241 | if [ ! -f "/etc/iptables/rules.v4" ]; then 242 | touch /etc/iptables/rules.v4 243 | fi 244 | iptables-save | awk '{if($1=="COMMIT"){delete x}}$1=="-A"?!x[$0]++:1' | iptables-restore 245 | iptables-save >/etc/iptables/rules.v4 246 | service netfilter-persistent restart 247 | } 248 | 249 | save_container_info() { 250 | if [ "$independent_ipv6_status" == "Y" ]; then 251 | echo "$CTID $password $core $memory $disk $sshn $web1_port $web2_port $port_first $port_last $system_ori $storage ${ipv6_address_without_last_segment}${CTID}" >>"ct${CTID}" 252 | data=$(echo " CTID root密码-password CPU核数-CPU 内存-memory 硬盘-disk SSH端口 80端口 443端口 外网端口起-port-start 外网端口止-port-end 系统-system 存储盘-storage 独立IPV6地址-ipv6_address") 253 | else 254 | echo "$CTID $password $core $memory $disk $sshn $web1_port $web2_port $port_first $port_last $system_ori $storage" >>"ct${CTID}" 255 | data=$(echo " CTID root密码-password CPU核数-CPU 内存-memory 硬盘-disk SSH端口 80端口 443端口 外网端口起-port-start 外网端口止-port-end 系统-system 存储盘-storage") 256 | fi 257 | values=$(cat "ct${CTID}") 258 | IFS=' ' read -ra data_array <<<"$data" 259 | IFS=' ' read -ra values_array <<<"$values" 260 | length=${#data_array[@]} 261 | for ((i = 0; i < $length; i++)); do 262 | echo "${data_array[$i]} ${values_array[$i]}" 263 | echo "" 264 | done >"/tmp/temp${CTID}.txt" 265 | sed -i 's/^/# /' "/tmp/temp${CTID}.txt" 266 | cat "/etc/pve/lxc/${CTID}.conf" >>"/tmp/temp${CTID}.txt" 267 | cp "/tmp/temp${CTID}.txt" "/etc/pve/lxc/${CTID}.conf" 268 | rm -rf "/tmp/temp${CTID}.txt" 269 | cat "ct${CTID}" 270 | } 271 | 272 | main() { 273 | cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn1.spiritlhl.net/" "http://cdn2.spiritlhl.net/" "http://cdn3.spiritlhl.net/" "http://cdn4.spiritlhl.net/") 274 | check_cdn_file 275 | load_default_config 276 | set_locale 277 | get_system_arch || exit 1 278 | check_china 279 | init "$@" 280 | validate_ctid || exit 1 281 | check_ipv6_setup 282 | prepare_system_image || exit 1 283 | create_container 284 | configure_networking 285 | configure_os 286 | configure_container_extras 287 | setup_port_forwarding 288 | save_container_info 289 | } 290 | 291 | main "$@" 292 | -------------------------------------------------------------------------------- /scripts/buildct_onlyv6.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2025.05.17 5 | # ./buildct_onlyv6.sh CTID 密码 CPU核数 内存 硬盘 系统 存储盘 6 | # ./buildct_onlyv6.sh 102 1234567 1 512 5 debian11 local 7 | 8 | cd /root >/dev/null 2>&1 9 | 10 | init() { 11 | CTID="${1:-102}" 12 | password="${2:-123456}" 13 | core="${3:-1}" 14 | memory="${4:-512}" 15 | disk="${5:-5}" 16 | system_ori="${6:-debian11}" 17 | storage="${7:-local}" 18 | rm -rf "ct$CTID" 19 | en_system=$(echo "$system_ori" | sed 's/[0-9]*//g; s/\.$//') 20 | num_system=$(echo "$system_ori" | sed 's/[a-zA-Z]*//g') 21 | system="$en_system-$num_system" 22 | } 23 | 24 | check_requirements() { 25 | if [ ! -f /usr/local/bin/pve_check_ipv6 ]; then 26 | _yellow "No ipv6 address exists to open a server with a standalone IPV6 address" 27 | fi 28 | if ! grep -q "vmbr2" /etc/network/interfaces; then 29 | _yellow "No vmbr2 exists to open a server with a standalone IPV6 address" 30 | fi 31 | 32 | service_status=$(systemctl is-active ndpresponder.service) 33 | if [ "$service_status" == "active" ]; then 34 | _green "The ndpresponder service started successfully and is running, and the host can open a service with a separate IPV6 address." 35 | _green "ndpresponder服务启动成功且正在运行,宿主机可开设带独立IPV6地址的服务。" 36 | else 37 | _green "The status of the ndpresponder service is abnormal and the host may not open a service with a separate IPV6 address." 38 | _green "ndpresponder服务状态异常,宿主机不可开设带独立IPV6地址的服务。" 39 | exit 1 40 | fi 41 | } 42 | 43 | check_cdn() { 44 | local o_url=$1 45 | local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) 46 | for cdn_url in "${shuffled_cdn_urls[@]}"; do 47 | if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then 48 | export cdn_success_url="$cdn_url" 49 | return 50 | fi 51 | sleep 0.5 52 | done 53 | export cdn_success_url="" 54 | } 55 | 56 | check_cdn_file() { 57 | check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" 58 | if [ -n "$cdn_success_url" ]; then 59 | echo "CDN available, using CDN" 60 | else 61 | echo "No CDN available, no use CDN" 62 | fi 63 | } 64 | 65 | download_with_retry() { 66 | local url="$1" 67 | local output="$2" 68 | local max_attempts=5 69 | local attempt=1 70 | local delay=1 71 | while [ $attempt -le $max_attempts ]; do 72 | wget -q "$url" -O "$output" && return 0 73 | echo "Download failed: $url, try $attempt, wait $delay seconds and retry..." 74 | echo "下载失败:$url,尝试第 $attempt 次,等待 $delay 秒后重试..." 75 | sleep $delay 76 | attempt=$((attempt + 1)) 77 | delay=$((delay * 2)) 78 | [ $delay -gt 30 ] && delay=30 79 | done 80 | echo -e "\e[31mDownload failed: $url, maximum number of attempts exceeded ($max_attempts)\e[0m" 81 | echo -e "\e[31m下载失败:$url,超过最大尝试次数 ($max_attempts)\e[0m" 82 | return 1 83 | } 84 | 85 | load_default_config() { 86 | local config_url="${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/default_ct_config.sh" 87 | local config_file="default_ct_config.sh" 88 | if download_with_retry "$config_url" "$config_file"; then 89 | . "./$config_file" 90 | else 91 | echo -e "\e[31mUnable to load default configuration, script terminated.\e[0m" 92 | echo -e "\e[31m无法加载默认配置,脚本终止。\e[0m" 93 | exit 1 94 | fi 95 | } 96 | 97 | get_ipv6_info() { 98 | if [ -f /usr/local/bin/pve_check_ipv6 ]; then 99 | host_ipv6_address=$(cat /usr/local/bin/pve_check_ipv6) 100 | ipv6_address_without_last_segment="${host_ipv6_address%:*}:" 101 | fi 102 | if [ -f /usr/local/bin/pve_ipv6_prefixlen ]; then 103 | ipv6_prefixlen=$(cat /usr/local/bin/pve_ipv6_prefixlen) 104 | fi 105 | if [ -f /usr/local/bin/pve_ipv6_gateway ]; then 106 | ipv6_gateway=$(cat /usr/local/bin/pve_ipv6_gateway) 107 | fi 108 | } 109 | 110 | setup_mirrors_for_cn() { 111 | pct exec $CTID -- curl -lk https://gitee.com/SuperManito/LinuxMirrors/raw/main/ChangeMirrors.sh -o ChangeMirrors.sh 112 | pct exec $CTID -- chmod 777 ChangeMirrors.sh 113 | pct exec $CTID -- ./ChangeMirrors.sh --source mirrors.tuna.tsinghua.edu.cn --web-protocol http --intranet false --close-firewall true --backup true --updata-software false --clean-cache false --ignore-backup-tips > /dev/null 114 | pct exec $CTID -- rm -rf ChangeMirrors.sh 115 | } 116 | 117 | setup_container_os() { 118 | if [ "$fixed_system" = true ]; then 119 | if [[ -z "${CN}" || "${CN}" != true ]]; then 120 | sleep 1 121 | else 122 | setup_mirrors_for_cn 123 | fi 124 | sleep 2 125 | public_network_check_res=$(pct exec $CTID -- curl -lk -m 6 ${cdn_success_url}https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test) 126 | if [[ $public_network_check_res == *"success"* ]]; then 127 | echo "network is public" 128 | else 129 | echo "nameserver 8.8.8.8" | pct exec $CTID -- tee -a /etc/resolv.conf 130 | sleep 1 131 | pct exec $CTID -- curl -lk -m 6 ${cdn_success_url}https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test 132 | fi 133 | sleep 2 134 | ssh_check_res=$(pct exec $CTID -- lsof -i:22) 135 | if [[ $ssh_check_res == *"ssh"* ]]; then 136 | echo "ssh config correct" 137 | else 138 | pct exec $CTID -- service ssh restart 139 | pct exec $CTID -- service sshd restart 140 | sleep 2 141 | pct exec $CTID -- systemctl restart sshd 142 | pct exec $CTID -- systemctl restart ssh 143 | fi 144 | else 145 | if echo "$system" | grep -qiE "centos|almalinux|rockylinux" >/dev/null 2>&1; then 146 | if [[ -z "${CN}" || "${CN}" != true ]]; then 147 | pct exec $CTID -- yum update -y 148 | pct exec $CTID -- yum install -y dos2unix curl 149 | else 150 | pct exec $CTID -- yum install -y curl 151 | setup_mirrors_for_cn 152 | pct exec $CTID -- yum install -y dos2unix 153 | fi 154 | elif echo "$system" | grep -qiE "fedora" >/dev/null 2>&1; then 155 | if [[ -z "${CN}" || "${CN}" != true ]]; then 156 | pct exec $CTID -- dnf update -y 157 | pct exec $CTID -- dnf install -y dos2unix curl 158 | else 159 | pct exec $CTID -- dnf install -y curl 160 | setup_mirrors_for_cn 161 | pct exec $CTID -- dnf install -y dos2unix 162 | fi 163 | elif echo "$system" | grep -qiE "opensuse" >/dev/null 2>&1; then 164 | if [[ -z "${CN}" || "${CN}" != true ]]; then 165 | pct exec $CTID -- zypper update -y 166 | pct exec $CTID -- zypper --non-interactive install dos2unix curl 167 | else 168 | pct exec $CTID -- zypper --non-interactive install curl 169 | setup_mirrors_for_cn 170 | pct exec $CTID -- zypper --non-interactive install dos2unix 171 | fi 172 | elif echo "$system" | grep -qiE "alpine|archlinux" >/dev/null 2>&1; then 173 | if [[ -z "${CN}" || "${CN}" != true ]]; then 174 | sleep 1 175 | else 176 | pct exec $CTID -- wget https://gitee.com/SuperManito/LinuxMirrors/raw/main/ChangeMirrors.sh 177 | pct exec $CTID -- chmod 777 ChangeMirrors.sh 178 | pct exec $CTID -- ./ChangeMirrors.sh --source mirrors.tuna.tsinghua.edu.cn --web-protocol http --intranet false --close-firewall true --backup true --updata-software false --clean-cache false --ignore-backup-tips > /dev/null 179 | pct exec $CTID -- rm -rf ChangeMirrors.sh 180 | fi 181 | elif echo "$system" | grep -qiE "ubuntu|debian|devuan" >/dev/null 2>&1; then 182 | if [[ -z "${CN}" || "${CN}" != true ]]; then 183 | pct exec $CTID -- apt-get update -y 184 | pct exec $CTID -- dpkg --configure -a 185 | pct exec $CTID -- apt-get update 186 | pct exec $CTID -- apt-get install dos2unix curl -y 187 | else 188 | pct exec $CTID -- apt-get install curl -y --fix-missing 189 | setup_mirrors_for_cn 190 | pct exec $CTID -- apt-get install dos2unix -y 191 | fi 192 | fi 193 | if echo "$system" | grep -qiE "alpine|archlinux|gentoo|openwrt" >/dev/null 2>&1; then 194 | pct exec $CTID -- curl -L ${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/ssh_sh.sh -o ssh_sh.sh 195 | pct exec $CTID -- chmod 777 ssh_sh.sh 196 | pct exec $CTID -- dos2unix ssh_sh.sh 197 | pct exec $CTID -- bash ssh_sh.sh 198 | else 199 | pct exec $CTID -- curl -L ${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/ssh_bash.sh -o ssh_bash.sh 200 | pct exec $CTID -- chmod 777 ssh_bash.sh 201 | pct exec $CTID -- dos2unix ssh_bash.sh 202 | pct exec $CTID -- bash ssh_bash.sh 203 | fi 204 | fi 205 | } 206 | 207 | create_container() { 208 | user_ip="172.16.1.${CTID}" 209 | if [ "$fixed_system" = true ]; then 210 | pct create $CTID /var/lib/vz/template/cache/${system_name} -cores $core -cpuunits 1024 -memory $memory -swap 128 -rootfs ${storage}:${disk} -onboot 1 -password $password -features nesting=1 211 | else 212 | pct create $CTID ${storage}:vztmpl/${system_name} -cores $core -cpuunits 1024 -memory $memory -swap 128 -rootfs ${storage}:${disk} -onboot 1 -password $password -features nesting=1 213 | fi 214 | pct start $CTID 215 | sleep 5 216 | pct set $CTID --hostname $CTID 217 | pct set $CTID --net0 name=eth0,ip6="${ipv6_address_without_last_segment}${CTID}/128",bridge=vmbr2,gw6="${host_ipv6_address}" 218 | pct set $CTID --net1 name=eth1,ip=${user_ip}/24,bridge=vmbr1,gw=172.16.1.1 219 | pct set $CTID --nameserver 8.8.8.8,2001:4860:4860::8888 --nameserver 8.8.4.4,2001:4860:4860::8844 220 | sleep 3 221 | } 222 | 223 | save_container_info() { 224 | echo "$CTID $password $core $memory $disk $system_ori $storage ${ipv6_address_without_last_segment}${CTID}" >>"ct${CTID}" 225 | data=$(echo " CTID root密码-password CPU核数-CPU 内存-memory 硬盘-disk 系统-system 存储盘-storage 外网IPV6-ipv6") 226 | values=$(cat "ct${CTID}") 227 | IFS=' ' read -ra data_array <<<"$data" 228 | IFS=' ' read -ra values_array <<<"$values" 229 | length=${#data_array[@]} 230 | for ((i = 0; i < $length; i++)); do 231 | echo "${data_array[$i]} ${values_array[$i]}" 232 | echo "" 233 | done >"/tmp/temp${CTID}.txt" 234 | sed -i 's/^/# /' "/tmp/temp${CTID}.txt" 235 | cat "/etc/pve/lxc/${CTID}.conf" >>"/tmp/temp${CTID}.txt" 236 | cp "/tmp/temp${CTID}.txt" "/etc/pve/lxc/${CTID}.conf" 237 | rm -rf "/tmp/temp${CTID}.txt" 238 | cat "ct${CTID}" 239 | } 240 | 241 | finalize_container() { 242 | pct exec $CTID -- echo '*/1 * * * * curl -m 6 -s ipv6.ip.sb && curl -m 6 -s ipv6.ip.sb' | crontab - 243 | pct exec $CTID -- rm -rf /etc/network/.pve-ignore.interfaces 244 | pct exec $CTID -- touch /etc/.pve-ignore.resolv.conf 245 | pct exec $CTID -- touch /etc/.pve-ignore.hosts 246 | pct exec $CTID -- touch /etc/.pve-ignore.hostname 247 | } 248 | 249 | main() { 250 | cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn1.spiritlhl.net/" "http://cdn2.spiritlhl.net/" "http://cdn3.spiritlhl.net/" "http://cdn4.spiritlhl.net/") 251 | check_cdn_file 252 | load_default_config 253 | set_locale 254 | check_requirements 255 | get_system_arch || exit 1 256 | check_china 257 | init "$@" 258 | validate_ctid || exit 1 259 | get_ipv6_info 260 | prepare_system_image || exit 1 261 | create_container 262 | setup_container_os 263 | finalize_container 264 | save_container_info 265 | } 266 | 267 | main "$@" 268 | -------------------------------------------------------------------------------- /scripts/buildvm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2025.05.17 5 | # ./buildvm.sh VMID 用户名 密码 CPU核数 内存 硬盘 SSH端口 80端口 443端口 外网端口起 外网端口止 系统 存储盘 独立IPV6 6 | # ./buildvm.sh 102 test1 1234567 1 512 5 40001 40002 40003 50000 50025 debian11 local N 7 | 8 | cd /root >/dev/null 2>&1 9 | 10 | init_params() { 11 | vm_num="${1:-102}" 12 | user="${2:-test}" 13 | password="${3:-123456}" 14 | core="${4:-1}" 15 | memory="${5:-512}" 16 | disk="${6:-5}" 17 | sshn="${7:-40001}" 18 | web1_port="${8:-40002}" 19 | web2_port="${9:-40003}" 20 | port_first="${10:-49975}" 21 | port_last="${11:-50000}" 22 | system="${12:-ubuntu22}" 23 | storage="${13:-local}" 24 | independent_ipv6="${14:-N}" 25 | independent_ipv6=$(echo "$independent_ipv6" | tr '[:upper:]' '[:lower:]') 26 | if [ ! -d "qcow" ]; then 27 | mkdir qcow 28 | fi 29 | rm -rf "vm$vm_num" 30 | } 31 | 32 | check_cdn() { 33 | local o_url=$1 34 | local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) # 打乱数组顺序 35 | for cdn_url in "${shuffled_cdn_urls[@]}"; do 36 | if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then 37 | export cdn_success_url="$cdn_url" 38 | return 39 | fi 40 | sleep 0.5 41 | done 42 | export cdn_success_url="" 43 | } 44 | 45 | check_cdn_file() { 46 | check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" 47 | if [ -n "$cdn_success_url" ]; then 48 | echo "CDN available, using CDN" 49 | else 50 | echo "No CDN available, using original links" 51 | export cdn_success_url="" 52 | fi 53 | } 54 | 55 | download_with_retry() { 56 | local url="$1" 57 | local output="$2" 58 | local max_attempts=5 59 | local attempt=1 60 | local delay=1 61 | while [ $attempt -le $max_attempts ]; do 62 | wget -q "$url" -O "$output" && return 0 63 | echo "Download failed: $url, try $attempt, wait $delay seconds and retry..." 64 | echo "下载失败:$url,尝试第 $attempt 次,等待 $delay 秒后重试..." 65 | sleep $delay 66 | attempt=$((attempt + 1)) 67 | delay=$((delay * 2)) 68 | [ $delay -gt 30 ] && delay=30 69 | done 70 | echo -e "\e[31mDownload failed: $url, maximum number of attempts exceeded ($max_attempts)\e[0m" 71 | echo -e "\e[31m下载失败:$url,超过最大尝试次数 ($max_attempts)\e[0m" 72 | return 1 73 | } 74 | 75 | load_default_config() { 76 | local config_url="${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/default_vm_config.sh" 77 | local config_file="default_vm_config.sh" 78 | if download_with_retry "$config_url" "$config_file"; then 79 | . "./$config_file" 80 | else 81 | echo -e "\e[31mUnable to load default configuration, script terminated.\e[0m" 82 | echo -e "\e[31m无法加载默认配置,脚本终止。\e[0m" 83 | exit 1 84 | fi 85 | } 86 | 87 | create_vm() { 88 | if [ "$independent_ipv6" == "n" ]; then 89 | qm create $vm_num --agent 1 --scsihw virtio-scsi-single --serial0 socket \ 90 | --cores $core --sockets 1 --cpu $cpu_type \ 91 | --net0 virtio,bridge=vmbr1,firewall=0 \ 92 | --ostype l26 \ 93 | ${kvm_flag} 94 | else 95 | qm create $vm_num --agent 1 --scsihw virtio-scsi-single --serial0 socket \ 96 | --cores $core --sockets 1 --cpu $cpu_type \ 97 | --net0 virtio,bridge=vmbr1,firewall=0 \ 98 | --net1 virtio,bridge=vmbr2,firewall=0 \ 99 | --ostype l26 \ 100 | ${kvm_flag} 101 | fi 102 | if [ "$system_arch" = "x86" ] || [ "$system_arch" = "x86_64" ]; then 103 | qm importdisk $vm_num /root/qcow/${system}.qcow2 ${storage} 104 | else 105 | qm set $vm_num --bios ovmf 106 | qm importdisk $vm_num /root/qcow/${system}.${ext} ${storage} 107 | fi 108 | sleep 3 109 | volid=$(pvesm list ${storage} | awk -v vmid="${vm_num}" '$5 == vmid && $1 ~ /\.raw$/ {print $1}' | tail -n 1) 110 | if [ -z "$volid" ]; then 111 | echo "No .raw file found for VM ID '${vm_num}' in storage '${storage}'. Searching for other formats..." 112 | volid=$(pvesm list ${storage} | awk -v vmid="${vm_num}" '$5 == vmid {print $1}' | tail -n 1) 113 | fi 114 | if [ -z "$volid" ]; then 115 | echo "Error: No file found for VM ID '${vm_num}' in storage '${storage}'" 116 | return 1 117 | fi 118 | file_path=$(pvesm path ${volid}) 119 | if [ $? -ne 0 ] || [ -z "$file_path" ]; then 120 | echo "Error: Failed to resolve path for volume '${volid}'" 121 | return 1 122 | fi 123 | file_name=$(basename "$file_path") 124 | echo "Found file: $file_name" 125 | echo "Attempting to set SCSI hardware with virtio-scsi-pci for VM $vm_num..." 126 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:${vm_num}/vm-${vm_num}-disk-0.raw 127 | if [ $? -ne 0 ]; then 128 | echo "Failed to set SCSI hardware with vm-${vm_num}-disk-0.raw. Trying alternative disk file..." 129 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:${vm_num}/$file_name 130 | if [ $? -ne 0 ]; then 131 | echo "Failed to set SCSI hardware with $file_name for VM $vm_num. Trying fallback file..." 132 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:$file_name 133 | if [ $? -ne 0 ]; then 134 | echo "All attempts failed. Exiting..." 135 | return 1 136 | fi 137 | fi 138 | fi 139 | qm set $vm_num --bootdisk scsi0 140 | qm set $vm_num --boot order=scsi0 141 | qm set $vm_num --memory $memory 142 | # --swap 256 143 | if [[ "$system_arch" == "arm" ]]; then 144 | qm set $vm_num --scsi1 ${storage}:cloudinit 145 | else 146 | qm set $vm_num --ide1 ${storage}:cloudinit 147 | fi 148 | configure_network 149 | qm resize $vm_num scsi0 ${disk}G 150 | if [ $? -ne 0 ]; then 151 | if [[ $disk =~ ^[0-9]+G$ ]]; then 152 | dnum=${disk::-1} 153 | disk_m=$((dnum * 1024)) 154 | qm resize $vm_num scsi0 ${disk_m}M 155 | fi 156 | fi 157 | qm start $vm_num 158 | return 0 159 | } 160 | 161 | configure_network() { 162 | user_ip="172.16.1.${vm_num}" 163 | if [ "$independent_ipv6" == "y" ]; then 164 | if [ ! -z "$host_ipv6_address" ] && [ ! -z "$ipv6_prefixlen" ] && [ ! -z "$ipv6_gateway" ] && [ ! -z "$ipv6_address_without_last_segment" ]; then 165 | if grep -q "vmbr2" /etc/network/interfaces; then 166 | qm set $vm_num --ipconfig0 ip=${user_ip}/24,gw=172.16.1.1 167 | qm set $vm_num --ipconfig1 ip6="${ipv6_address_without_last_segment}${vm_num}/128",gw6="${host_ipv6_address}" 168 | qm set $vm_num --nameserver 1.1.1.1 169 | # qm set $vm_num --nameserver 1.0.0.1 170 | qm set $vm_num --searchdomain local 171 | independent_ipv6_status="Y" 172 | else 173 | independent_ipv6_status="N" 174 | fi 175 | else 176 | independent_ipv6_status="N" 177 | fi 178 | else 179 | independent_ipv6_status="N" 180 | fi 181 | if [ "$independent_ipv6_status" == "N" ]; then 182 | qm set $vm_num --ipconfig0 ip=${user_ip}/24,gw=172.16.1.1 183 | qm set $vm_num --nameserver 8.8.8.8 184 | # qm set $vm_num --nameserver 8.8.4.4 185 | qm set $vm_num --searchdomain local 186 | fi 187 | qm set $vm_num --cipassword $password --ciuser $user 188 | sleep 5 189 | } 190 | 191 | setup_port_forwarding() { 192 | user_ip="172.16.1.${vm_num}" 193 | iptables -t nat -A PREROUTING -i vmbr0 -p tcp --dport ${sshn} -j DNAT --to-destination ${user_ip}:22 194 | if [ "${web1_port}" -ne 0 ]; then 195 | iptables -t nat -A PREROUTING -i vmbr0 -p tcp -m tcp --dport ${web1_port} -j DNAT --to-destination ${user_ip}:80 196 | fi 197 | if [ "${web2_port}" -ne 0 ]; then 198 | iptables -t nat -A PREROUTING -i vmbr0 -p tcp -m tcp --dport ${web2_port} -j DNAT --to-destination ${user_ip}:443 199 | fi 200 | if [ "${port_first}" -ne 0 ] && [ "${port_last}" -ne 0 ]; then 201 | iptables -t nat -A PREROUTING -i vmbr0 -p tcp -m tcp --dport ${port_first}:${port_last} -j DNAT --to-destination ${user_ip}:${port_first}-${port_last} 202 | iptables -t nat -A PREROUTING -i vmbr0 -p udp -m udp --dport ${port_first}:${port_last} -j DNAT --to-destination ${user_ip}:${port_first}-${port_last} 203 | fi 204 | if [ ! -f "/etc/iptables/rules.v4" ]; then 205 | touch /etc/iptables/rules.v4 206 | fi 207 | iptables-save | awk '{if($1=="COMMIT"){delete x}}$1=="-A"?!x[$0]++:1' | iptables-restore 208 | iptables-save >/etc/iptables/rules.v4 209 | service netfilter-persistent restart 210 | } 211 | 212 | save_vm_info() { 213 | if [ "$independent_ipv6_status" == "Y" ]; then 214 | echo "$vm_num $user $password $core $memory $disk $sshn $web1_port $web2_port $port_first $port_last $system $storage ${ipv6_address_without_last_segment}${vm_num}" >>"vm${vm_num}" 215 | data=$(echo " VMID 用户名-username 密码-password CPU核数-CPU 内存-memory 硬盘-disk SSH端口 80端口 443端口 外网端口起-port-start 外网端口止-port-end 系统-system 存储盘-storage 独立IPV6地址-ipv6_address") 216 | else 217 | echo "$vm_num $user $password $core $memory $disk $sshn $web1_port $web2_port $port_first $port_last $system $storage" >>"vm${vm_num}" 218 | data=$(echo " VMID 用户名-username 密码-password CPU核数-CPU 内存-memory 硬盘-disk SSH端口 80端口 443端口 外网端口起-port-start 外网端口止-port-end 系统-system 存储盘-storage") 219 | fi 220 | values=$(cat "vm${vm_num}") 221 | IFS=' ' read -ra data_array <<<"$data" 222 | IFS=' ' read -ra values_array <<<"$values" 223 | length=${#data_array[@]} 224 | for ((i = 0; i < $length; i++)); do 225 | echo "${data_array[$i]} ${values_array[$i]}" 226 | echo "" 227 | done >"/tmp/temp${vm_num}.txt" 228 | sed -i 's/^/# /' "/tmp/temp${vm_num}.txt" 229 | cat "/etc/pve/qemu-server/${vm_num}.conf" >>"/tmp/temp${vm_num}.txt" 230 | cp "/tmp/temp${vm_num}.txt" "/etc/pve/qemu-server/${vm_num}.conf" 231 | rm -rf "/tmp/temp${vm_num}.txt" 232 | cat "vm${vm_num}" 233 | } 234 | 235 | main() { 236 | cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn1.spiritlhl.net/" "http://cdn2.spiritlhl.net/" "http://cdn3.spiritlhl.net/" "http://cdn4.spiritlhl.net/") 237 | check_cdn_file 238 | load_default_config || exit 1 239 | setup_locale 240 | init_params "$@" 241 | validate_vm_num || exit 1 242 | get_system_arch || exit 1 243 | check_kvm_support 244 | prepare_system_image || exit 1 245 | check_ipv6_config || exit 1 246 | create_vm || exit 1 247 | setup_port_forwarding 248 | save_vm_info 249 | } 250 | 251 | main "$@" 252 | rm -rf default_vm_config.sh 253 | -------------------------------------------------------------------------------- /scripts/buildvm_extra_ip.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2025.05.17 5 | # 自动选择要绑定的IPV4地址 额外的IPV4地址需要与本机的IPV4地址在同一个子网内,即前缀一致 6 | # 此时开设出的虚拟机的网关为宿主机的IPV4的网关,不需要强制约定MAC地址。 7 | # 此时附加的IPV4地址是宿主机目前的IPV4地址顺位后面的地址 8 | # 比如目前是 1.1.1.32 然后 1.1.1.33 已经有虚拟机了,那么本脚本附加IP地址为 1.1.1.34 9 | 10 | # ./buildvm_extra_ip.sh VMID 用户名 密码 CPU核数 内存 硬盘 系统 存储盘 是否附加IPV6(默认为N) 11 | # ./buildvm_extra_ip.sh 152 test1 1234567 1 512 5 debian11 local N 12 | 13 | cd /root >/dev/null 2>&1 14 | 15 | init_params() { 16 | vm_num="${1:-152}" 17 | user="${2:-test}" 18 | password="${3:-123456}" 19 | core="${4:-1}" 20 | memory="${5:-512}" 21 | disk="${6:-5}" 22 | system="${7:-ubuntu22}" 23 | storage="${8:-local}" 24 | independent_ipv6="${9:-N}" 25 | independent_ipv6=$(echo "$independent_ipv6" | tr '[:upper:]' '[:lower:]') 26 | rm -rf "vm$vm_num" 27 | user_ip="" 28 | user_ip_range="" 29 | gateway="" 30 | if [ ! -d "qcow" ]; then 31 | mkdir qcow 32 | fi 33 | rm -rf "vm$vm_num" 34 | } 35 | 36 | check_cdn() { 37 | local o_url=$1 38 | local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) # 打乱数组顺序 39 | for cdn_url in "${shuffled_cdn_urls[@]}"; do 40 | if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then 41 | export cdn_success_url="$cdn_url" 42 | return 43 | fi 44 | sleep 0.5 45 | done 46 | export cdn_success_url="" 47 | } 48 | 49 | check_cdn_file() { 50 | check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" 51 | if [ -n "$cdn_success_url" ]; then 52 | echo "CDN available, using CDN" 53 | else 54 | echo "No CDN available, no use CDN" 55 | fi 56 | } 57 | 58 | download_with_retry() { 59 | local url="$1" 60 | local output="$2" 61 | local max_attempts=5 62 | local attempt=1 63 | local delay=1 64 | while [ $attempt -le $max_attempts ]; do 65 | wget -q "$url" -O "$output" && return 0 66 | echo "Download failed: $url, try $attempt, wait $delay seconds and retry..." 67 | echo "下载失败:$url,尝试第 $attempt 次,等待 $delay 秒后重试..." 68 | sleep $delay 69 | attempt=$((attempt + 1)) 70 | delay=$((delay * 2)) 71 | [ $delay -gt 30 ] && delay=30 72 | done 73 | echo -e "\e[31mDownload failed: $url, maximum number of attempts exceeded ($max_attempts)\e[0m" 74 | echo -e "\e[31m下载失败:$url,超过最大尝试次数 ($max_attempts)\e[0m" 75 | return 1 76 | } 77 | 78 | load_default_config() { 79 | local config_url="${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/default_vm_config.sh" 80 | local config_file="default_vm_config.sh" 81 | if download_with_retry "$config_url" "$config_file"; then 82 | . "./$config_file" 83 | else 84 | echo -e "\e[31mUnable to load default configuration, script terminated.\e[0m" 85 | echo -e "\e[31m无法加载默认配置,脚本终止。\e[0m" 86 | exit 1 87 | fi 88 | } 89 | 90 | get_host_network_info() { 91 | if ! command -v lshw >/dev/null 2>&1; then 92 | apt-get install -y lshw 93 | fi 94 | if ! command -v ping >/dev/null 2>&1; then 95 | apt-get install -y iputils-ping 96 | apt-get install -y ping 97 | fi 98 | interface=$(lshw -C network | awk '/logical name:/{print $3}' | head -1) 99 | user_main_ip_range=$(grep -A 1 "iface ${interface}" /etc/network/interfaces | grep "address" | awk '{print $2}' | head -n 1) 100 | if [ -z "$user_main_ip_range" ]; then 101 | user_main_ip_range=$(grep -A 1 "iface vmbr0" /etc/network/interfaces | grep "address" | awk '{print $2}' | head -n 1) 102 | if [ -z "$user_main_ip_range" ]; then 103 | _red "宿主机可用IP区间查询失败" 104 | exit 1 105 | fi 106 | fi 107 | user_main_ip=$(echo "$user_main_ip_range" | cut -d'/' -f1) 108 | user_ip_range=$(echo "$user_main_ip_range" | cut -d'/' -f2) 109 | ip_range=$((32 - user_ip_range)) 110 | range=$((2 ** ip_range - 3)) 111 | IFS='.' read -r -a octets <<<"$user_main_ip" 112 | ip_list=() 113 | for ((i = 0; i < $range; i++)); do 114 | octet=$((i % 256)) 115 | if [ $octet -gt 254 ]; then 116 | break 117 | fi 118 | ip="${octets[0]}.${octets[1]}.${octets[2]}.$((octets[3] + octet))" 119 | ip_list+=("$ip") 120 | done 121 | _green "当前宿主机可用的外网IP列表长度为${range}" 122 | for ip in "${ip_list[@]}"; do 123 | if ! ping -c 1 "$ip" >/dev/null; then 124 | user_ip="$ip" 125 | break 126 | fi 127 | done 128 | gateway=$(grep -E "iface $interface" -A 3 "/etc/network/interfaces" | grep "gateway" | awk '{print $2}' | head -n 1) 129 | if [ -z "$gateway" ]; then 130 | gateway=$(grep -E "iface vmbr0" -A 3 "/etc/network/interfaces" | grep "gateway" | awk '{print $2}' | head -n 1) 131 | if [ -z "$gateway" ]; then 132 | _red "宿主机网关查询失败" 133 | exit 1 134 | fi 135 | fi 136 | if [ -z "$user_ip" ]; then 137 | _red "可使用的IP列表查询失败" 138 | exit 1 139 | fi 140 | if [ -z "$user_ip_range" ]; then 141 | _red "本虚拟机将要绑定的IP选择失败" 142 | exit 1 143 | fi 144 | _green "当前虚拟机将绑定的IP为:${user_ip}" 145 | user_ip_prefix=$(echo "$user_ip" | awk -F '.' '{print $1"."$2"."$3}') 146 | user_main_ip_prefix=$(echo "$user_main_ip" | awk -F '.' '{print $1"."$2"."$3}') 147 | if [ "$user_ip_prefix" = "$user_main_ip_prefix" ]; then 148 | _yellow "宿主机的IPV4前缀与将要开设的虚拟机的IPV4前缀相同。" 149 | else 150 | _blue "宿主机的IPV4前缀与将要开设的虚拟机的IPV4前缀不同,请使用 需要手动指定IPV4地址的版本 的脚本" 151 | exit 1 152 | fi 153 | } 154 | 155 | create_vm() { 156 | if [ "$independent_ipv6" == "n" ]; then 157 | qm create $vm_num \ 158 | --agent 1 \ 159 | --scsihw virtio-scsi-single \ 160 | --serial0 socket \ 161 | --cores $core \ 162 | --sockets 1 \ 163 | --cpu $cpu_type \ 164 | --net0 virtio,bridge=vmbr0,firewall=0 \ 165 | --ostype l26 \ 166 | ${kvm_flag} 167 | else 168 | qm create $vm_num \ 169 | --agent 1 \ 170 | --scsihw virtio-scsi-single \ 171 | --serial0 socket \ 172 | --cores $core \ 173 | --sockets 1 \ 174 | --cpu $cpu_type \ 175 | --net0 virtio,bridge=vmbr0,firewall=0 \ 176 | --net1 virtio,bridge=vmbr2,firewall=0 \ 177 | --ostype l26 \ 178 | ${kvm_flag} 179 | fi 180 | if [ "$system_arch" = "x86" ] || [ "$system_arch" = "x86_64" ]; then 181 | qm importdisk $vm_num /root/qcow/${system}.qcow2 ${storage} 182 | else 183 | qm set $vm_num --bios ovmf 184 | qm importdisk $vm_num /root/qcow/${system}.${ext} ${storage} 185 | fi 186 | sleep 3 187 | volid=$(pvesm list ${storage} | awk -v vmid="${vm_num}" '$5 == vmid && $1 ~ /\.raw$/ {print $1}' | tail -n 1) 188 | if [ -z "$volid" ]; then 189 | echo "No .raw file found for VM ID '${vm_num}' in storage '${storage}'. Searching for other formats..." 190 | volid=$(pvesm list ${storage} | awk -v vmid="${vm_num}" '$5 == vmid {print $1}' | tail -n 1) 191 | fi 192 | if [ -z "$volid" ]; then 193 | echo "Error: No file found for VM ID '${vm_num}' in storage '${storage}'" 194 | exit 1 195 | fi 196 | file_path=$(pvesm path ${volid}) 197 | if [ $? -ne 0 ] || [ -z "$file_path" ]; then 198 | echo "Error: Failed to resolve path for volume '${volid}'" 199 | exit 1 200 | fi 201 | file_name=$(basename "$file_path") 202 | echo "Found file: $file_name" 203 | echo "Attempting to set SCSI hardware with virtio-scsi-pci for VM $vm_num..." 204 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:${vm_num}/vm-${vm_num}-disk-0.raw 205 | if [ $? -ne 0 ]; then 206 | echo "Failed to set SCSI hardware with vm-${vm_num}-disk-0.raw. Trying alternative disk file..." 207 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:${vm_num}/$file_name 208 | if [ $? -ne 0 ]; then 209 | echo "Failed to set SCSI hardware with $file_name for VM $vm_num. Trying fallback file..." 210 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:$file_name 211 | if [ $? -ne 0 ]; then 212 | echo "All attempts failed. Exiting..." 213 | exit 1 214 | fi 215 | fi 216 | fi 217 | } 218 | 219 | configure_vm() { 220 | qm set $vm_num --bootdisk scsi0 221 | qm set $vm_num --boot order=scsi0 222 | qm set $vm_num --memory $memory 223 | if [[ "$system_arch" == "arm" ]]; then 224 | qm set $vm_num --scsi1 ${storage}:cloudinit 225 | else 226 | qm set $vm_num --ide1 ${storage}:cloudinit 227 | fi 228 | if [ "$independent_ipv6" == "y" ]; then 229 | if [ ! -z "$host_ipv6_address" ] && [ ! -z "$ipv6_prefixlen" ] && [ ! -z "$ipv6_gateway" ] && [ ! -z "$ipv6_address_without_last_segment" ]; then 230 | if grep -q "vmbr2" /etc/network/interfaces; then 231 | qm set $vm_num --ipconfig0 ip=${user_ip}/${user_ip_range},gw=${gateway} 232 | qm set $vm_num --ipconfig1 ip6="${ipv6_address_without_last_segment}${vm_num}/128",gw6="${host_ipv6_address}" 233 | qm set $vm_num --nameserver 1.1.1.1 234 | qm set $vm_num --searchdomain local 235 | independent_ipv6_status="Y" 236 | else 237 | independent_ipv6_status="N" 238 | fi 239 | else 240 | independent_ipv6_status="N" 241 | fi 242 | else 243 | independent_ipv6_status="N" 244 | fi 245 | if [ "$independent_ipv6_status" == "N" ]; then 246 | qm set $vm_num --ipconfig0 ip=${user_ip}/${user_ip_range},gw=${gateway} 247 | qm set $vm_num --nameserver 8.8.8.8 248 | qm set $vm_num --searchdomain local 249 | fi 250 | qm set $vm_num --cipassword $password --ciuser $user 251 | sleep 5 252 | qm resize $vm_num scsi0 ${disk}G 253 | if [ $? -ne 0 ]; then 254 | if [[ $disk =~ ^[0-9]+G$ ]]; then 255 | dnum=${disk::-1} 256 | disk_m=$((dnum * 1024)) 257 | qm resize $vm_num scsi0 ${disk_m}M 258 | fi 259 | fi 260 | qm start $vm_num 261 | } 262 | 263 | save_vm_info() { 264 | if [ "$independent_ipv6_status" == "Y" ]; then 265 | echo "$vm_num $user $password $core $memory $disk $system $storage $user_ip" >>"vm${vm_num}" 266 | data=$(echo " VMID 用户名-username 密码-password CPU核数-CPU 内存-memory 硬盘-disk 系统-system 存储盘-storage 外网IP地址-ipv4") 267 | else 268 | echo "$vm_num $user $password $core $memory $disk $system $storage $user_ip ${ipv6_address_without_last_segment}${vm_num}" >>"vm${vm_num}" 269 | data=$(echo " VMID 用户名-username 密码-password CPU核数-CPU 内存-memory 硬盘-disk 系统-system 存储盘-storage 外网IPV4-ipv4 外网IPV6-ipv6") 270 | fi 271 | values=$(cat "vm${vm_num}") 272 | IFS=' ' read -ra data_array <<<"$data" 273 | IFS=' ' read -ra values_array <<<"$values" 274 | length=${#data_array[@]} 275 | for ((i = 0; i < $length; i++)); do 276 | echo "${data_array[$i]} ${values_array[$i]}" 277 | echo "" 278 | done >"/tmp/temp${vm_num}.txt" 279 | sed -i 's/^/# /' "/tmp/temp${vm_num}.txt" 280 | cat "/etc/pve/qemu-server/${vm_num}.conf" >>"/tmp/temp${vm_num}.txt" 281 | cp "/tmp/temp${vm_num}.txt" "/etc/pve/qemu-server/${vm_num}.conf" 282 | rm -rf "/tmp/temp${vm_num}.txt" 283 | cat "vm${vm_num}" 284 | } 285 | 286 | main() { 287 | cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn1.spiritlhl.net/" "http://cdn2.spiritlhl.net/" "http://cdn3.spiritlhl.net/" "http://cdn4.spiritlhl.net/") 288 | check_cdn_file 289 | load_default_config || exit 1 290 | setup_locale 291 | get_system_arch || exit 1 292 | check_kvm_support 293 | init_params "$@" 294 | validate_vm_num || exit 1 295 | get_host_network_info 296 | create_vm 297 | configure_vm 298 | save_vm_info 299 | } 300 | 301 | main "$@" 302 | rm -rf default_vm_config.sh 303 | -------------------------------------------------------------------------------- /scripts/buildvm_fullnat_ip.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2025.05.17 5 | # 创建NAT全端口映射的虚拟机 6 | # 前置条件: 7 | # 要用到的外网IPV4地址已绑定到vmbr0网卡上(手动附加时务必在PVE安装完毕且自动配置网关后再附加),且宿主机的IPV4地址仍为顺序第一 8 | # 即 使用 curl ip.sb 仍显示宿主机原有IPV4地址,但可通过额外的IPV4地址登录进入宿主机 9 | 10 | # ./buildvm_fullnat_ip.sh VMID 用户名 密码 CPU核数 内存 硬盘 系统 存储盘 外网IPV4地址 是否附加IPV6(默认为N) 11 | # 示例: 12 | # ./buildvm_fullnat_ip.sh 152 test1 oneclick123 1 1024 10 debian11 local a.b.c.d N 13 | 14 | cd /root >/dev/null 2>&1 15 | 16 | init_params() { 17 | vm_num="${1:-152}" 18 | user="${2:-test}" 19 | password="${3:-123456}" 20 | core="${4:-1}" 21 | memory="${5:-512}" 22 | disk="${6:-5}" 23 | system="${7:-ubuntu22}" 24 | storage="${8:-local}" 25 | extranet_ipv4="${9}" 26 | independent_ipv6="${10:-N}" 27 | independent_ipv6=$(echo "$independent_ipv6" | tr '[:upper:]' '[:lower:]') 28 | rm -rf "vm$vm_num" 29 | if [[ -z "$extranet_ipv4" ]]; then 30 | _yellow "No IPV4 address is manually assigned" 31 | _yellow "IPV4地址未手动指定" 32 | exit 1 33 | else 34 | if is_ipv4 "$extranet_ipv4"; then 35 | _green "This IPV4 address will be used: ${extranet_ipv4}" 36 | _green "将使用此IPV4地址: ${extranet_ipv4}" 37 | else 38 | _yellow "IPV4 addresses do not conform to the rules" 39 | _yellow "IPV4地址不符合规则" 40 | exit 1 41 | fi 42 | fi 43 | if [ ! -d "qcow" ]; then 44 | mkdir qcow 45 | fi 46 | } 47 | 48 | check_cdn() { 49 | local o_url=$1 50 | local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) # 打乱数组顺序 51 | for cdn_url in "${shuffled_cdn_urls[@]}"; do 52 | if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then 53 | export cdn_success_url="$cdn_url" 54 | return 55 | fi 56 | sleep 0.5 57 | done 58 | export cdn_success_url="" 59 | } 60 | 61 | check_cdn_file() { 62 | check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" 63 | if [ -n "$cdn_success_url" ]; then 64 | echo "CDN available, using CDN" 65 | else 66 | echo "No CDN available, no use CDN" 67 | fi 68 | } 69 | 70 | download_with_retry() { 71 | local url="$1" 72 | local output="$2" 73 | local max_attempts=5 74 | local attempt=1 75 | local delay=1 76 | while [ $attempt -le $max_attempts ]; do 77 | wget -q "$url" -O "$output" && return 0 78 | echo "Download failed: $url, try $attempt, wait $delay seconds and retry..." 79 | echo "下载失败:$url,尝试第 $attempt 次,等待 $delay 秒后重试..." 80 | sleep $delay 81 | attempt=$((attempt + 1)) 82 | delay=$((delay * 2)) 83 | [ $delay -gt 30 ] && delay=30 84 | done 85 | echo -e "\e[31mDownload failed: $url, maximum number of attempts exceeded ($max_attempts)\e[0m" 86 | echo -e "\e[31m下载失败:$url,超过最大尝试次数 ($max_attempts)\e[0m" 87 | return 1 88 | } 89 | 90 | load_default_config() { 91 | local config_url="${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/default_vm_config.sh" 92 | local config_file="default_vm_config.sh" 93 | if download_with_retry "$config_url" "$config_file"; then 94 | . "./$config_file" 95 | else 96 | echo -e "\e[31mUnable to load default configuration, script terminated.\e[0m" 97 | echo -e "\e[31m无法加载默认配置,脚本终止。\e[0m" 98 | exit 1 99 | fi 100 | } 101 | 102 | check_network() { 103 | # 查询信息 104 | if ! command -v lshw >/dev/null 2>&1; then 105 | apt-get install -y lshw 106 | fi 107 | if ! command -v ping >/dev/null 2>&1; then 108 | apt-get install -y iputils-ping 109 | apt-get install -y ping 110 | fi 111 | interface=$(lshw -C network | awk '/logical name:/{print $3}' | head -1) 112 | _green "The current IP to which the VM will be bound is: ${extranet_ipv4}" 113 | _green "当前虚拟机将绑定的IP为:${extranet_ipv4}" 114 | } 115 | 116 | create_vm() { 117 | if [ "$independent_ipv6" = "n" ]; then 118 | qm create "$vm_num" \ 119 | --agent 1 \ 120 | --scsihw virtio-scsi-single \ 121 | --serial0 socket \ 122 | --cores "$core" \ 123 | --sockets 1 \ 124 | --cpu "$cpu_type" \ 125 | --net0 virtio,bridge=vmbr1,firewall=0 \ 126 | --ostype l26 \ 127 | ${kvm_flag} 128 | elif [ "$independent_ipv6" = "y" ]; then 129 | qm create "$vm_num" \ 130 | --agent 1 \ 131 | --scsihw virtio-scsi-single \ 132 | --serial0 socket \ 133 | --cores "$core" \ 134 | --sockets 1 \ 135 | --cpu "$cpu_type" \ 136 | --net0 virtio,bridge=vmbr1,firewall=0 \ 137 | --net1 virtio,bridge=vmbr2,firewall=0 \ 138 | --ostype l26 \ 139 | ${kvm_flag} 140 | fi 141 | if [ "$system_arch" = "x86" ] || [ "$system_arch" = "x86_64" ]; then 142 | qm importdisk $vm_num /root/qcow/${system}.qcow2 ${storage} 143 | else 144 | qm set $vm_num --bios ovmf 145 | qm importdisk $vm_num /root/qcow/${system}.${ext} ${storage} 146 | fi 147 | sleep 3 148 | volid=$(pvesm list ${storage} | awk -v vmid="${vm_num}" '$5 == vmid && $1 ~ /\.raw$/ {print $1}' | tail -n 1) 149 | if [ -z "$volid" ]; then 150 | echo "No .raw file found for VM ID '${vm_num}' in storage '${storage}'. Searching for other formats..." 151 | volid=$(pvesm list ${storage} | awk -v vmid="${vm_num}" '$5 == vmid {print $1}' | tail -n 1) 152 | fi 153 | if [ -z "$volid" ]; then 154 | echo "Error: No file found for VM ID '${vm_num}' in storage '${storage}'" 155 | exit 1 156 | fi 157 | file_path=$(pvesm path ${volid}) 158 | if [ $? -ne 0 ] || [ -z "$file_path" ]; then 159 | echo "Error: Failed to resolve path for volume '${volid}'" 160 | exit 1 161 | fi 162 | file_name=$(basename "$file_path") 163 | echo "Found file: $file_name" 164 | echo "Attempting to set SCSI hardware with virtio-scsi-pci for VM $vm_num..." 165 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:${vm_num}/vm-${vm_num}-disk-0.raw 166 | if [ $? -ne 0 ]; then 167 | echo "Failed to set SCSI hardware with vm-${vm_num}-disk-0.raw. Trying alternative disk file..." 168 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:${vm_num}/$file_name 169 | if [ $? -ne 0 ]; then 170 | echo "Failed to set SCSI hardware with $file_name for VM $vm_num. Trying fallback file..." 171 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:$file_name 172 | if [ $? -ne 0 ]; then 173 | echo "All attempts failed. Exiting..." 174 | exit 1 175 | fi 176 | fi 177 | fi 178 | qm set $vm_num --bootdisk scsi0 179 | qm set $vm_num --boot order=scsi0 180 | qm set $vm_num --memory $memory 181 | # --swap 256 182 | if [[ "$system_arch" == "arm" ]]; then 183 | qm set $vm_num --scsi1 ${storage}:cloudinit 184 | else 185 | qm set $vm_num --ide1 ${storage}:cloudinit 186 | fi 187 | } 188 | 189 | configure_network() { 190 | user_ip="172.16.1.${vm_num}" 191 | if [ "$independent_ipv6" == "y" ]; then 192 | if [ ! -z "$host_ipv6_address" ] && [ ! -z "$ipv6_prefixlen" ] && [ ! -z "$ipv6_gateway" ] && [ ! -z "$ipv6_address_without_last_segment" ]; then 193 | if grep -q "vmbr2" /etc/network/interfaces; then 194 | qm set $vm_num --ipconfig0 ip=${user_ip}/24,gw=172.16.1.1 195 | qm set $vm_num --ipconfig1 ip6="${ipv6_address_without_last_segment}${vm_num}/128",gw6="${host_ipv6_address}" 196 | qm set $vm_num --nameserver 1.1.1.1 197 | # qm set $vm_num --nameserver 1.0.0.1 198 | qm set $vm_num --searchdomain local 199 | independent_ipv6_status="Y" 200 | else 201 | independent_ipv6_status="N" 202 | fi 203 | else 204 | independent_ipv6_status="N" 205 | fi 206 | else 207 | independent_ipv6_status="N" 208 | fi 209 | if [ "$independent_ipv6_status" == "N" ]; then 210 | _green "Use ${user_ip}/32 to set ipconfig0" 211 | qm set $vm_num --ipconfig0 ip=${user_ip}/24,gw=172.16.1.1 212 | qm set $vm_num --nameserver 8.8.8.8 213 | # qm set $vm_num --nameserver 8.8.4.4 214 | qm set $vm_num --searchdomain local 215 | fi 216 | qm set $vm_num --cipassword $password --ciuser $user 217 | sleep 5 218 | qm resize $vm_num scsi0 ${disk}G 219 | if [ $? -ne 0 ]; then 220 | if [[ $disk =~ ^[0-9]+G$ ]]; then 221 | dnum=${disk::-1} 222 | disk_m=$((dnum * 1024)) 223 | qm resize $vm_num scsi0 ${disk_m}M 224 | fi 225 | fi 226 | qm start $vm_num 227 | } 228 | 229 | setup_firewall() { 230 | iptables -t nat -A PREROUTING -d $extranet_ipv4 -p tcp -j DNAT --to-destination $user_ip 231 | iptables -t nat -A PREROUTING -d $extranet_ipv4 -p udp -j DNAT --to-destination $user_ip 232 | iptables -t nat -A POSTROUTING -s $user_ip -o vmbr0 -j SNAT --to-source $extranet_ipv4 233 | 234 | if [ ! -f "/etc/iptables/rules.v4" ]; then 235 | touch /etc/iptables/rules.v4 236 | fi 237 | iptables-save | awk '{if($1=="COMMIT"){delete x}}$1=="-A"?!x[$0]++:1' | iptables-restore 238 | iptables-save >/etc/iptables/rules.v4 239 | service netfilter-persistent restart 240 | } 241 | 242 | record_vm_info() { 243 | # 虚拟机的相关信息将会存储到对应的虚拟机的NOTE中,可在WEB端查看 244 | if [ "$independent_ipv6_status" == "N" ]; then 245 | echo "$vm_num $user $password $core $memory $disk $system $storage $extranet_ipv4" >>"vm${vm_num}" 246 | data=$(echo " VMID 用户名-username 密码-password CPU核数-CPU 内存-memory 硬盘-disk 系统-system 存储盘-storage 外网IP地址-ipv4") 247 | else 248 | echo "$vm_num $user $password $core $memory $disk $system $storage $extranet_ipv4 ${ipv6_address_without_last_segment}${vm_num}" >>"vm${vm_num}" 249 | data=$(echo " VMID 用户名-username 密码-password CPU核数-CPU 内存-memory 硬盘-disk 系统-system 存储盘-storage 外网IPV4-ipv4 外网IPV6-ipv6") 250 | fi 251 | values=$(cat "vm${vm_num}") 252 | IFS=' ' read -ra data_array <<<"$data" 253 | IFS=' ' read -ra values_array <<<"$values" 254 | length=${#data_array[@]} 255 | for ((i = 0; i < $length; i++)); do 256 | echo "${data_array[$i]} ${values_array[$i]}" 257 | echo "" 258 | done >"/tmp/temp${vm_num}.txt" 259 | sed -i 's/^/# /' "/tmp/temp${vm_num}.txt" 260 | cat "/etc/pve/qemu-server/${vm_num}.conf" >>"/tmp/temp${vm_num}.txt" 261 | cp "/tmp/temp${vm_num}.txt" "/etc/pve/qemu-server/${vm_num}.conf" 262 | rm -rf "/tmp/temp${vm_num}.txt" 263 | cat "vm${vm_num}" 264 | } 265 | 266 | main() { 267 | cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn1.spiritlhl.net/" "http://cdn2.spiritlhl.net/" "http://cdn3.spiritlhl.net/" "http://cdn4.spiritlhl.net/") 268 | check_cdn_file 269 | load_default_config || exit 1 270 | setup_locale 271 | get_system_arch || exit 1 272 | check_kvm_support 273 | init_params "$@" 274 | validate_vm_num || exit 1 275 | prepare_system_image 276 | check_network 277 | check_ipv6_config 278 | create_vm 279 | configure_network 280 | setup_firewall 281 | record_vm_info 282 | } 283 | 284 | main "$@" 285 | rm -rf default_vm_config.sh 286 | -------------------------------------------------------------------------------- /scripts/buildvm_macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2025.05.09 5 | 6 | # ./build_macos_vm.sh VMID CPU核数 内存 硬盘 SSH端口 VNC端口 系统 存储盘 独立IPV6 7 | # ./build_macos_vm.sh 100 2 4096 45 44022 45901 high-sierra local N 8 | 9 | cd /root >/dev/null 2>&1 10 | 11 | init_params() { 12 | vm_num="${1:-102}" 13 | core="${2:-1}" 14 | memory="${3:-512}" 15 | disk="${4:-5}" 16 | sshn="${5:-40001}" 17 | vnc_port="${6:-5901}" 18 | system="${7:-big‑sur}" 19 | storage="${8:-local}" 20 | independent_ipv6="${9:-N}" 21 | independent_ipv6=$(echo "$independent_ipv6" | tr '[:upper:]' '[:lower:]') 22 | rm -rf "vm$vm_num" 23 | } 24 | 25 | check_cdn() { 26 | local o_url=$1 27 | local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) # 打乱数组顺序 28 | for cdn_url in "${shuffled_cdn_urls[@]}"; do 29 | if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then 30 | export cdn_success_url="$cdn_url" 31 | return 32 | fi 33 | sleep 0.5 34 | done 35 | export cdn_success_url="" 36 | } 37 | 38 | check_cdn_file() { 39 | check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" 40 | if [ -n "$cdn_success_url" ]; then 41 | echo "CDN available, using CDN" 42 | else 43 | echo "No CDN available, no use CDN" 44 | fi 45 | } 46 | 47 | download_with_retry() { 48 | local url="$1" 49 | local output="$2" 50 | local max_attempts=5 51 | local attempt=1 52 | local delay=1 53 | while [ $attempt -le $max_attempts ]; do 54 | wget -q "$url" -O "$output" && return 0 55 | echo "Download failed: $url, try $attempt, wait $delay seconds and retry..." 56 | echo "下载失败:$url,尝试第 $attempt 次,等待 $delay 秒后重试..." 57 | sleep $delay 58 | attempt=$((attempt + 1)) 59 | delay=$((delay * 2)) 60 | [ $delay -gt 30 ] && delay=30 61 | done 62 | echo -e "\e[31mDownload failed: $url, maximum number of attempts exceeded ($max_attempts)\e[0m" 63 | echo -e "\e[31m下载失败:$url,超过最大尝试次数 ($max_attempts)\e[0m" 64 | return 1 65 | } 66 | 67 | load_default_config() { 68 | local config_url="${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/default_vm_config.sh" 69 | local config_file="default_vm_config.sh" 70 | if download_with_retry "$config_url" "$config_file"; then 71 | . "./$config_file" 72 | else 73 | echo -e "\e[31mUnable to load default configuration, script terminated.\e[0m" 74 | echo -e "\e[31m无法加载默认配置,脚本终止。\e[0m" 75 | exit 1 76 | fi 77 | } 78 | 79 | check_cpu_vendor() { 80 | # 检查是否为AMD或Intel CPU 81 | if grep -q "AMD" /proc/cpuinfo; then 82 | cpu_vendor="amd" 83 | elif grep -q "Intel" /proc/cpuinfo; then 84 | cpu_vendor="intel" 85 | else 86 | cpu_vendor="unknown" 87 | fi 88 | _green "CPU厂商: $cpu_vendor" 89 | } 90 | 91 | check_kvm_support_for_macos() { 92 | if [ -e /dev/kvm ]; then 93 | if [ -r /dev/kvm ] && [ -w /dev/kvm ]; then 94 | _green "KVM硬件加速可用,将使用硬件加速。" 95 | if [ "$cpu_vendor" = "amd" ]; then 96 | # AMD CPU必须使用Penryn类型,但在Proxmox参数中使用默认值q35 97 | cpu_type="q35" 98 | kvm_flag="--kvm 1" 99 | cpu_args="-device isa-applesmc,osk=\"ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc\" -smbios type=2 -device usb-kbd,bus=ehci.0,port=2 -device usb-mouse,bus=ehci.0,port=3 -cpu Penryn,kvm=on,vendor=GenuineIntel,+kvm_pv_unhalt,+kvm_pv_eoi,+hypervisor,+invtsc,+ssse3,+sse4.2,+popcnt,+avx,+avx2,+aes,+fma,+bmi1,+bmi2,+xsave,+xsaveopt,check" 100 | else 101 | # 非AMD CPU使用host 102 | cpu_type="q35" 103 | kvm_flag="--kvm 1" 104 | cpu_args="-device isa-applesmc,osk=\"ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc\" -smbios type=2 -device usb-kbd,bus=ehci.0,port=2 -device usb-mouse,bus=ehci.0,port=3 -cpu host,kvm=on,vendor=GenuineIntel,+kvm_pv_unhalt,+kvm_pv_eoi,+hypervisor,+invtsc" 105 | fi 106 | return 0 107 | fi 108 | fi 109 | if grep -E 'vmx|svm' /proc/cpuinfo >/dev/null; then 110 | _yellow "CPU支持虚拟化,但/dev/kvm不可用,请检查BIOS设置或内核模块。" 111 | else 112 | _yellow "CPU不支持硬件虚拟化。" 113 | fi 114 | _yellow "将使用QEMU软件模拟(TCG)模式,性能会受到影响。" 115 | # 软件模拟模式下使用qemu64 116 | cpu_type="q35" 117 | kvm_flag="--kvm 0" 118 | cpu_args="-device isa-applesmc,osk=\"ourhardworkbythesewordsguardedpleasedontsteal(c)AppleComputerInc\" -smbios type=2 -device usb-kbd,bus=ehci.0,port=2 -device usb-mouse,bus=ehci.0,port=3 -cpu qemu64" 119 | return 1 120 | } 121 | 122 | check_iso_exists() { 123 | system_iso="/var/lib/vz/template/iso/${system}.iso" 124 | opencore_iso="/var/lib/vz/template/iso/opencore.iso" 125 | if [ ! -f "$system_iso" ]; then 126 | _red "错误:系统镜像 '${system}.iso' 未找到" 127 | _yellow "当前支持的系统镜像有:" 128 | available_isos=$(find /var/lib/vz/template/iso/ -name "*.iso" | sed 's|/var/lib/vz/template/iso/||g' | sed 's/\.iso$//g' | sort) 129 | for iso in $available_isos; do 130 | _green "- $iso" 131 | done 132 | return 1 133 | fi 134 | if [ ! -f "$opencore_iso" ]; then 135 | _red "错误:OpenCore引导镜像 'opencore.iso' 未找到" 136 | return 1 137 | fi 138 | return 0 139 | } 140 | 141 | create_vm() { 142 | if [[ "$system" == "high-sierra" || "$system" == "mojave" ]]; then 143 | disk_device="--sata0" 144 | else 145 | disk_device="--virtio0" 146 | fi 147 | if [ "$independent_ipv6" == "n" ]; then 148 | qm create $vm_num --agent 1 --scsihw virtio-scsi-pci \ 149 | --cores $core --sockets 1 \ 150 | --net0 vmxnet3,bridge=vmbr1,firewall=0 \ 151 | --args "$cpu_args" \ 152 | --machine q35 \ 153 | --ostype other \ 154 | --bios ovmf \ 155 | --memory $memory \ 156 | --vga vmware \ 157 | --balloon 0 \ 158 | --tablet 1 \ 159 | --autostart 0 \ 160 | --onboot 0 \ 161 | --numa 0 \ 162 | --vmgenid 1 \ 163 | --name macos-${vm_num} \ 164 | ${kvm_flag} 165 | else 166 | qm create $vm_num --agent 1 --scsihw virtio-scsi-pci \ 167 | --cores $core --sockets 1 \ 168 | --net0 vmxnet3,bridge=vmbr1,firewall=0 \ 169 | --net1 vmxnet3,bridge=vmbr2,firewall=0 \ 170 | --args "$cpu_args" \ 171 | --machine q35 \ 172 | --ostype other \ 173 | --bios ovmf \ 174 | --memory $memory \ 175 | --vga vmware \ 176 | --balloon 0 \ 177 | --tablet 1 \ 178 | --autostart 0 \ 179 | --onboot 0 \ 180 | --numa 0 \ 181 | --vmgenid 1 \ 182 | --name macos-${vm_num} \ 183 | ${kvm_flag} 184 | fi 185 | qm set $vm_num --efidisk0 ${storage}:4 186 | if [[ "$system" == "high-sierra" || "$system" == "mojave" ]]; then 187 | qm set $vm_num --sata0 ${storage}:${disk},cache=none,ssd=1,discard=on 188 | qm resize $vm_num sata0 ${disk}G 189 | if [ $? -ne 0 ]; then 190 | if [[ $disk =~ ^[0-9]+G$ ]]; then 191 | dnum=${disk::-1} 192 | disk_m=$((dnum * 1024)) 193 | qm resize $vm_num sata0 ${disk_m}M 194 | fi 195 | fi 196 | else 197 | qm set $vm_num --virtio0 ${storage}:${disk},cache=none,discard=on 198 | qm resize $vm_num virtio0 ${disk}G 199 | if [ $? -ne 0 ]; then 200 | if [[ $disk =~ ^[0-9]+G$ ]]; then 201 | dnum=${disk::-1} 202 | disk_m=$((dnum * 1024)) 203 | qm resize $vm_num virtio0 ${disk_m}M 204 | fi 205 | fi 206 | fi 207 | qm set $vm_num --ide0 ${storage}:iso/opencore.iso,media=cdrom,cache=unsafe 208 | qm set $vm_num --ide1 ${storage}:iso/${system}.iso,media=cdrom,cache=unsafe 209 | if [[ "$system" == "high-sierra" || "$system" == "mojave" ]]; then 210 | grep -q '^boot:' /etc/pve/qemu-server/${vm_num}.conf && 211 | sed -i 's/^boot:.*/boot: order=ide0;ide1;sata0;net0/' /etc/pve/qemu-server/${vm_num}.conf || 212 | echo 'boot: order=ide0;ide1;sata0;net0' >>/etc/pve/qemu-server/${vm_num}.conf 213 | else 214 | grep -q '^boot:' /etc/pve/qemu-server/${vm_num}.conf && 215 | sed -i 's/^boot:.*/boot: order=ide0;ide1;virtio0;net0/' /etc/pve/qemu-server/${vm_num}.conf || 216 | echo 'boot: order=ide0;ide1;virtio0;net0' >>/etc/pve/qemu-server/${vm_num}.conf 217 | fi 218 | sed -i 's/media=cdrom/media=disk/' /etc/pve/qemu-server/${vm_num}.conf 219 | qemu_needs_fix=0 220 | if qemu-system-x86_64 --version | grep -e "6.1" -e "6.2" -e "7.1" -e "7.2" -e "8.0" -e "8.1" -e "9.0.2" -e "9.2.0" >/dev/null; then 221 | qemu_needs_fix=1 222 | fi 223 | if [ "$cpu_vendor" = "amd" ]; then 224 | if [ $qemu_needs_fix -eq 1 ]; then 225 | sed -i 's/+bmi2,+xsave,+xsaveopt,check/+bmi2,+xsave,+xsaveopt,check -global ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off/g' /etc/pve/qemu-server/${vm_num}.conf 226 | fi 227 | else # Intel or other CPU 228 | if [ $qemu_needs_fix -eq 1 ]; then 229 | sed -i 's/+kvm_pv_eoi,+hypervisor,+invtsc/+kvm_pv_eoi,+hypervisor,+invtsc -global ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off/g' /etc/pve/qemu-server/${vm_num}.conf 230 | fi 231 | fi 232 | return 0 233 | } 234 | 235 | configure_network() { 236 | user_ip="172.16.1.${vm_num}" 237 | if [ "$independent_ipv6" == "y" ]; then 238 | if [ ! -z "$host_ipv6_address" ] && [ ! -z "$ipv6_prefixlen" ] && [ ! -z "$ipv6_gateway" ] && [ ! -z "$ipv6_address_without_last_segment" ]; then 239 | if grep -q "vmbr2" /etc/network/interfaces; then 240 | independent_ipv6_status="Y" 241 | else 242 | independent_ipv6_status="N" 243 | fi 244 | else 245 | independent_ipv6_status="N" 246 | fi 247 | else 248 | independent_ipv6_status="N" 249 | fi 250 | return 0 251 | } 252 | 253 | setup_port_forwarding() { 254 | user_ip="172.16.1.${vm_num}" 255 | iptables -t nat -A PREROUTING -i vmbr0 -p tcp --dport ${sshn} -j DNAT --to-destination ${user_ip}:22 256 | iptables -t nat -A PREROUTING -i vmbr0 -p tcp --dport ${vnc_port} -j DNAT --to-destination ${user_ip}:5900 257 | if [ ! -f "/etc/iptables/rules.v4" ]; then 258 | touch /etc/iptables/rules.v4 259 | fi 260 | iptables-save | awk '{if($1=="COMMIT"){delete x}}$1=="-A"?!x[$0]++:1' | iptables-restore 261 | iptables-save >/etc/iptables/rules.v4 262 | service netfilter-persistent restart 263 | } 264 | 265 | save_vm_info() { 266 | if [ "$independent_ipv6_status" == "Y" ]; then 267 | echo "$vm_num $core $memory $disk $sshn $vnc_port $system $storage ${ipv6_address_without_last_segment}${vm_num}" >>"vm${vm_num}" 268 | data=$(echo " VMID CPU核数-CPU 内存-memory 硬盘-disk SSH端口 VNC端口 系统-system 存储盘-storage 独立IPV6地址-ipv6_address") 269 | else 270 | echo "$vm_num $core $memory $disk $sshn $vnc_port $system $storage" >>"vm${vm_num}" 271 | data=$(echo " VMID CPU核数-CPU 内存-memory 硬盘-disk SSH端口 VNC端口 系统-system 存储盘-storage") 272 | fi 273 | values=$(cat "vm${vm_num}") 274 | IFS=' ' read -ra data_array <<<"$data" 275 | IFS=' ' read -ra values_array <<<"$values" 276 | length=${#data_array[@]} 277 | for ((i = 0; i < $length; i++)); do 278 | echo "${data_array[$i]} ${values_array[$i]}" 279 | echo "" 280 | done >"/tmp/temp${vm_num}.txt" 281 | sed -i 's/^/# /' "/tmp/temp${vm_num}.txt" 282 | cat "/etc/pve/qemu-server/${vm_num}.conf" >>"/tmp/temp${vm_num}.txt" 283 | cp "/tmp/temp${vm_num}.txt" "/etc/pve/qemu-server/${vm_num}.conf" 284 | rm -rf "/tmp/temp${vm_num}.txt" 285 | cat "vm${vm_num}" 286 | } 287 | 288 | main() { 289 | cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn1.spiritlhl.net/" "http://cdn2.spiritlhl.net/" "http://cdn3.spiritlhl.net/" "http://cdn4.spiritlhl.net/") 290 | check_cdn_file 291 | load_default_config || exit 1 292 | setup_locale 293 | get_system_arch 294 | init_params "$@" 295 | validate_vm_num || exit 1 296 | get_system_arch || exit 1 297 | check_cpu_vendor 298 | check_kvm_support_for_macos 299 | check_iso_exists || exit 1 300 | check_ipv6_config || exit 1 301 | create_vm || exit 1 302 | configure_network 303 | setup_port_forwarding 304 | save_vm_info 305 | _green "macOS虚拟机创建完成!" 306 | _green "VM ID: $vm_num" 307 | _green "SSH端口: $sshn, VNC端口: $vnc_port" 308 | _green "CPU厂商: $cpu_vendor, 使用OpenCore引导和${system}系统镜像" 309 | } 310 | 311 | main "$@" 312 | rm -rf default_vm_config.sh 313 | -------------------------------------------------------------------------------- /scripts/buildvm_onlyv6.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2025.05.17 5 | # 自动选择要绑定的IPV6地址 6 | # ./buildvm_onlyv6.sh VMID 用户名 密码 CPU核数 内存 硬盘 系统 存储盘 7 | # ./buildvm_onlyv6.sh 152 test1 1234567 1 512 5 debian11 local 8 | 9 | cd /root >/dev/null 2>&1 10 | 11 | init_params() { 12 | vm_num="${1:-152}" 13 | user="${2:-test}" 14 | password="${3:-123456}" 15 | core="${4:-1}" 16 | memory="${5:-512}" 17 | disk="${6:-5}" 18 | system="${7:-ubuntu22}" 19 | storage="${8:-local}" 20 | rm -rf "vm$vm_num" 21 | if [ ! -d "qcow" ]; then 22 | mkdir qcow 23 | fi 24 | } 25 | 26 | check_environment() { 27 | if [ ! -f /usr/local/bin/pve_check_ipv6 ]; then 28 | _yellow "No ipv6 address exists to open a server with a standalone IPV6 address" 29 | fi 30 | if ! grep -q "vmbr2" /etc/network/interfaces; then 31 | _yellow "No vmbr2 exists to open a server with a standalone IPV6 address" 32 | fi 33 | service_status=$(systemctl is-active ndpresponder.service) 34 | if [ "$service_status" == "active" ]; then 35 | _green "The ndpresponder service started successfully and is running, and the host can open a service with a separate IPV6 address." 36 | _green "ndpresponder服务启动成功且正在运行,宿主机可开设带独立IPV6地址的服务。" 37 | else 38 | _green "The status of the ndpresponder service is abnormal and the host may not open a service with a separate IPV6 address." 39 | _green "ndpresponder服务状态异常,宿主机不可开设带独立IPV6地址的服务。" 40 | exit 1 41 | fi 42 | } 43 | 44 | check_cdn() { 45 | local o_url=$1 46 | local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) # 打乱数组顺序 47 | for cdn_url in "${shuffled_cdn_urls[@]}"; do 48 | if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then 49 | export cdn_success_url="$cdn_url" 50 | return 51 | fi 52 | sleep 0.5 53 | done 54 | export cdn_success_url="" 55 | } 56 | 57 | check_cdn_file() { 58 | check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" 59 | if [ -n "$cdn_success_url" ]; then 60 | echo "CDN available, using CDN" 61 | else 62 | echo "No CDN available, no use CDN" 63 | fi 64 | } 65 | 66 | download_with_retry() { 67 | local url="$1" 68 | local output="$2" 69 | local max_attempts=5 70 | local attempt=1 71 | local delay=1 72 | while [ $attempt -le $max_attempts ]; do 73 | wget -q "$url" -O "$output" && return 0 74 | echo "Download failed: $url, try $attempt, wait $delay seconds and retry..." 75 | echo "下载失败:$url,尝试第 $attempt 次,等待 $delay 秒后重试..." 76 | sleep $delay 77 | attempt=$((attempt + 1)) 78 | delay=$((delay * 2)) 79 | [ $delay -gt 30 ] && delay=30 80 | done 81 | echo -e "\e[31mDownload failed: $url, maximum number of attempts exceeded ($max_attempts)\e[0m" 82 | echo -e "\e[31m下载失败:$url,超过最大尝试次数 ($max_attempts)\e[0m" 83 | return 1 84 | } 85 | 86 | load_default_config() { 87 | local config_url="${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/default_vm_config.sh" 88 | local config_file="default_vm_config.sh" 89 | if download_with_retry "$config_url" "$config_file"; then 90 | . "./$config_file" 91 | else 92 | echo -e "\e[31mUnable to load default configuration, script terminated.\e[0m" 93 | echo -e "\e[31m无法加载默认配置,脚本终止。\e[0m" 94 | exit 1 95 | fi 96 | } 97 | 98 | get_ipv6_info() { 99 | if [ -f /usr/local/bin/pve_check_ipv6 ]; then 100 | host_ipv6_address=$(cat /usr/local/bin/pve_check_ipv6) 101 | ipv6_address_without_last_segment="${host_ipv6_address%:*}:" 102 | fi 103 | if [ -f /usr/local/bin/pve_ipv6_prefixlen ]; then 104 | ipv6_prefixlen=$(cat /usr/local/bin/pve_ipv6_prefixlen) 105 | fi 106 | if [ -f /usr/local/bin/pve_ipv6_gateway ]; then 107 | ipv6_gateway=$(cat /usr/local/bin/pve_ipv6_gateway) 108 | fi 109 | } 110 | 111 | create_vm() { 112 | qm create $vm_num \ 113 | --agent 1 \ 114 | --scsihw virtio-scsi-single \ 115 | --serial0 socket \ 116 | --cores $core \ 117 | --sockets 1 \ 118 | --cpu $cpu_type \ 119 | --net0 virtio,bridge=vmbr1,firewall=0 \ 120 | --net1 virtio,bridge=vmbr2,firewall=0 \ 121 | --ostype l26 \ 122 | ${kvm_flag} 123 | if [ "$system_arch" = "x86" ] || [ "$system_arch" = "x86_64" ]; then 124 | qm importdisk $vm_num /root/qcow/${system}.qcow2 ${storage} 125 | else 126 | qm set $vm_num --bios ovmf 127 | qm importdisk $vm_num /root/qcow/${system}.${ext} ${storage} 128 | fi 129 | sleep 3 130 | } 131 | 132 | configure_vm() { 133 | volid=$(pvesm list ${storage} | awk -v vmid="${vm_num}" '$5 == vmid && $1 ~ /\.raw$/ {print $1}' | tail -n 1) 134 | if [ -z "$volid" ]; then 135 | echo "No .raw file found for VM ID '${vm_num}' in storage '${storage}'. Searching for other formats..." 136 | volid=$(pvesm list ${storage} | awk -v vmid="${vm_num}" '$5 == vmid {print $1}' | tail -n 1) 137 | fi 138 | if [ -z "$volid" ]; then 139 | echo "Error: No file found for VM ID '${vm_num}' in storage '${storage}'" 140 | exit 1 141 | fi 142 | file_path=$(pvesm path ${volid}) 143 | if [ $? -ne 0 ] || [ -z "$file_path" ]; then 144 | echo "Error: Failed to resolve path for volume '${volid}'" 145 | exit 1 146 | fi 147 | file_name=$(basename "$file_path") 148 | echo "Found file: $file_name" 149 | echo "Attempting to set SCSI hardware with virtio-scsi-pci for VM $vm_num..." 150 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:${vm_num}/vm-${vm_num}-disk-0.raw 151 | if [ $? -ne 0 ]; then 152 | echo "Failed to set SCSI hardware with vm-${vm_num}-disk-0.raw. Trying alternative disk file..." 153 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:${vm_num}/$file_name 154 | if [ $? -ne 0 ]; then 155 | echo "Failed to set SCSI hardware with $file_name for VM $vm_num. Trying fallback file..." 156 | qm set $vm_num --scsihw virtio-scsi-pci --scsi0 ${storage}:$file_name 157 | if [ $? -ne 0 ]; then 158 | echo "All attempts failed. Exiting..." 159 | exit 1 160 | fi 161 | fi 162 | fi 163 | qm set $vm_num --bootdisk scsi0 164 | qm set $vm_num --boot order=scsi0 165 | qm set $vm_num --memory $memory 166 | # --swap 256 167 | if [[ "$system_arch" == "arm" ]]; then 168 | qm set $vm_num --scsi1 ${storage}:cloudinit 169 | else 170 | qm set $vm_num --ide1 ${storage}:cloudinit 171 | fi 172 | qm set $vm_num --nameserver 1.1.1.1 173 | # qm set $vm_num --nameserver 1.0.0.1 174 | qm set $vm_num --searchdomain local 175 | user_ip="172.16.1.${vm_num}" 176 | qm set $vm_num --ipconfig0 ip=${user_ip}/24,gw=172.16.1.1 177 | qm set $vm_num --ipconfig1 ip6="${ipv6_address_without_last_segment}${vm_num}/128",gw6="${host_ipv6_address}" 178 | qm set $vm_num --cipassword $password --ciuser $user 179 | sleep 5 180 | } 181 | 182 | resize_disk() { 183 | qm resize $vm_num scsi0 ${disk}G 184 | if [ $? -ne 0 ]; then 185 | if [[ $disk =~ ^[0-9]+G$ ]]; then 186 | dnum=${disk::-1} 187 | disk_m=$((dnum * 1024)) 188 | qm resize $vm_num scsi0 ${disk_m}M 189 | fi 190 | fi 191 | } 192 | 193 | start_vm_and_save_info() { 194 | qm start $vm_num 195 | echo "$vm_num $user $password $core $memory $disk $system $storage ${ipv6_address_without_last_segment}${vm_num}" >>"vm${vm_num}" 196 | # 虚拟机的相关信息将会存储到对应的虚拟机的NOTE中,可在WEB端查看 197 | data=$(echo " VMID 用户名-username 密码-password CPU核数-CPU 内存-memory 硬盘-disk 系统-system 存储盘-storage 外网IPV6-ipv6") 198 | values=$(cat "vm${vm_num}") 199 | IFS=' ' read -ra data_array <<<"$data" 200 | IFS=' ' read -ra values_array <<<"$values" 201 | length=${#data_array[@]} 202 | for ((i = 0; i < $length; i++)); do 203 | echo "${data_array[$i]} ${values_array[$i]}" 204 | echo "" 205 | done >"/tmp/temp${vm_num}.txt" 206 | sed -i 's/^/# /' "/tmp/temp${vm_num}.txt" 207 | cat "/etc/pve/qemu-server/${vm_num}.conf" >>"/tmp/temp${vm_num}.txt" 208 | cp "/tmp/temp${vm_num}.txt" "/etc/pve/qemu-server/${vm_num}.conf" 209 | rm -rf "/tmp/temp${vm_num}.txt" 210 | cat "vm${vm_num}" 211 | } 212 | 213 | main() { 214 | cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn1.spiritlhl.net/" "http://cdn2.spiritlhl.net/" "http://cdn3.spiritlhl.net/" "http://cdn4.spiritlhl.net/") 215 | check_cdn_file 216 | load_default_config || exit 1 217 | setup_locale 218 | init_params "$@" 219 | validate_vm_num || exit 1 220 | check_environment 221 | get_system_arch || exit 1 222 | check_kvm_support 223 | prepare_system_image 224 | get_ipv6_info 225 | create_vm 226 | configure_vm 227 | resize_disk 228 | start_vm_and_save_info 229 | } 230 | 231 | main "$@" 232 | rm -rf default_vm_config.sh 233 | -------------------------------------------------------------------------------- /scripts/create_ct.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2025.04.20 5 | 6 | # cd /root 7 | 8 | _red() { echo -e "\033[31m\033[01m$@\033[0m"; } 9 | _green() { echo -e "\033[32m\033[01m$@\033[0m"; } 10 | _yellow() { echo -e "\033[33m\033[01m$@\033[0m"; } 11 | _blue() { echo -e "\033[36m\033[01m$@\033[0m"; } 12 | reading() { read -rp "$(_green "$1")" "$2"; } 13 | utf8_locale=$(locale -a 2>/dev/null | grep -i -m 1 -E "UTF-8|utf8") 14 | if [[ -z "$utf8_locale" ]]; then 15 | echo "No UTF-8 locale found" 16 | else 17 | export LC_ALL="$utf8_locale" 18 | export LANG="$utf8_locale" 19 | export LANGUAGE="$utf8_locale" 20 | echo "Locale set to $utf8_locale" 21 | fi 22 | 23 | get_system_arch() { 24 | local sysarch="$(uname -m)" 25 | if [ "${sysarch}" = "unknown" ] || [ "${sysarch}" = "" ]; then 26 | local sysarch="$(arch)" 27 | fi 28 | # 根据架构信息设置系统位数并下载文件,其余 * 包括了 x86_64 29 | case "${sysarch}" in 30 | "i386" | "i686" | "x86_64") 31 | system_arch="x86" 32 | ;; 33 | "armv7l" | "armv8" | "armv8l" | "aarch64") 34 | system_arch="arch" 35 | ;; 36 | *) 37 | system_arch="" 38 | ;; 39 | esac 40 | } 41 | 42 | check_cdn() { 43 | local o_url=$1 44 | local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) # 打乱数组顺序 45 | for cdn_url in "${shuffled_cdn_urls[@]}"; do 46 | if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then 47 | export cdn_success_url="$cdn_url" 48 | return 49 | fi 50 | sleep 0.5 51 | done 52 | export cdn_success_url="" 53 | } 54 | 55 | check_cdn_file() { 56 | check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" 57 | if [ -n "$cdn_success_url" ]; then 58 | _yellow "CDN available, using CDN" 59 | else 60 | _yellow "No CDN available, no use CDN" 61 | fi 62 | } 63 | 64 | cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn3.spiritlhl.net/" "http://cdn1.spiritlhl.net/" "https://ghproxy.com/" "http://cdn2.spiritlhl.net/") 65 | check_cdn_file 66 | 67 | pre_check() { 68 | home_dir=$(eval echo "~$(whoami)") 69 | if [ "$home_dir" != "/root" ]; then 70 | _red "The script will exit if the current path is not /root." 71 | _red "当前路径不是/root,脚本将退出。" 72 | exit 1 73 | fi 74 | if ! command -v dos2unix >/dev/null 2>&1; then 75 | apt-get install dos2unix -y 76 | fi 77 | if [ ! -f "buildct.sh" ]; then 78 | curl -L ${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/buildct.sh -o buildct.sh && chmod +x buildct.sh 79 | dos2unix buildct.sh 80 | fi 81 | } 82 | 83 | # files=$(find . -maxdepth 1 -name "ct*" | sort) 84 | # if [ -n "$files" ]; then 85 | # for file in $files 86 | # do 87 | # cat "$file" >> ctlog 88 | # done 89 | # fi 90 | 91 | check_info() { 92 | log_file="ctlog" 93 | if [ ! -f "ctlog" ]; then 94 | _yellow "ctrlog file does not exist in the current directory" 95 | _yellow "当前目录下不存在ctlog文件" 96 | ct_num=113 97 | web2_port=20003 98 | port_end=30025 99 | else 100 | while read line; do 101 | last_line="$line" 102 | done <"$log_file" 103 | last_line_array=($last_line) 104 | ct_num="${last_line_array[0]}" 105 | password="${last_line_array[1]}" 106 | ssh_port="${last_line_array[5]}" 107 | web1_port="${last_line_array[6]}" 108 | web2_port="${last_line_array[7]}" 109 | port_start="${last_line_array[8]}" 110 | port_end="${last_line_array[9]}" 111 | system="${last_line_array[10]}" 112 | storage="${last_line_array[11]}" 113 | _green "Information corresponding to the current last NAT container:" 114 | _green "当前最后一个NAT容器对应的信息:" 115 | echo "NAT容器(NAT container): $ct_num" 116 | # echo "用户名: $user" 117 | # echo "密码: $password" 118 | echo "外网SSH端口(Extranet SSH port): $ssh_port" 119 | echo "外网80端口(Extranet port 80): $web1_port" 120 | echo "外网443端口(Extranet port 443): $web2_port" 121 | echo "外网其他端口范围(Other port ranges): $port_start-$port_end" 122 | echo "系统(System):$system" 123 | echo "存储盘(Storage Disk):$storage" 124 | fi 125 | } 126 | 127 | build_new_cts() { 128 | while true; do 129 | _green "How many more NAT servers need to be generated? (Enter how many new NAT servers to add):" 130 | reading "还需要生成几个NAT服务器?(输入新增几个NAT服务器):" new_nums 131 | if [[ "$new_nums" =~ ^[1-9][0-9]*$ ]]; then 132 | break 133 | else 134 | _yellow "Invalid input, please enter a positive integer." 135 | _yellow "输入无效,请输入一个正整数。" 136 | fi 137 | done 138 | while true; do 139 | _green "How many CPUs are assigned to each container? (Enter 1 if 1 core is assigned to each container):" 140 | reading "每个容器分配几个CPU?(若每个容器分配1核,则输入1):" cpu_nums 141 | if [[ "$cpu_nums" =~ ^[1-9][0-9]*$ ]]; then 142 | break 143 | else 144 | _yellow "Invalid input, please enter a positive integer." 145 | _yellow "输入无效,请输入一个正整数。" 146 | fi 147 | done 148 | while true; do 149 | _green "How much memory is allocated per container? (If 512 MB of memory is allocated per container, enter 512):" 150 | reading "每个容器分配多少内存?(若每个容器分配512MB内存,则输入512):" memory_nums 151 | if [[ "$memory_nums" =~ ^[1-9][0-9]*$ ]]; then 152 | break 153 | else 154 | _yellow "Invalid input, please enter a positive integer." 155 | _yellow "输入无效,请输入一个正整数。" 156 | fi 157 | done 158 | while true; do 159 | _green "On which storage drive are the containers opened? (Leave blank or enter 'local' if the container is to be opened on the system disk):" 160 | reading "容器们开设在哪个存储盘上?(若容器要开设在系统盘上,则留空或输入local):" storage 161 | if [ -z "$storage" ]; then 162 | storage="local" 163 | fi 164 | break 165 | done 166 | while true; do 167 | _green "How many hard disks are allocated per container? (If 5G hard drives are allocated per container, enter 5):" 168 | reading "每个容器分配多少硬盘?(若每个容器分配5G硬盘,则输入5):" disk_nums 169 | if [[ "$disk_nums" =~ ^[1-9][0-9]*$ ]]; then 170 | break 171 | else 172 | _yellow "Invalid input, please enter a positive integer." 173 | _yellow "输入无效,请输入一个正整数。" 174 | fi 175 | done 176 | while true; do 177 | _green "What system does each container use? (Leave blank or enter debian11 if all use debian11):" 178 | reading "每个容器都使用什么系统?(若都使用debian11,则留空或输入debian11):" system 179 | if [ -z "$system" ]; then 180 | system="debian11" 181 | fi 182 | # 这块待增加系统列表查询 183 | break 184 | done 185 | while true; do 186 | _green "Need to attach a separate IPV6 address to each container?([N]/y)" 187 | reading "是否附加独立的IPV6地址?([N]/y)" independent_ipv6 188 | independent_ipv6=$(echo "$independent_ipv6" | tr '[:upper:]' '[:lower:]') 189 | if [ "$independent_ipv6" = "y" ] || [ "$independent_ipv6" = "n" ]; then 190 | break 191 | else 192 | _yellow "Invalid input, please enter y or n." 193 | _yellow "输入无效,请输入Y或者N。" 194 | fi 195 | done 196 | for ((i = 1; i <= $new_nums; i++)); do 197 | ct_num=$(($ct_num + 1)) 198 | ori=$(date | md5sum) 199 | password=${ori:2:9} 200 | ssh_port=$(($web2_port + 1)) 201 | web1_port=$(($web2_port + 2)) 202 | web2_port=$(($web1_port + 1)) 203 | port_start=$(($port_end + 1)) 204 | port_end=$(($port_start + 25)) 205 | ./buildct.sh $ct_num $password $cpu_nums $memory_nums $disk_nums $ssh_port $web1_port $web2_port $port_start $port_end $system $storage $independent_ipv6 206 | cat "ct$ct_num" >>ctlog 207 | rm -rf "ct$ct_num" 208 | if [ "$i" = "$new_nums" ]; then 209 | break 210 | fi 211 | sleep 30 212 | done 213 | } 214 | 215 | pre_check 216 | check_info 217 | build_new_cts 218 | check_info 219 | -------------------------------------------------------------------------------- /scripts/create_vm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2025.04.20 5 | 6 | # cd /root 7 | 8 | _red() { echo -e "\033[31m\033[01m$@\033[0m"; } 9 | _green() { echo -e "\033[32m\033[01m$@\033[0m"; } 10 | _yellow() { echo -e "\033[33m\033[01m$@\033[0m"; } 11 | _blue() { echo -e "\033[36m\033[01m$@\033[0m"; } 12 | reading() { read -rp "$(_green "$1")" "$2"; } 13 | utf8_locale=$(locale -a 2>/dev/null | grep -i -m 1 -E "UTF-8|utf8") 14 | if [[ -z "$utf8_locale" ]]; then 15 | echo "No UTF-8 locale found" 16 | else 17 | export LC_ALL="$utf8_locale" 18 | export LANG="$utf8_locale" 19 | export LANGUAGE="$utf8_locale" 20 | echo "Locale set to $utf8_locale" 21 | fi 22 | 23 | get_system_arch() { 24 | local sysarch="$(uname -m)" 25 | if [ "${sysarch}" = "unknown" ] || [ "${sysarch}" = "" ]; then 26 | local sysarch="$(arch)" 27 | fi 28 | # 根据架构信息设置系统位数并下载文件,其余 * 包括了 x86_64 29 | case "${sysarch}" in 30 | "i386" | "i686" | "x86_64") 31 | system_arch="x86" 32 | ;; 33 | "armv7l" | "armv8" | "armv8l" | "aarch64") 34 | system_arch="arch" 35 | ;; 36 | *) 37 | system_arch="" 38 | ;; 39 | esac 40 | } 41 | 42 | check_cdn() { 43 | local o_url=$1 44 | local shuffled_cdn_urls=($(shuf -e "${cdn_urls[@]}")) # 打乱数组顺序 45 | for cdn_url in "${shuffled_cdn_urls[@]}"; do 46 | if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then 47 | export cdn_success_url="$cdn_url" 48 | return 49 | fi 50 | sleep 0.5 51 | done 52 | export cdn_success_url="" 53 | } 54 | 55 | check_cdn_file() { 56 | check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" 57 | if [ -n "$cdn_success_url" ]; then 58 | _yellow "CDN available, using CDN" 59 | else 60 | _yellow "No CDN available, no use CDN" 61 | fi 62 | } 63 | 64 | cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn3.spiritlhl.net/" "http://cdn1.spiritlhl.net/" "https://ghproxy.com/" "http://cdn2.spiritlhl.net/") 65 | check_cdn_file 66 | 67 | pre_check() { 68 | home_dir=$(eval echo "~$(whoami)") 69 | if [ "$home_dir" != "/root" ]; then 70 | _red "The script will exit if the current path is not /root." 71 | _red "当前路径不是/root,脚本将退出。" 72 | exit 1 73 | fi 74 | if ! command -v dos2unix >/dev/null 2>&1; then 75 | apt-get install dos2unix -y 76 | fi 77 | if [ ! -f "buildvm.sh" ]; then 78 | curl -L ${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/pve/main/scripts/buildvm.sh -o buildvm.sh && chmod +x buildvm.sh 79 | dos2unix buildvm.sh 80 | fi 81 | } 82 | 83 | # files=$(find . -maxdepth 1 -name "vm*" | sort) 84 | # if [ -n "$files" ]; then 85 | # for file in $files 86 | # do 87 | # cat "$file" >> vmlog 88 | # done 89 | # fi 90 | 91 | check_info() { 92 | log_file="vmlog" 93 | if [ ! -f "vmlog" ]; then 94 | _yellow "vmlog file does not exist in the current directory" 95 | _yellow "当前目录下不存在vmlog文件" 96 | vm_num=112 97 | web2_port=40003 98 | port_end=50025 99 | else 100 | while read line; do 101 | last_line="$line" 102 | done <"$log_file" 103 | last_line_array=($last_line) 104 | vm_num="${last_line_array[0]}" 105 | user="${last_line_array[1]}" 106 | password="${last_line_array[2]}" 107 | ssh_port="${last_line_array[6]}" 108 | web1_port="${last_line_array[7]}" 109 | web2_port="${last_line_array[8]}" 110 | port_start="${last_line_array[9]}" 111 | port_end="${last_line_array[10]}" 112 | system="${last_line_array[11]}" 113 | storage="${last_line_array[12]}" 114 | _green "Current information corresponding to the last NAT server:" 115 | _green "当前最后一个NAT服务器对应的信息:" 116 | echo "NAT服务器(NAT Server): $vm_num" 117 | # echo "用户名: $user" 118 | # echo "密码: $password" 119 | echo "外网SSH端口(Extranet SSH port): $ssh_port" 120 | echo "外网80端口(Extranet port 80): $web1_port" 121 | echo "外网443端口(Extranet port 443): $web2_port" 122 | echo "外网其他端口范围(Other port ranges): $port_start-$port_end" 123 | echo "系统(System):$system" 124 | echo "存储盘(Storage Disk):$storage" 125 | fi 126 | } 127 | 128 | build_new_vms() { 129 | while true; do 130 | _green "How many more NAT servers need to be generated? (Enter how many new NAT servers to add):" 131 | reading "还需要生成几个NAT服务器?(输入新增几个NAT服务器):" new_nums 132 | if [[ "$new_nums" =~ ^[1-9][0-9]*$ ]]; then 133 | break 134 | else 135 | _yellow "Invalid input, please enter a positive integer." 136 | _yellow "输入无效,请输入一个正整数。" 137 | fi 138 | done 139 | while true; do 140 | _green "How many CPUs are assigned to each virtual machine? (Enter 1 if 1 core is assigned to each virtual machine):" 141 | reading "每个虚拟机分配几个CPU?(若每个虚拟机分配1核,则输入1):" cpu_nums 142 | if [[ "$cpu_nums" =~ ^[1-9][0-9]*$ ]]; then 143 | break 144 | else 145 | _yellow "Invalid input, please enter a positive integer." 146 | _yellow "输入无效,请输入一个正整数。" 147 | fi 148 | done 149 | while true; do 150 | _green "How much memory is allocated per virtual machine? (If 512 MB of memory is allocated per virtual machine, enter 512):" 151 | reading "每个虚拟机分配多少内存?(若每个虚拟机分配512MB内存,则输入512):" memory_nums 152 | if [[ "$memory_nums" =~ ^[1-9][0-9]*$ ]]; then 153 | break 154 | else 155 | _yellow "Invalid input, please enter a positive integer." 156 | _yellow "输入无效,请输入一个正整数。" 157 | fi 158 | done 159 | while true; do 160 | _green "On which storage drive are the virtual machines opened? (Leave blank or enter 'local' if the virtual machine is to be opened on the system disk):" 161 | reading "虚拟机们开设在哪个存储盘上?(若虚拟机要开设在系统盘上,则留空或输入local):" storage 162 | if [ -z "$storage" ]; then 163 | storage="local" 164 | fi 165 | break 166 | done 167 | while true; do 168 | _green "How many hard disks are allocated per virtual machine? (If 5G hard drives are allocated per virtual machine, enter 5):" 169 | reading "每个虚拟机分配多少硬盘?(若每个虚拟机分配5G硬盘,则输入5):" disk_nums 170 | if [[ "$disk_nums" =~ ^[1-9][0-9]*$ ]]; then 171 | break 172 | else 173 | _yellow "Invalid input, please enter a positive integer." 174 | _yellow "输入无效,请输入一个正整数。" 175 | fi 176 | done 177 | if [ "$system_arch" = "x86" ] || [ "$system_arch" = "x86_64" ]; then 178 | while true; do 179 | sys_status="false" 180 | _green "What system does each virtual machine use? (Leave blank or enter debian11 if all use debian11):" 181 | reading "每个虚拟机都使用什么系统?(若都使用debian11,则留空或输入debian11):" system 182 | if [ -z "$system" ]; then 183 | system="debian11" 184 | fi 185 | systems=("debian10" "debian11" "debian9" "ubuntu18" "ubuntu20" "ubuntu22" "archlinux" "centos9-stream" "centos8-stream" "almalinux8" "almalinux9" "fedora33" "fedora34" "opensuse-leap-15") 186 | for sys in ${systems[@]}; do 187 | if [[ "$system" == "$sys" ]]; then 188 | sys_status="true" 189 | break 190 | fi 191 | done 192 | if [ "$sys_status" = "true" ]; then 193 | break 194 | else 195 | _yellow "This system is not supported, please check https://github.com/spiritLHLS/Images for the names of supported systems" 196 | _yellow "不支持该系统,请查看 https://github.com/spiritLHLS/Images 支持的系统名字" 197 | fi 198 | done 199 | else 200 | while true; do 201 | sys_status="false" 202 | _green "What system does each virtual machine use? (Leave blank or enter debian11 if all use debian11):" 203 | reading "每个虚拟机都使用什么系统?(若都使用ubuntu22,则留空或输入ubuntu22):" system 204 | if [ -z "$system" ]; then 205 | system="ubuntu22" 206 | fi 207 | systems=("ubuntu14" "ubuntu16" "ubuntu18" "ubuntu20" "ubuntu22") 208 | for sys in ${systems[@]}; do 209 | if [[ "$system" == "$sys" ]]; then 210 | sys_status="true" 211 | break 212 | fi 213 | done 214 | if [ "$sys_status" = "true" ]; then 215 | break 216 | else 217 | _yellow "Unable to install corresponding system, please check http://cloud-images.ubuntu.com for supported system images " 218 | _yellow "无法安装对应系统,请查看 http://cloud-images.ubuntu.com 支持的系统镜像 " 219 | fi 220 | done 221 | fi 222 | while true; do 223 | _green "Need to attach a separate IPV6 address to each virtual machine?([N]/y)" 224 | reading "是否附加独立的IPV6地址?([N]/y)" independent_ipv6 225 | independent_ipv6=$(echo "$independent_ipv6" | tr '[:upper:]' '[:lower:]') 226 | if [ "$independent_ipv6" = "y" ] || [ "$independent_ipv6" = "n" ]; then 227 | break 228 | else 229 | _yellow "Invalid input, please enter y or n." 230 | _yellow "输入无效,请输入Y或者N。" 231 | fi 232 | done 233 | for ((i = 1; i <= $new_nums; i++)); do 234 | vm_num=$(($vm_num + 1)) 235 | user=$(cat /dev/urandom | tr -dc 'a-zA-Z' | fold -w 4 | head -n 1) 236 | ori=$(date | md5sum) 237 | password=${ori:2:9} 238 | ssh_port=$(($web2_port + 1)) 239 | web1_port=$(($web2_port + 2)) 240 | web2_port=$(($web1_port + 1)) 241 | port_start=$(($port_end + 1)) 242 | port_end=$(($port_start + 25)) 243 | ./buildvm.sh $vm_num $user $password $cpu_nums $memory_nums $disk_nums $ssh_port $web1_port $web2_port $port_start $port_end $system $storage $independent_ipv6 244 | cat "vm$vm_num" >>vmlog 245 | rm -rf "vm$vm_num" 246 | if [ "$i" = "$new_nums" ]; then 247 | break 248 | fi 249 | sleep 30 250 | done 251 | } 252 | 253 | pre_check 254 | get_system_arch 255 | if [ -z "${system_arch}" ] || [ ! -v system_arch ]; then 256 | _red "This script can only run on machines under x86_64 or arm architecture." 257 | exit 1 258 | fi 259 | check_info 260 | build_new_vms 261 | check_info 262 | -------------------------------------------------------------------------------- /scripts/pve_delete.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2025.05.09 5 | # ./pve_delete.sh arg1 arg2 6 | # arg 可填入虚拟机/容器的序号,可以有任意多个 7 | # 日志 /var/log/pve_delete.log 8 | 9 | # 启用错误检查 10 | set -e 11 | set -u 12 | 13 | # 日志函数 14 | log_file="/var/log/pve_delete.log" 15 | log() { 16 | echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$log_file" 17 | } 18 | 19 | # 检查VM/CT状态函数 20 | check_vmct_status() { 21 | local id=$1 22 | local type=$2 23 | local max_attempts=5 24 | for ((i=1; i<=max_attempts; i++)); do 25 | if [ "$type" = "vm" ]; then 26 | # 检查VM是否已经停止 27 | if [ "$(qm status "$id" 2>/dev/null | grep -w "status:" | awk '{print $2}')" = "stopped" ]; then 28 | return 0 29 | fi 30 | elif [ "$type" = "ct" ]; then 31 | # 检查容器是否已经停止 32 | if [ "$(pct status "$id" 2>/dev/null | grep -w "status:" | awk '{print $2}')" = "stopped" ]; then 33 | return 0 34 | fi 35 | fi 36 | sleep 1 37 | done 38 | return 1 39 | } 40 | 41 | # 安全删除文件/路径 42 | safe_remove() { 43 | local path=$1 44 | if [ -e "$path" ]; then 45 | log "Removing: $path" 46 | rm -rf "$path" 47 | fi 48 | } 49 | 50 | # 清理VM相关文件 51 | cleanup_vm_files() { 52 | local vmid=$1 53 | log "Cleaning up files for VM $vmid" 54 | # 获取所有存储名称 55 | pvesm status | awk 'NR > 1 {print $1}' | while read -r storage; do 56 | # 遍历存储并列出相关的卷 57 | pvesm list "$storage" | awk -v vmid="$vmid" '$5 == vmid {print $1}' | while read -r volid; do 58 | vol_path=$(pvesm path "$volid" 2>/dev/null || true) 59 | if [ -n "$vol_path" ]; then 60 | safe_remove "$vol_path" 61 | else 62 | log "Warning: Failed to resolve path for volume $volid in storage $storage" 63 | fi 64 | done 65 | done 66 | rm -rf "/root/vm${vmid}" 67 | } 68 | 69 | # 清理CT相关文件 70 | cleanup_ct_files() { 71 | local ctid=$1 72 | log "Cleaning up files for CT $ctid" 73 | # 获取所有存储名称 74 | pvesm status | awk 'NR > 1 {print $1}' | while read -r storage; do 75 | # 遍历存储并列出相关的卷 76 | pvesm list "$storage" | awk -v ctid="$ctid" '$5 == ctid {print $1}' | while read -r volid; do 77 | vol_path=$(pvesm path "$volid" 2>/dev/null || true) 78 | if [ -n "$vol_path" ]; then 79 | safe_remove "$vol_path" 80 | else 81 | log "Warning: Failed to resolve path for volume $volid in storage $storage" 82 | fi 83 | done 84 | done 85 | rm -rf "/root/ct${ctid}" 86 | } 87 | 88 | # 处理VM删除 89 | handle_vm_deletion() { 90 | local vmid=$1 91 | local ip_address=$2 92 | log "Starting deletion process for VM $vmid (IP: $ip_address)" 93 | # 解锁VM 94 | log "Attempting to unlock VM $vmid" 95 | qm unlock "$vmid" 2>/dev/null || true 96 | # 停止VM 97 | log "Stopping VM $vmid" 98 | qm stop "$vmid" 2>/dev/null || true 99 | # 检查VM是否完全停止 100 | if ! check_vmct_status "$vmid" "vm"; then 101 | log "Warning: VM $vmid did not stop cleanly" 102 | return 1 103 | fi 104 | # 删除VM 105 | log "Destroying VM $vmid" 106 | qm destroy "$vmid" 107 | # 清理相关文件 108 | cleanup_vm_files "$vmid" 109 | # 更新iptables规则 110 | if [ -n "$ip_address" ]; then 111 | log "Removing iptables rules for IP $ip_address" 112 | sed -i "/$ip_address:/d" /etc/iptables/rules.v4 113 | fi 114 | } 115 | 116 | # 处理CT删除 117 | handle_ct_deletion() { 118 | local ctid=$1 119 | local ip_address=$2 120 | log "Starting deletion process for CT $ctid (IP: $ip_address)" 121 | # 停止容器 122 | log "Stopping CT $ctid" 123 | pct stop "$ctid" 2>/dev/null || true 124 | # 检查容器是否完全停止 125 | if ! check_vmct_status "$ctid" "ct"; then 126 | log "Warning: CT $ctid did not stop cleanly" 127 | return 1 128 | fi 129 | # 删除容器 130 | log "Destroying CT $ctid" 131 | pct destroy "$ctid" 132 | # 清理相关文件 133 | cleanup_ct_files "$ctid" 134 | # 更新iptables规则 135 | if [ -n "$ip_address" ]; then 136 | log "Removing iptables rules for IP $ip_address" 137 | sed -i "/$ip_address:/d" /etc/iptables/rules.v4 138 | fi 139 | } 140 | 141 | # 主函数 142 | main() { 143 | # 检查参数 144 | if [ $# -eq 0 ]; then 145 | echo "Usage: $0 [VMID/CTID...]" 146 | exit 1 147 | fi 148 | 149 | # 创建唯一ID数组 150 | declare -A unique_ids 151 | for arg in "$@"; do 152 | if [[ "$arg" =~ ^[0-9]+$ ]]; then 153 | unique_ids["$arg"]=1 154 | else 155 | log "Warning: Invalid ID format: $arg" 156 | fi 157 | done 158 | 159 | # 获取所有VM和CT的IP信息 160 | declare -A vmip_array 161 | declare -A ctip_array 162 | 163 | # 获取VM的IP 164 | vmids=$(qm list | awk '{if(NR>1)print $1}') 165 | if [ -n "$vmids" ]; then 166 | for vmid in $vmids; do 167 | ip_address=$(qm config "$vmid" | grep -oP 'ip=\K[0-9.]+' || true) 168 | if [ -n "$ip_address" ]; then 169 | vmip_array["$vmid"]=$ip_address 170 | fi 171 | done 172 | fi 173 | 174 | # 获取CT的IP 175 | ctids=$(pct list | awk '{if(NR>1)print $1}') 176 | if [ -n "$ctids" ]; then 177 | for ctid in $ctids; do 178 | ip_address=$(pct config "$ctid" | grep -oP 'ip=\K[0-9.]+' || true) 179 | if [ -n "$ip_address" ]; then 180 | ctip_array["$ctid"]=$ip_address 181 | fi 182 | done 183 | fi 184 | 185 | # 处理删除操作 186 | for id in "${!unique_ids[@]}"; do 187 | if [ -n "${vmip_array[$id]+x}" ]; then 188 | handle_vm_deletion "$id" "${vmip_array[$id]}" 189 | elif [ -n "${ctip_array[$id]+x}" ]; then 190 | handle_ct_deletion "$id" "${ctip_array[$id]}" 191 | else 192 | log "Warning: ID $id not found in existing VMs or CTs" 193 | fi 194 | done 195 | 196 | # 重建iptables规则 197 | log "Rebuilding iptables rules..." 198 | if [ -f "/etc/iptables/rules.v4" ]; then 199 | cat /etc/iptables/rules.v4 | iptables-restore 200 | else 201 | log "Warning: iptables rules file not found" 202 | fi 203 | 204 | # 重启ndpresponder服务(如果存在) 205 | if [ -f "/usr/local/bin/ndpresponder" ]; then 206 | log "Restarting ndpresponder service..." 207 | systemctl restart ndpresponder.service 208 | fi 209 | 210 | log "Operation completed successfully" 211 | echo "Finish." 212 | } 213 | 214 | # 检查是否为root用户 215 | if [ "$(id -u)" != "0" ]; then 216 | echo "This script must be run as root" 217 | exit 1 218 | fi 219 | 220 | # 运行主函数 221 | main "$@" 222 | -------------------------------------------------------------------------------- /scripts/ssh_bash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2024.06.05 5 | 6 | if [ -f "/etc/resolv.conf" ]; then 7 | cp /etc/resolv.conf /etc/resolv.conf.bak 8 | echo "nameserver 8.8.8.8" | tee -a /etc/resolv.conf >/dev/null 9 | echo "nameserver 8.8.4.4" | tee -a /etc/resolv.conf >/dev/null 10 | fi 11 | temp_file_apt_fix="/tmp/apt_fix.txt" 12 | REGEX=("debian|astra|devuan" "ubuntu" "centos|red hat|kernel|oracle linux|alma|rocky" "'amazon linux'" "fedora" "arch" "freebsd" "opensuse") 13 | RELEASE=("Debian" "Ubuntu" "CentOS" "CentOS" "Fedora" "Arch" "FreeBSD" "opensuse") 14 | PACKAGE_UPDATE=("! apt-get update && apt-get --fix-broken install -y && apt-get update" "apt-get update" "yum -y update" "yum -y update" "yum -y update" "pacman -Sy" "pkg update" "zypper update") 15 | PACKAGE_INSTALL=("apt-get -y install" "apt-get -y install" "yum -y install" "yum -y install" "yum -y install" "pacman -Sy --noconfirm --needed" "pkg install -y" "zypper install -y") 16 | PACKAGE_REMOVE=("apt-get -y remove" "apt-get -y remove" "yum -y remove" "yum -y remove" "yum -y remove" "pacman -Rsc --noconfirm" "pkg delete" "zypper remove") 17 | PACKAGE_UNINSTALL=("apt-get -y autoremove" "apt-get -y autoremove" "yum -y autoremove" "yum -y autoremove" "yum -y autoremove" "" "pkg autoremove" "zypper autoremove") 18 | CMD=("$(grep -i pretty_name /etc/os-release 2>/dev/null | cut -d \" -f2)" "$(hostnamectl 2>/dev/null | grep -i system | cut -d : -f2)" "$(lsb_release -sd 2>/dev/null)" "$(grep -i description /etc/lsb-release 2>/dev/null | cut -d \" -f2)" "$(grep . /etc/redhat-release 2>/dev/null)" "$(grep . /etc/issue 2>/dev/null | cut -d \\ -f1 | sed '/^[ ]*$/d')" "$(grep -i pretty_name /etc/os-release 2>/dev/null | cut -d \" -f2)" "$(uname -s)") 19 | SYS="${CMD[0]}" 20 | [[ -n $SYS ]] || exit 1 21 | for ((int = 0; int < ${#REGEX[@]}; int++)); do 22 | if [[ $(echo "$SYS" | tr '[:upper:]' '[:lower:]') =~ ${REGEX[int]} ]]; then 23 | SYSTEM="${RELEASE[int]}" 24 | [[ -n $SYSTEM ]] && break 25 | fi 26 | done 27 | [[ -z $SYSTEM ]] && exit 1 28 | [[ $EUID -ne 0 ]] && exit 1 29 | utf8_locale=$(locale -a 2>/dev/null | grep -i -m 1 -E "UTF-8|utf8") 30 | if [[ -z "$utf8_locale" ]]; then 31 | echo "No UTF-8 locale found" 32 | else 33 | export LC_ALL="$utf8_locale" 34 | export LANG="$utf8_locale" 35 | export LANGUAGE="$utf8_locale" 36 | echo "Locale set to $utf8_locale" 37 | fi 38 | 39 | check_china() { 40 | echo "IP area being detected ......" 41 | if [[ -z "${CN}" ]]; then 42 | if [[ $(curl -m 6 -s https://ipapi.co/json | grep 'China') != "" ]]; then 43 | echo "根据ipapi.co提供的信息,当前IP可能在中国" 44 | CN=true 45 | fi 46 | fi 47 | } 48 | 49 | change_debian_apt_sources() { 50 | cp /etc/apt/sources.list /etc/apt/sources.list.bak 51 | echo "backup the current /etc/apt/sources.list to /etc/apt/sources.list.bak" 52 | DEBIAN_VERSION=$(lsb_release -sr) 53 | if [[ -z "${CN}" || "${CN}" != true ]]; then 54 | URL="http://deb.debian.org/debian" 55 | else 56 | # Use mirrors.aliyun.com sources list if IP is in China 57 | URL="http://mirrors.aliyun.com/debian" 58 | fi 59 | 60 | case $DEBIAN_VERSION in 61 | 6*) DEBIAN_RELEASE="squeeze" ;; 62 | 7*) DEBIAN_RELEASE="wheezy" ;; 63 | 8*) DEBIAN_RELEASE="jessie" ;; 64 | 9*) DEBIAN_RELEASE="stretch" ;; 65 | 10*) DEBIAN_RELEASE="buster" ;; 66 | 11*) DEBIAN_RELEASE="bullseye" ;; 67 | 12*) DEBIAN_RELEASE="bookworm" ;; 68 | *) echo "The system is not Debian 6/7/8/9/10/11/12 . No changes were made to the apt-get sources." && return 1 ;; 69 | esac 70 | 71 | cat >/etc/apt/sources.list </dev/null 2>&1; then 83 | apt_update_output=$(apt-get update 2>&1) 84 | echo "$apt_update_output" >"$temp_file_apt_fix" 85 | if grep -q 'NO_PUBKEY' "$temp_file_apt_fix"; then 86 | public_keys=$(grep -oE 'NO_PUBKEY [0-9A-F]+' "$temp_file_apt_fix" | awk '{ print $2 }') 87 | joined_keys=$(echo "$public_keys" | paste -sd " ") 88 | echo "No Public Keys: ${joined_keys}" 89 | apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ${joined_keys} 90 | apt-get update 91 | if [ $? -eq 0 ]; then 92 | _green "Fixed" 93 | fi 94 | fi 95 | rm "$temp_file_apt_fix" 96 | else 97 | ${PACKAGE_UPDATE[int]} 98 | fi 99 | } 100 | 101 | install_required_modules() { 102 | modules=("sshpass" "openssh-server") 103 | for module in "${modules[@]}"; do 104 | if command -v apt-get >/dev/null 2>&1; then 105 | if dpkg -s $module >/dev/null 2>&1; then 106 | echo "$module has benn installed." 107 | else 108 | apt-get install -y $module 109 | if [ $? -ne 0 ]; then 110 | apt-get install -y $module --fix-missing 111 | fi 112 | echo "$module has been tried and installed!" 113 | fi 114 | else 115 | ${PACKAGE_INSTALL[int]} $module 116 | fi 117 | done 118 | if command -v apt-get >/dev/null 2>&1; then 119 | ${PACKAGE_INSTALL[int]} cron 120 | else 121 | ${PACKAGE_INSTALL[int]} cronie 122 | fi 123 | } 124 | 125 | remove_duplicate_lines() { 126 | awk '!NF || !x[$0]++' "$1" >"$1.tmp" && mv -f "$1.tmp" "$1" 127 | } 128 | 129 | check_china 130 | if [[ "${CN}" == true ]]; then 131 | if [[ "${SYSTEM}" == "Debian" ]]; then 132 | change_debian_apt_sources 133 | fi 134 | fi 135 | checkupdate 136 | install_required_modules 137 | if [ -f "/etc/motd" ]; then 138 | echo '' >/etc/motd 139 | echo 'Related repo https://github.com/oneclickvirt/pve' >>/etc/motd 140 | echo '--by https://t.me/spiritlhl' >>/etc/motd 141 | fi 142 | service iptables stop 2>/dev/null 143 | chkconfig iptables off 2>/dev/null 144 | if [ -f "/etc/sysconfig/selinux" ]; then 145 | sed -i.bak '/^SELINUX=/cSELINUX=disabled' /etc/sysconfig/selinux 146 | fi 147 | if [ -f "/etc/selinux/config" ]; then 148 | sed -i.bak '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config 149 | fi 150 | setenforce 0 151 | update_sshd_config() { 152 | local config_file="$1" 153 | if [ -f "$config_file" ]; then 154 | echo "updating $config_file" 155 | sudo sed -i "s/^#\?Port.*/Port 22/g" "$config_file" 156 | sudo sed -i "s/^#\?PermitRootLogin.*/PermitRootLogin yes/g" "$config_file" 157 | sudo sed -i "s/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g" "$config_file" 158 | sudo sed -i 's/#ListenAddress 0.0.0.0/ListenAddress 0.0.0.0/' "$config_file" 159 | sudo sed -i 's/#ListenAddress ::/ListenAddress ::/' "$config_file" 160 | sudo sed -i 's/#AddressFamily any/AddressFamily any/' "$config_file" 161 | sudo sed -i "s/^#\?PubkeyAuthentication.*/PubkeyAuthentication no/g" "$config_file" 162 | sudo sed -i '/^#UsePAM\|UsePAM/c #UsePAM no' "$config_file" 163 | sudo sed -i '/^AuthorizedKeysFile/s/^/#/' "$config_file" 164 | sudo sed -i 's/^#[[:space:]]*KbdInteractiveAuthentication.*\|^KbdInteractiveAuthentication.*/KbdInteractiveAuthentication yes/' "$config_file" 165 | fi 166 | } 167 | update_sshd_config "/etc/ssh/sshd_config" 168 | remove_duplicate_lines /etc/ssh/sshd_config 169 | if [ -d /etc/ssh/sshd_config.d ]; then 170 | for config_file in /etc/ssh/sshd_config.d/*; do 171 | if [ -f "$config_file" ]; then 172 | update_sshd_config "$config_file" 173 | remove_duplicate_lines "$config_file" 174 | fi 175 | done 176 | fi 177 | config_dir="/etc/ssh/sshd_config.d/" 178 | for file in "$config_dir"* 179 | do 180 | if [ -f "$file" ] && [ -r "$file" ]; then 181 | if grep -q "PasswordAuthentication no" "$file"; then 182 | sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' "$file" 183 | echo "File $file updated" 184 | fi 185 | fi 186 | done 187 | if command -v service >/dev/null 2>&1; then 188 | service ssh restart 189 | service sshd restart 190 | fi 191 | if command -v systemctl >/dev/null 2>&1; then 192 | systemctl restart sshd 193 | systemctl restart ssh 194 | fi 195 | sed -i 's/.*precedence ::ffff:0:0\/96.*/precedence ::ffff:0:0\/96 100/g' /etc/gai.conf 196 | rm -rf "$0" 197 | -------------------------------------------------------------------------------- /scripts/ssh_sh.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # from 3 | # https://github.com/oneclickvirt/pve 4 | # 2024.03.12 5 | 6 | if [ -f "/etc/resolv.conf" ]; then 7 | cp /etc/resolv.conf /etc/resolv.conf.bak 8 | echo "nameserver 8.8.8.8" | tee -a /etc/resolv.conf >/dev/null 9 | echo "nameserver 8.8.4.4" | tee -a /etc/resolv.conf >/dev/null 10 | fi 11 | config_dir="/etc/ssh/sshd_config.d/" 12 | for file in "$config_dir"* 13 | do 14 | if [ -f "$file" ] && [ -r "$file" ]; then 15 | if grep -q "PasswordAuthentication no" "$file"; then 16 | sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' "$file" 17 | echo "File $file updated" 18 | fi 19 | fi 20 | done 21 | if [ "$(cat /etc/os-release | grep -E '^ID=' | cut -d '=' -f 2 | tr -d '"')" == "alpine" ]; then 22 | apk update 23 | apk add --no-cache openssh-server 24 | apk add --no-cache sshpass 25 | apk add --no-cache openssh-keygen 26 | apk add --no-cache bash 27 | apk add --no-cache curl 28 | apk add --no-cache wget 29 | apk add --no-cache lsof 30 | cd /etc/ssh 31 | ssh-keygen -A 32 | chattr -i /etc/ssh/sshd_config 33 | sed -i '/^#PermitRootLogin\|PermitRootLogin/c PermitRootLogin yes' /etc/ssh/sshd_config 34 | sed -i "s/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g" /etc/ssh/sshd_config 35 | sed -i 's/#ListenAddress 0.0.0.0/ListenAddress 0.0.0.0/' /etc/ssh/sshd_config 36 | sed -i 's/#ListenAddress ::/ListenAddress ::/' /etc/ssh/sshd_config 37 | sed -i '/^#AddressFamily\|AddressFamily/c AddressFamily any' /etc/ssh/sshd_config 38 | sed -i "s/^#\?\(Port\).*/\1 22/" /etc/ssh/sshd_config 39 | sed -i -E 's/^#?(Port).*/\1 22/' /etc/ssh/sshd_config 40 | sed -i '/^#UsePAM\|UsePAM/c #UsePAM no' /etc/ssh/sshd_config 41 | sed -E -i 's/preserve_hostname:[[:space:]]*false/preserve_hostname: true/g' /etc/cloud/cloud.cfg 42 | sed -E -i 's/disable_root:[[:space:]]*true/disable_root: false/g' /etc/cloud/cloud.cfg 43 | sed -E -i 's/ssh_pwauth:[[:space:]]*false/ssh_pwauth: true/g' /etc/cloud/cloud.cfg 44 | /usr/sbin/sshd 45 | rc-update add sshd default 46 | chattr +i /etc/ssh/sshd_config 47 | elif [ "$(cat /etc/os-release | grep -E '^ID=' | cut -d '=' -f 2 | tr -d '"')" == "openwrt" ]; then 48 | opkg update 49 | opkg install openssh-server 50 | opkg install bash 51 | opkg install openssh-keygen 52 | opkg install shadow-chpasswd 53 | opkg install chattr 54 | opkg install cronie 55 | opkg install cron 56 | /etc/init.d/sshd enable 57 | /etc/init.d/sshd start 58 | cd /etc/ssh 59 | ssh-keygen -A 60 | chattr -i /etc/ssh/sshd_config 61 | sed -i "s/^#\?Port.*/Port 22/g" /etc/ssh/sshd_config 62 | sed -i "s/^#\?PermitRootLogin.*/PermitRootLogin yes/g" /etc/ssh/sshd_config 63 | sed -i "s/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g" /etc/ssh/sshd_config 64 | sed -i 's/#ListenAddress 0.0.0.0/ListenAddress 0.0.0.0/' /etc/ssh/sshd_config 65 | sed -i 's/#ListenAddress ::/ListenAddress ::/' /etc/ssh/sshd_config 66 | sed -i 's/#AddressFamily any/AddressFamily any/' /etc/ssh/sshd_config 67 | sed -i "s/^#\?PubkeyAuthentication.*/PubkeyAuthentication no/g" /etc/ssh/sshd_config 68 | sed -i '/^AuthorizedKeysFile/s/^/#/' /etc/ssh/sshd_config 69 | chattr +i /etc/ssh/sshd_config 70 | /etc/init.d/sshd restart 71 | elif [ "$(grep . /etc/issue 2>/dev/null | cut -d \\ -f1 | sed '/^[ ]*$/d')" =~ *"Arch"* ]; then 72 | curl -slk https://raw.githubusercontent.com/SuperManito/LinuxMirrors/main/ChangeMirrors.sh -o ChangeMirrors.sh 73 | chmod 777 ChangeMirrors.sh 74 | ./ChangeMirrors.sh --use-official-source --web-protocol http --intranet false --close-firewall true --backup true --updata-software false --clean-cache false --ignore-backup-tips > /dev/null 75 | rm -rf /etc/pacman.d/gnupg/ 76 | pacman-key --init 77 | pacman-key --populate archlinux 78 | pacman -Syyuu 79 | pacman -Sy --needed openssh 80 | pacman -Sy --needed bash 81 | pacman -Sy --needed chattr 82 | pacman -Sy --needed cronie 83 | pacman -Sy --needed cron 84 | systemctl enable sshd 85 | systemctl start sshd 86 | chattr -i /etc/ssh/sshd_config 87 | sed -i "s/^#\?Port.*/Port 22/g" /etc/ssh/sshd_config 88 | sed -i "s/^#\?PermitRootLogin.*/PermitRootLogin yes/g" /etc/ssh/sshd_config 89 | sed -i "s/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g" /etc/ssh/sshd_config 90 | sed -i 's/#ListenAddress 0.0.0.0/ListenAddress 0.0.0.0/' /etc/ssh/sshd_config 91 | sed -i 's/#ListenAddress ::/ListenAddress ::/' /etc/ssh/sshd_config 92 | sed -i 's/#AddressFamily any/AddressFamily any/' /etc/ssh/sshd_config 93 | sed -i "s/^#\?PubkeyAuthentication.*/PubkeyAuthentication no/g" /etc/ssh/sshd_config 94 | sed -i '/^AuthorizedKeysFile/s/^/#/' /etc/ssh/sshd_config 95 | chattr +i /etc/ssh/sshd_config 96 | systemctl restart sshd 97 | fi 98 | # gentoo 99 | /etc/init.d/cron enable || true 100 | /etc/init.d/cron start || true 101 | if [ -f "/etc/motd" ]; then 102 | echo '' >/etc/motd 103 | echo 'Related repo https://github.com/oneclickvirt/pve_lxc_images' >>/etc/motd 104 | echo '--by https://t.me/spiritlhl' >>/etc/motd 105 | fi 106 | if [ -f "/etc/banner" ]; then 107 | echo '' >/etc/banner 108 | echo 'Related repo https://github.com/oneclickvirt/pve_lxc_images' >>/etc/banner 109 | echo '--by https://t.me/spiritlhl' >>/etc/banner 110 | fi 111 | rm -f "$0" 112 | --------------------------------------------------------------------------------