-
Notifications
You must be signed in to change notification settings - Fork 312
/
sing-box.sh
3149 lines (2817 loc) · 156 KB
/
sing-box.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env bash
# 当前脚本版本号
VERSION='v1.2.9 (2024.12.20)'
# 各变量默认值
GH_PROXY='https://ghproxy.lvedong.eu.org/'
TEMP_DIR='/tmp/sing-box'
WORK_DIR='/etc/sing-box'
START_PORT_DEFAULT='8881'
MIN_PORT=100
MAX_PORT=65520
MIN_HOPPING_PORT=10000
MAX_HOPPING_PORT=65535
TLS_SERVER_DEFAULT=addons.mozilla.org
PROTOCOL_LIST=("XTLS + reality" "hysteria2" "tuic" "ShadowTLS" "shadowsocks" "trojan" "vmess + ws" "vless + ws + tls" "H2 + reality" "gRPC + reality")
NODE_TAG=("xtls-reality" "hysteria2" "tuic" "ShadowTLS" "shadowsocks" "trojan" "vmess-ws" "vless-ws-tls" "h2-reality" "grpc-reality")
CONSECUTIVE_PORTS=${#PROTOCOL_LIST[@]}
CDN_DOMAIN=("8cc.free.hr" "cm.yutian.us.kg" "fan.yutian.us.kg" "xn--b6gac.eu.org" "dash.cloudflare.com" "skk.moe" "visa.com")
SUBSCRIBE_TEMPLATE="https://raw.githubusercontent.com/fscarmen/client_template/main"
export DEBIAN_FRONTEND=noninteractive
trap "rm -rf $TEMP_DIR >/dev/null 2>&1 ; echo -e '\n' ;exit" INT QUIT TERM EXIT
mkdir -p $TEMP_DIR
E[0]="Language:\n 1. English (default) \n 2. 简体中文"
C[0]="${E[0]}"
E[1]="Refactored the chatGPT detection method based on lmc999's detection and unlocking script."
C[1]="根据 lmc999 的检测解锁脚本,重构了检测 chatGPT 方法"
E[2]="Downloading Sing-box. Please wait a seconds ..."
C[2]="下载 Sing-box 中,请稍等 ..."
E[3]="Input errors up to 5 times.The script is aborted."
C[3]="输入错误达5次,脚本退出"
E[4]="UUID should be 36 characters, please re-enter \(\${UUID_ERROR_TIME} times remaining\):"
C[4]="UUID 应为36位字符,请重新输入 \(剩余\${UUID_ERROR_TIME}次\):"
E[5]="The script supports Debian, Ubuntu, CentOS, Alpine, Fedora or Arch systems only. Feedback: [https://github.com/fscarmen/sing-box/issues]"
C[5]="本脚本只支持 Debian、Ubuntu、CentOS、Alpine、Fedora 或 Arch 系统,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
E[6]="Curren operating system is \$SYS.\\\n The system lower than \$SYSTEM \${MAJOR[int]} is not supported. Feedback: [https://github.com/fscarmen/sing-box/issues]"
C[6]="当前操作是 \$SYS\\\n 不支持 \$SYSTEM \${MAJOR[int]} 以下系统,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
E[7]="Install dependence-list:"
C[7]="安装依赖列表:"
E[8]="All dependencies already exist and do not need to be installed additionally."
C[8]="所有依赖已存在,不需要额外安装"
E[9]="To upgrade, press [y]. No upgrade by default:"
C[9]="升级请按 [y],默认不升级:"
E[10]="\(4/6\) Please enter VPS IP \(Default is: \${SERVER_IP_DEFAULT}\):"
C[10]="\(4/6\) 请输入 VPS IP \(默认为: \${SERVER_IP_DEFAULT}\):"
E[11]="\(2/6\) Please enter the starting port number. Must be \${MIN_PORT} - \${MAX_PORT}, consecutive \${NUM} free ports are required \(Default is: \${START_PORT_DEFAULT}\):"
C[11]="\(2/6\) 请输入开始的端口号,必须是 \${MIN_PORT} - \${MAX_PORT},需要连续\${NUM}个空闲的端口 \(默认为: \${START_PORT_DEFAULT}\):"
E[12]="\(5/6\) Please enter UUID \(Default is \${UUID_DEFAULT}\):"
C[12]="\(5/6\) 请输入 UUID \(默认为 \${UUID_DEFAULT}\):"
E[13]="\(6/6\) Please enter the node name. \(Default is \${NODE_NAME_DEFAULT}\):"
C[13]="\(6/6\) 请输入节点名称 \(默认为: \${NODE_NAME_DEFAULT}\):"
E[14]="Node name only allow uppercase and lowercase letters and numeric characters, please re-enter \(\${a} times remaining\):"
C[14]="节点名称只允许英文大小写及数字字符,请重新输入 \(剩余\${a}次\):"
E[15]="Sing-box script has not been installed yet."
C[15]="Sing-box 脚本还没有安装"
E[16]="Sing-box is completely uninstalled."
C[16]="Sing-box 已彻底卸载"
E[17]="Version"
C[17]="脚本版本"
E[18]="New features"
C[18]="功能新增"
E[19]="System infomation"
C[19]="系统信息"
E[20]="Operating System"
C[20]="当前操作系统"
E[21]="Kernel"
C[21]="内核"
E[22]="Architecture"
C[22]="处理器架构"
E[23]="Virtualization"
C[23]="虚拟化"
E[24]="Choose:"
C[24]="请选择:"
E[25]="Curren architecture \$(uname -m) is not supported. Feedback: [https://github.com/fscarmen/sing-box/issues]"
C[25]="当前架构 \$(uname -m) 暂不支持,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
E[26]="Not install"
C[26]="未安装"
E[27]="close"
C[27]="关闭"
E[28]="open"
C[28]="开启"
E[29]="View links (sb -n)"
C[29]="查看节点信息 (sb -n)"
E[30]="Change listen ports (sb -p)"
C[30]="更换监听端口 (sb -p)"
E[31]="Sync Sing-box to the latest version (sb -v)"
C[31]="同步 Sing-box 至最新版本 (sb -v)"
E[32]="Upgrade kernel, turn on BBR, change Linux system (sb -b)"
C[32]="升级内核、安装BBR、DD脚本 (sb -b)"
E[33]="Uninstall (sb -u)"
C[33]="卸载 (sb -u)"
E[34]="Install Sing-box"
C[34]="安装 Sing-box"
E[35]="Exit"
C[35]="退出"
E[36]="Please enter the correct number"
C[36]="请输入正确数字"
E[37]="successful"
C[37]="成功"
E[38]="failed"
C[38]="失败"
E[39]="Sing-box is not installed and cannot change the Argo tunnel."
C[39]="Sing-box 未安装,不能更换 Argo 隧道"
E[40]="Sing-box local verion: \$LOCAL\\\t The newest verion: \$ONLINE"
C[40]="Sing-box 本地版本: \$LOCAL\\\t 最新版本: \$ONLINE"
E[41]="No upgrade required."
C[41]="不需要升级"
E[42]="Downloading the latest version Sing-box failed, script exits. Feedback:[https://github.com/fscarmen/sing-box/issues]"
C[42]="下载最新版本 Sing-box 失败,脚本退出,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
E[43]="The script must be run as root, you can enter sudo -i and then download and run again. Feedback:[https://github.com/fscarmen/sing-box/issues]"
C[43]="必须以root方式运行脚本,可以输入 sudo -i 后重新下载运行,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
E[44]="Ports are in used: \${IN_USED[*]}"
C[44]="正在使用中的端口: \${IN_USED[*]}"
E[45]="Ports used: \${NOW_START_PORT} - \$((NOW_START_PORT+NOW_CONSECUTIVE_PORTS-1))"
C[45]="使用端口: \${NOW_START_PORT} - \$((NOW_START_PORT+NOW_CONSECUTIVE_PORTS-1))"
E[46]="Warp / warp-go was detected to be running. Please enter the correct server IP:"
C[46]="检测到 warp / warp-go 正在运行,请输入确认的服务器 IP:"
E[47]="No server ip, script exits. Feedback:[https://github.com/fscarmen/sing-box/issues]"
C[47]="没有 server ip,脚本退出,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
E[48]="ShadowTLS - Copy the above two Neko links and manually set up the chained proxies in order. Tutorial: https://github.com/fscarmen/sing-box/blob/main/README.md#sekobox-%E8%AE%BE%E7%BD%AE-shadowtls-%E6%96%B9%E6%B3%95"
C[48]="ShadowTLS - 复制上面两条 Neko links 进去,并按顺序手动设置链式代理,详细教程: https://github.com/fscarmen/sing-box/blob/main/README.md#sekobox-%E8%AE%BE%E7%BD%AE-shadowtls-%E6%96%B9%E6%B3%95"
E[49]="(1/6) Select more protocols to install (e.g. hgbd). The order of the port numbers of the protocols is related to the ordering of the multiple choices:\n a. all (default)"
C[49]="(1/6) 多选需要安装协议(比如 hgbd),协议的端口号次序与多选的排序有关:\n a. all (默认)"
E[50]="Please enter the \$TYPE domain name:"
C[50]="请输入 \$TYPE 域名:"
E[51]="Please choose or custom a cdn, http support is required:"
C[51]="请选择或输入 cdn,要求支持 http:"
E[52]="Please set the ip \[\${WS_SERVER_IP_SHOW}] to domain \[\${TYPE_HOST_DOMAIN}], and set the origin rule to \[\${TYPE_PORT_WS}] in Cloudflare."
C[52]="请在 Cloudflare 绑定 \[\${WS_SERVER_IP_SHOW}] 的域名为 \[\${TYPE_HOST_DOMAIN}], 并设置 origin rule 为 \[\${TYPE_PORT_WS}]"
E[53]="Please select or enter the preferred domain, the default is \${CDN_DOMAIN[0]}:"
C[53]="请选择或者填入优选域名,默认为 \${CDN_DOMAIN[0]}:"
E[54]="The contents of the ShadowTLS configuration file need to be updated for the sing_box kernel."
C[54]="ShadowTLS 配置文件内容,需要更新 sing_box 内核"
E[55]="The script runs today: \$TODAY. Total: \$TOTAL"
C[55]="脚本当天运行次数: \$TODAY,累计运行次数: \$TOTAL"
E[56]="Process ID"
C[56]="进程ID"
E[57]="Selecting the ws return method:\n 1. Argo (default)\n 2. Origin rules"
C[57]="选择 ws 的回源方式:\n 1. Argo (默认)\n 2. Origin rules"
E[58]="Memory Usage"
C[58]="内存占用"
E[59]="Install ArgoX scripts (argo + xray) [https://github.com/fscarmen/argox]"
C[59]="安装 ArgoX 脚本 (argo + xray) [https://github.com/fscarmen/argox]"
E[60]="The order of the selected protocols and ports is as follows:"
C[60]="选择的协议及端口次序如下:"
E[61]="There are no replaceable Argo tunnels."
C[61]="没有可更换的Argo 隧道"
E[62]="Add / Remove protocols (sb -r)"
C[62]="增加 / 删除协议 (sb -r)"
E[63]="(1/3) Installed protocols."
C[63]="(1/3) 已安装的协议"
E[64]="Please select the protocols to be removed (multiple selections possible. Press Enter to skip):"
C[64]="请选择需要删除的协议(可以多选,回车跳过):"
E[65]="(2/3) Uninstalled protocols."
C[65]="(2/3) 未安装的协议"
E[66]="Please select the protocols to be added (multiple choices possible. Press Enter to skip):"
C[66]="请选择需要增加的协议(可以多选,回车跳过):"
E[67]="(3/3) Confirm all protocols for reloading."
C[67]="(3/3) 确认重装的所有协议"
E[68]="Press [n] if there is an error, other keys to continue:"
C[68]="如有错误请按 [n],其他键继续:"
E[69]="Install sba scripts (argo + sing-box) [https://github.com/fscarmen/sba]"
C[69]="安装 sba 脚本 (argo + sing-box) [https://github.com/fscarmen/sba]"
E[70]="Please set inSecure in tls to true."
C[70]="请把 tls 里的 inSecure 设置为 true"
E[71]="Create shortcut [ sb ] successfully."
C[71]="创建快捷 [ sb ] 指令成功!"
E[72]="Path to each client configuration file: ${WORK_DIR}/subscribe/\n The full template can be found at:\n https://t.me/ztvps/100\n https://github.com/chika0801/sing-box-examples/tree/main/Tun"
C[72]="各客户端配置文件路径: ${WORK_DIR}/subscribe/\n 完整模板可参照:\n https://t.me/ztvps/100\n https://github.com/chika0801/sing-box-examples/tree/main/Tun"
E[73]="There is no protocol left, if you are sure please re-run [ sb -u ] to uninstall all."
C[73]="没有协议剩下,如确定请重新执行 [ sb -u ] 卸载所有"
E[74]="Keep protocols"
C[74]="保留协议"
E[75]="Add protocols"
C[75]="新增协议"
E[76]="Install TCP brutal"
C[76]="安装 TCP brutal"
E[77]="With sing-box installed, the script exits."
C[77]="已安装 sing-box ,脚本退出"
E[78]="Parameter [ $ERROR_PARAMETER ] error, script exits."
C[78]="[ $ERROR_PARAMETER ] 参数错误,脚本退出"
E[79]="\(3/6\) Please enter the port number of nginx. Must be \${MIN_PORT} - \${MAX_PORT} \(Default is: \${PORT_NGINX_DEFAULT}\):"
C[79]="\(3/6\) 请输入 nginx 端口号,必须是 \${MIN_PORT} - \${MAX_PORT} \(默认为: \${PORT_NGINX_DEFAULT}\):"
E[80]="subscribe"
C[80]="订阅"
E[81]="Adaptive Clash / V2rayN / NekoBox / ShadowRocket / SFI / SFA / SFM Clients"
C[81]="自适应 Clash / V2rayN / NekoBox / ShadowRocket / SFI / SFA / SFM 客户端"
E[82]="template"
C[82]="模版"
E[83]="To uninstall Nginx press [y], it is not uninstalled by default:"
C[83]="如要卸载 Nginx 请按 [y],默认不卸载:"
E[84]="Set SElinux: enforcing --> disabled"
C[84]="设置 SElinux: enforcing --> disabled"
E[85]="Please input Argo Token or Json ( User can easily obtain the json at https://fscarmen.cloudflare.now.cc ):"
C[85]="请输入 Argo Token 或者 Json ( 用户通过以下网站轻松获取 json: https://fscarmen.cloudflare.now.cc ):"
E[86]="Argo authentication message does not match the rules, neither Token nor Json, script exits. Feedback:[https://github.com/fscarmen/sba/issues]"
C[86]="Argo 认证信息不符合规则,既不是 Token,也是不是 Json,脚本退出,问题反馈:[https://github.com/fscarmen/sba/issues]"
E[87]="Please input the Argo domain (Default is temporary domain if left blank):"
C[87]="请输入 Argo 域名 (如果没有,可以跳过以使用 Argo 临时域名):"
E[88]="Please input the Argo domain (cannot be empty):"
C[88]="请输入 Argo 域名 (不能为空):"
E[89]="( Additional dependencies: nginx )"
C[89]="( 额外依赖: nginx )"
E[90]="Argo tunnel is: \$ARGO_TYPE\\\n The domain is: \$ARGO_DOMAIN"
C[90]="Argo 隧道类型为: \$ARGO_TYPE\\\n 域名是: \$ARGO_DOMAIN"
E[91]="Argo tunnel type:\n 1. Try\n 2. Token or Json"
C[91]="Argo 隧道类型:\n 1. Try\n 2. Token 或者 Json"
E[92]="Change the Argo tunnel (sb -t)"
C[92]="更换 Argo 隧道 (sb -t)"
E[93]="Can't get the temporary tunnel domain, script exits. Feedback:[https://github.com/fscarmen/sing-box/issues]"
C[93]="获取不到临时隧道的域名,脚本退出,问题反馈:[https://github.com/fscarmen/sing-box/issues]"
E[94]="Please bind \[\${ARGO_DOMAIN}] tunnel TYPE to HTTP and URL to \[\localhost:\${PORT_NGINX}] in Cloudflare."
C[94]="请在 Cloudflare 绑定 \[\${ARGO_DOMAIN}] 隧道 TYPE 为 HTTP,URL 为 \[\localhost:\${PORT_NGINX}]"
E[95]="netfilter-persistent installation failed, but the installation progress will not stop. portHopping forwarding rules are temporary rules, reboot may be invalidated."
C[95]="netfilter-persistent安装失败,但安装进度不会停止。PortHopping转发规则为临时规则,重启可能失效"
E[96]="netfilter-persistent is not started, PortHopping forwarding rules cannot be persisted. Reboot the system, the rules will be invalidated, please manually execute [netfilter-persistent save], continue the script does not affect the subsequent configuration."
C[96]="netfilter-persistent未启动,PortHopping转发规则无法持久化,重启系统,规则将会失效,请手动执行 [netfilter-persistent save],继续运行脚本不影响后续配置"
E[97]="Port Hopping/Multiple: Users sometimes report that their ISPs block or throttle persistent UDP connections. However, these restrictions often only apply to the specific port being used. Port hopping can be used as a workaround for this situation. This function needs to occupy multiple ports, please make sure that these ports are not listening to other services. \n Tip1: The number of ports should not be too many, the recommended number is about 1000, the minimum value: $MIN_HOPPING_PORT, the maximum value: $MAX_HOPPING_PORT.\n Tip2: nat machines have a limited number of ports to listen on, usually 20-30. If setting ports out of the nat range will cause the node to not work, please use with caution!\n This function is not used by default."
C[97]="端口跳跃/多端口(Port Hopping)介绍: 用户有时报告运营商会阻断或限速 UDP 连接。不过,这些限制往往仅限单个端口。端口跳跃可用作此情况的解决方法。该功能需要占用多个端口,请保证这些端口没有监听其他服务\n Tip1: 端口选择数量不宜过多,推荐1000个左右,最小值:$MIN_HOPPING_PORT,最大值: $MAX_HOPPING_PORT\n Tip2: nat 鸡由于可用于监听的端口有限,一般为20-30个。如设置了不开放的端口会导致节点不通,请慎用!\n 默认不使用该功能"
E[98]="Enter the port range, e.g. 50000:51000. Leave blank to disable:"
C[98]="请输入端口范围,例如 50000:51000,如要禁用请留空:"
E[99]="The \${SING_BOX_SCRIPT} is detected to be installed. Script exits."
C[99]="检测到已安装 \${SING_BOX_SCRIPT},脚本退出!"
# 自定义字体彩色,read 函数
warning() { echo -e "\033[31m\033[01m$*\033[0m"; } # 红色
error() { echo -e "\033[31m\033[01m$*\033[0m" && exit 1; } # 红色
info() { echo -e "\033[32m\033[01m$*\033[0m"; } # 绿色
hint() { echo -e "\033[33m\033[01m$*\033[0m"; } # 黄色
reading() { read -rp "$(info "$1")" "$2"; }
text() { grep -q '\$' <<< "${E[$*]}" && eval echo "\$(eval echo "\${${L}[$*]}")" || eval echo "\${${L}[$*]}"; }
# 检测是否需要启用 Github CDN,如能直接连通,则不使用
check_cdn() {
[ -n "$GH_PROXY" ] && wget --server-response --quiet --output-document=/dev/null --no-check-certificate --tries=2 --timeout=3 https://raw.githubusercontent.com/fscarmen/sing-box/main/README.md >/dev/null 2>&1 && unset GH_PROXY
}
# 检测是否解锁 chatGPT,以决定是否使用 warp 链式代理或者是 direct out,此处判断改编自 https://github.com/lmc999/RegionRestrictionCheck
check_chatgpt() {
local CHECK_STACK=-$1
local UA_BROWSER="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
local UA_SEC_CH_UA='"Google Chrome";v="125", "Chromium";v="125", "Not.A/Brand";v="24"'
wget --help | grep -q '\-\-ciphers' && local IS_CIPHERS=is_ciphers
# 首先检查API访问
local CHECK_RESULT1=$(wget --timeout=2 --tries=2 --retry-connrefused --waitretry=5 ${CHECK_STACK} -qO- --content-on-error --header='authority: api.openai.com' --header='accept: */*' --header='accept-language: en-US,en;q=0.9' --header='authorization: Bearer null' --header='content-type: application/json' --header='origin: https://platform.openai.com' --header='referer: https://platform.openai.com/' --header="sec-ch-ua: ${UA_SEC_CH_UA}" --header='sec-ch-ua-mobile: ?0' --header='sec-ch-ua-platform: "Windows"' --header='sec-fetch-dest: empty' --header='sec-fetch-mode: cors' --header='sec-fetch-site: same-site' --user-agent="${UA_BROWSER}" 'https://api.openai.com/compliance/cookie_requirements')
[ -z "$CHECK_RESULT1" ] && grep -qw is_ciphers <<< "$IS_CIPHERS" && local CHECK_RESULT1=$(wget --timeout=2 --tries=2 --retry-connrefused --waitretry=5 ${CHECK_STACK} --ciphers=DEFAULT@SECLEVEL=1 --no-check-certificate -qO- --content-on-error --header='authority: api.openai.com' --header='accept: */*' --header='accept-language: en-US,en;q=0.9' --header='authorization: Bearer null' --header='content-type: application/json' --header='origin: https://platform.openai.com' --header='referer: https://platform.openai.com/' --header="sec-ch-ua: ${UA_SEC_CH_UA}" --header='sec-ch-ua-mobile: ?0' --header='sec-ch-ua-platform: "Windows"' --header='sec-fetch-dest: empty' --header='sec-fetch-mode: cors' --header='sec-fetch-site: same-site' --user-agent="${UA_BROWSER}" 'https://api.openai.com/compliance/cookie_requirements')
# 如果API检测失败或者检测到unsupported_country,直接返回ban
if [ -z "$CHECK_RESULT1" ] || grep -qi 'unsupported_country' <<< "$CHECK_RESULT1"; then
echo "ban"
return
fi
# API检测通过后,继续检查网页访问
local CHECK_RESULT2=$(wget --timeout=2 --tries=2 --retry-connrefused --waitretry=5 ${CHECK_STACK} -qO- --content-on-error --header='authority: ios.chat.openai.com' --header='accept: */*;q=0.8,application/signed-exchange;v=b3;q=0.7' --header='accept-language: en-US,en;q=0.9' --header="sec-ch-ua: ${UA_SEC_CH_UA}" --header='sec-ch-ua-mobile: ?0' --header='sec-ch-ua-platform: "Windows"' --header='sec-fetch-dest: document' --header='sec-fetch-mode: navigate' --header='sec-fetch-site: none' --header='sec-fetch-user: ?1' --header='upgrade-insecure-requests: 1' --user-agent="${UA_BROWSER}" https://ios.chat.openai.com/)
[ -z "$CHECK_RESULT2" ] && grep -qw is_ciphers <<< "$IS_CIPHERS" && local CHECK_RESULT2=$(wget --timeout=2 --tries=2 --retry-connrefused --waitretry=5 ${CHECK_STACK} --ciphers=DEFAULT@SECLEVEL=1 --no-check-certificate -qO- --content-on-error --header='authority: ios.chat.openai.com' --header='accept: */*;q=0.8,application/signed-exchange;v=b3;q=0.7' --header='accept-language: en-US,en;q=0.9' --header="sec-ch-ua: ${UA_SEC_CH_UA}" --header='sec-ch-ua-mobile: ?0' --header='sec-ch-ua-platform: "Windows"' --header='sec-fetch-dest: document' --header='sec-fetch-mode: navigate' --header='sec-fetch-site: none' --header='sec-fetch-user: ?1' --header='upgrade-insecure-requests: 1' --user-agent="${UA_BROWSER}" https://ios.chat.openai.com/)
# 检查第二个结果
if [ -z "$CHECK_RESULT2" ] || grep -qi 'VPN' <<< "$CHECK_RESULT2"; then
echo "ban"
else
echo "unlock"
fi
}
# 脚本当天及累计运行次数统计
statistics_of_run-times() {
local COUNT=$(wget --no-check-certificate -qO- --tries=2 --timeout=2 "https://hit.forvps.gq/https://raw.githubusercontent.com/fscarmen/sing-box/main/sing-box.sh" 2>&1 | grep -m1 -oE "[0-9]+[ ]+/[ ]+[0-9]+") &&
TODAY=$(awk -F ' ' '{print $1}' <<< "$COUNT") &&
TOTAL=$(awk -F ' ' '{print $3}' <<< "$COUNT")
}
# 选择中英语言
select_language() {
if [ -z "$L" ]; then
if [ -s ${WORK_DIR}/language ]; then
L=$(cat ${WORK_DIR}/language)
else
L=E && hint "\n $(text 0) \n" && reading " $(text 24) " LANGUAGE
[ "$LANGUAGE" = 2 ] && L=C
fi
fi
}
# 字母与数字的 ASCII 码值转换
asc() {
if [[ "$1" = [a-z] ]]; then
[ "$2" = '++' ] && printf "\\$(printf '%03o' "$[ $(printf "%d" "'$1'") + 1 ]")" || printf "%d" "'$1'"
else
[[ "$1" =~ ^[0-9]+$ ]] && printf "\\$(printf '%03o' "$1")"
fi
}
# 收录一些热心网友和官网的 cdn
input_cdn() {
echo ""
for c in "${!CDN_DOMAIN[@]}"; do
hint " $[c+1]. ${CDN_DOMAIN[c]} "
done
reading "\n $(text 53) " CUSTOM_CDN
case "$CUSTOM_CDN" in
[1-${#CDN_DOMAIN[@]}] )
CDN="${CDN_DOMAIN[$((CUSTOM_CDN-1))]}"
;;
?????* )
CDN="$CUSTOM_CDN"
;;
* )
CDN="${CDN_DOMAIN[0]}"
esac
}
# 输入 Nginx 服务端口
input_nginx_port() {
local NUM=$1
local PORT_ERROR_TIME=6
# 生成 1000 - 65535 随机默认端口数
local PORT_NGINX_DEFAULT=$(shuf -i ${MIN_PORT}-${MAX_PORT} -n 1)
while true; do
[[ "$PORT_ERROR_TIME" > 1 && "$PORT_ERROR_TIME" < 6 ]] && unset IN_USED PORT_NGINX
(( PORT_ERROR_TIME-- )) || true
if [ "$PORT_ERROR_TIME" = 0 ]; then
error "\n $(text 3) \n"
else
[ -z "$PORT_NGINX" ] && reading "\n $(text 79) " PORT_NGINX
fi
PORT_NGINX=${PORT_NGINX:-"$PORT_NGINX_DEFAULT"}
if [[ "$PORT_NGINX" =~ ^[1-9][0-9]{1,4}$ && "$PORT_NGINX" -ge "$MIN_PORT" && "$PORT_NGINX" -le "$MAX_PORT" ]]; then
ss -nltup | grep -q ":$PORT_NGINX" && warning "\n $(text 44) \n" || break
fi
done
}
# 输入 hysteria2 跳跃端口
input_hopping_port() {
local HOPPING_ERROR_TIME=6
until [ -n "$IS_HOPPING" ]; do
(( HOPPING_ERROR_TIME-- )) || true
case "$HOPPING_ERROR_TIME" in
0 )
error "\n $(text 3) \n"
;;
5 )
hint "\n $(text 97) \n" && reading " $(text 98) " PORT_HOPPING_RANGE
;;
* )
reading " $(text 98) " PORT_HOPPING_RANGE
esac
if [[ "${PORT_HOPPING_RANGE//-/:}" =~ ^[1-6][0-9]{4}:[1-6][0-9]{4}$ ]]; then
# 为防止输入错误,把 - 改为 : ,比如 10000-11000 改为 10000:11000
PORT_HOPPING_RANGE=${PORT_HOPPING_RANGE//-/:}
PORT_HOPPING_START=${PORT_HOPPING_RANGE%:*}
PORT_HOPPING_END=${PORT_HOPPING_RANGE#*:}
[[ "$PORT_HOPPING_START" < "$PORT_HOPPING_END" && "$PORT_HOPPING_START" -ge "$MIN_HOPPING_PORT" && "$PORT_HOPPING_END" -le "$MAX_HOPPING_PORT" ]] && IS_HOPPING=is_hopping || warning "\n $(text 36) "
elif [[ -z "$PORT_HOPPING_RANGE" || "${PORT_HOPPING_RANGE,,}" =~ ^(n|no)$ ]]; then
IS_HOPPING=no_hoppinng
else
warning "\n $(text 36) "
fi
done
}
# 输入 Argo 域名和认证信息
input_argo_auth() {
local IS_CHANGE_ARGO=$1
[ -n "$IS_CHANGE_ARGO" ] && local EMPTY_ERROR_TIME=5
local DOMAIN_ERROR_TIME=6
# 处理可能输入的错误,去掉开头和结尾的空格,去掉最后的 :
if [ "$IS_CHANGE_ARGO" = 'is_change_argo' ]; then
until [ -n "$ARGO_DOMAIN" ]; do
(( EMPTY_ERROR_TIME-- )) || true
[ "$EMPTY_ERROR_TIME" = 0 ] && error "\n $(text 3) \n"
reading "\n $(text 88) " ARGO_DOMAIN
[ -n "$IS_CHANGE_ARGO" ] && ARGO_DOMAIN=$(sed 's/[ ]*//g; s/:[ ]*//' <<< "$ARGO_DOMAIN")
done
elif [ "$NONINTERACTIVE_INSTALL" != 'noninteractive_install' ]; then
[ -z "$ARGO_DOMAIN" ] && reading "\n $(text 87) " ARGO_DOMAIN
ARGO_DOMAIN=$(sed 's/[ ]*//g; s/:[ ]*//' <<< "$ARGO_DOMAIN")
fi
if [[ -z "$ARGO_DOMAIN" && ( "$ARGO_DOMAIN" =~ trycloudflare\.com$ || "$IS_CHANGE_ARGO" = 'is_add_protocols' || "$IS_CHANGE_ARGO" = 'is_install' || "$NONINTERACTIVE_INSTALL" = 'noninteractive_install' ) ]]; then
ARGO_RUNS="${WORK_DIR}/cloudflared tunnel --edge-ip-version auto --no-autoupdate --url http://localhost:$PORT_NGINX"
elif [ -n "${ARGO_DOMAIN}" ]; then
if [ -z "${ARGO_AUTH}" ]; then
until [[ "$ARGO_AUTH" =~ TunnelSecret || "$ARGO_AUTH" =~ ^[A-Z0-9a-z=]{120,250}$ || "$ARGO_AUTH" =~ .*cloudflared.*service[[:space:]]+install[[:space:]]+[A-Z0-9a-z=]{1,100} ]]; do
[ "$DOMAIN_ERROR_TIME" != 6 ] && warning "\n $(text 86) \n"
(( DOMAIN_ERROR_TIME-- )) || true
[ "$DOMAIN_ERROR_TIME" != 0 ] && reading "\n $(text 85) " ARGO_AUTH || error "\n $(text 3) \n"
done
fi
# 根据 ARGO_AUTH 的内容,自行判断是 Json 还是 Token
if [[ "$ARGO_AUTH" =~ TunnelSecret ]]; then
ARGO_TYPE=is_json_argo
ARGO_JSON=${ARGO_AUTH//[ ]/}
[ "$IS_CHANGE_ARGO" = 'is_install' ] && export_argo_json_file $TEMP_DIR || export_argo_json_file ${WORK_DIR}
ARGO_RUNS="${WORK_DIR}/cloudflared tunnel --edge-ip-version auto --config ${WORK_DIR}/tunnel.yml run"
elif [[ "$ARGO_AUTH" =~ ^[A-Z0-9a-z=]{120,250}$ ]]; then
ARGO_TYPE=is_token_argo
ARGO_TOKEN=$ARGO_AUTH
ARGO_RUNS="${WORK_DIR}/cloudflared tunnel --edge-ip-version auto run --token ${ARGO_TOKEN}"
elif [[ "$ARGO_AUTH" =~ .*cloudflared.*service[[:space:]]+install[[:space:]]+[A-Z0-9a-z=]{1,100} ]]; then
ARGO_TYPE=is_token_argo
ARGO_TOKEN=$(awk -F ' ' '{print $NF}' <<< "$ARGO_AUTH")
ARGO_RUNS="${WORK_DIR}/cloudflared tunnel --edge-ip-version auto run --token ${ARGO_TOKEN}"
fi
fi
}
# 更换 Argo 隧道类型
change_argo() {
check_install
if [ "${STATUS[0]}" = "$(text 26)" ]; then
error "\n $(text 39) "
elif [ "${STATUS[1]}" = "$(text 26)" ]; then
error "\n $(text 61) "
fi
case $(grep "ExecStart=" /etc/systemd/system/argo.service) in
*--config* )
ARGO_TYPE='Json'
;;
*--token* )
ARGO_TYPE='Token'
;;
* )
ARGO_TYPE='Try'
cmd_systemctl enable argo && sleep 2 && [ "$(systemctl is-active argo)" = 'active' ] && fetch_quicktunnel_domain
esac
fetch_nodes_value
hint "\n $(text 90) \n"
unset ARGO_DOMAIN
hint " $(text 91) \n" && reading " $(text 24) " CHANGE_TO
case "$CHANGE_TO" in
1 )
cmd_systemctl disable argo
[ -s ${WORK_DIR}/tunnel.json ] && rm -f ${WORK_DIR}/tunnel.{json,yml}
sed -i "s@ExecStart=.*@ExecStart=${WORK_DIR}/cloudflared tunnel --edge-ip-version auto --no-autoupdate --url http://localhost:$PORT_NGINX@g" /etc/systemd/system/argo.service
;;
2 )
[ -s ${WORK_DIR}/tunnel.json ] && rm -f ${WORK_DIR}/tunnel.{json,yml}
input_argo_auth is_change_argo
cmd_systemctl disable argo
if [ -n "$ARGO_TOKEN" ]; then
sed -i "s@ExecStart=.*@ExecStart=${WORK_DIR}/cloudflared tunnel --edge-ip-version auto run --token ${ARGO_TOKEN}@g" /etc/systemd/system/argo.service
elif [ -n "$ARGO_JSON" ]; then
sed -i "s@ExecStart=.*@ExecStart=${WORK_DIR}/cloudflared tunnel --edge-ip-version auto --config ${WORK_DIR}/tunnel.yml run@g" /etc/systemd/system/argo.service
fi
[ -s ${WORK_DIR}/conf/17_${NODE_TAG[6]}_inbounds.json ] && sed -i "s/VMESS_HOST_DOMAIN.*/VMESS_HOST_DOMAIN\": \"$ARGO_DOMAIN\"/" ${WORK_DIR}/conf/17_${NODE_TAG[6]}_inbounds.json
[ -s ${WORK_DIR}/conf/18_${NODE_TAG[7]}_inbounds.json ] && sed -i "s/\"server_name\":.*/\"server_name\": \"$ARGO_DOMAIN\",/" ${WORK_DIR}/conf/18_${NODE_TAG[7]}_inbounds.json
;;
* )
exit 0
esac
cmd_systemctl enable argo
fetch_nodes_value
export_nginx_conf_file
export_list
}
check_root() {
[ "$(id -u)" != 0 ] && error "\n $(text 43) \n"
}
# 判断处理器架构
check_arch() {
case "$(uname -m)" in
aarch64|arm64 )
SING_BOX_ARCH=arm64; JQ_ARCH=arm64; QRENCODE_ARCH=arm64; ARGO_ARCH=arm64
;;
x86_64|amd64 )
SING_BOX_ARCH=amd64; JQ_ARCH=amd64; QRENCODE_ARCH=amd64; ARGO_ARCH=amd64
;;
armv7l )
SING_BOX_ARCH=armv7; JQ_ARCH=armhf; QRENCODE_ARCH=arm; ARGO_ARCH=amd64
;;
* )
error " $(text 25) "
esac
}
# 检查系统是否已经安装 tcp-brutal
check_brutal() {
IS_BRUTAL=false && [ -x "$(type -p lsmod)" ] && lsmod | grep -q brutal && IS_BRUTAL=true
[ "$IS_BRUTAL" = 'false' ] && [ -x "$(type -p modprobe)" ] && modprobe brutal 2>/dev/null && IS_BRUTAL=true
}
# 查安装及运行状态,下标0: sing-box,下标1: argo,下标2:docker;状态码: 26 未安装, 27 已安装未运行, 28 运行中
check_install() {
[[ "$IS_SUB" = 'is_sub' || -s ${WORK_DIR}/subscribe/qr ]] && IS_SUB=is_sub || IS_SUB=no_sub
if ls ${WORK_DIR}/conf/*${NODE_TAG[1]}_inbounds.json >/dev/null 2>&1; then
check_port_hopping_nat
[ -n "$PORT_HOPPING_END" ] && IS_HOPPING=is_hopping || IS_HOPPING=no_hopping
fi
# 检测是否安装其他 sing-box systemd 状态,和是否其他一键脚本
if [ -s /etc/systemd/system/sing-box.service ]; then
SYSTEMD_EXECSTART=$(grep '^ExecStart=' /etc/systemd/system/sing-box.service)
case "$SYSTEMD_EXECSTART" in
'ExecStart=/etc/sing-box/sing-box run -C /etc/sing-box/conf/' )
[ "$(systemctl is-active sing-box)" = 'active' ] && STATUS[0]=$(text 28) || STATUS[0]=$(text 27)
;;
'ExecStart=/etc/v2ray-agent/sing-box/sing-box run -c /etc/v2ray-agent/sing-box/conf/config.json' )
SING_BOX_SCRIPT='mack-a/v2ray-agent' && error "\n $(text 99) \n"
;;
'ExecStart=/etc/s-box/sing-box run -c /etc/s-box/sb.json' )
SING_BOX_SCRIPT='yonggekkk/sing-box_hysteria2_tuic_argo_reality' && error "\n $(text 99) \n"
;;
'ExecStart=/usr/local/s-ui/bin/runSingbox.sh' )
SING_BOX_SCRIPT='alireza0/s-ui' && error "\n $(text 99) \n"
;;
'ExecStart=/usr/local/bin/sing-box run -c /usr/local/etc/sing-box/config.json' )
SING_BOX_SCRIPT='FranzKafkaYu/sing-box-yes' && error "\n $(text 99) \n"
;;
* )
SING_BOX_SCRIPT='Unknown or customized sing-box' && error "\n $(text 99) \n"
esac
elif [ -s /lib/systemd/system/sing-box.service ]; then
SYSTEMD_EXECSTART=$(grep '^ExecStart=' /lib/systemd/system/sing-box.service)
case "$SYSTEMD_EXECSTART" in
'ExecStart=/etc/sing-box/bin/sing-box run -c /etc/sing-box/config.json -C /etc/sing-box/conf' )
SING_BOX_SCRIPT='233boy/sing-box' && error "\n $(text 99) \n"
;;
* )
SING_BOX_SCRIPT='Unknown or customized sing-box' && error "\n $(text 99) \n"
esac
else
STATUS[0]=$(text 26)
fi
if [ "${STATUS[0]}" = "$(text 26)" ] && [ ! -s ${WORK_DIR}/sing-box ]; then
{
local VERSION_LATEST=$(wget --no-check-certificate --tries=2 --timeout=3 -qO- ${GH_PROXY}https://api.github.com/repos/SagerNet/sing-box/releases | awk -F '["v-]' '/tag_name/{print $5}' | sort -Vr | sed -n '1p')
local ONLINE=$(wget --no-check-certificate --tries=2 --timeout=3 -qO- ${GH_PROXY}https://api.github.com/repos/SagerNet/sing-box/releases | awk -F '["v]' -v var="tag_name.*$VERSION_LATEST" '$0 ~ var {print $5; exit}')
ONLINE=${ONLINE:-'1.11.0-beta.11'}
wget --no-check-certificate --continue ${GH_PROXY}https://github.com/SagerNet/sing-box/releases/download/v$ONLINE/sing-box-$ONLINE-linux-$SING_BOX_ARCH.tar.gz -qO- | tar xz -C $TEMP_DIR sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box >/dev/null 2>&1
[ -s $TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box ] && mv $TEMP_DIR/sing-box-$ONLINE-linux-$SING_BOX_ARCH/sing-box $TEMP_DIR
wget --no-check-certificate --continue -qO $TEMP_DIR/jq ${GH_PROXY}https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-$JQ_ARCH >/dev/null 2>&1 && chmod +x $TEMP_DIR/jq >/dev/null 2>&1
wget --no-check-certificate --continue -qO $TEMP_DIR/qrencode ${GH_PROXY}https://github.com/fscarmen/client_template/raw/main/qrencode-go/qrencode-go-linux-$QRENCODE_ARCH >/dev/null 2>&1 && chmod +x $TEMP_DIR/qrencode >/dev/null 2>&1
}&
fi
if [ "$NONINTERACTIVE_INSTALL" != 'noninteractive_install' ]; then
STATUS[1]=$(text 26) && IS_ARGO=no_argo && [ -s /etc/systemd/system/argo.service ] && IS_ARGO=is_argo && STATUS[1]=$(text 27) && [ "$(systemctl is-active argo)" = 'active' ] && STATUS[1]=$(text 28)
fi
if [ -s /etc/systemd/system/argo.service ]; then
local ARGO_CONTENT=$(grep '^ExecStart' /etc/systemd/system/argo.service)
if grep -q '\--token' <<< "$ARGO_CONTENT"; then
ARGO_TYPE=is_token_argo
elif grep -q '\--config' <<< "$ARGO_CONTENT"; then
ARGO_TYPE=is_json_argo
elif grep -q '\--url' <<< "$ARGO_CONTENT"; then
ARGO_TYPE=is_quicktunnel_argo
fi
fi
[[ "${STATUS[1]}" = "$(text 26)" || "$NONINTERACTIVE_INSTALL" = 'noninteractive_install' ]] && [ ! -s ${WORK_DIR}/cloudflared ] && { wget --no-check-certificate -qO $TEMP_DIR/cloudflared ${GH_PROXY}https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-$ARGO_ARCH >/dev/null 2>&1 && chmod +x $TEMP_DIR/cloudflared >/dev/null 2>&1; }&
}
# 检测 sing-box 的状态
check_sing-box_status() {
case "${STATUS[0]}" in
"$(text 26)" )
error "\n Sing-box $(text 28) $(text 38) \n"
;;
"$(text 27)" )
cmd_systemctl enable sing-box
[ "$(systemctl is-active sing-box)" = 'active' ] && info "\n Sing-box $(text 28) $(text 37) \n" || error "\n Sing-box $(text 28) $(text 38) \n"
;;
"$(text 28)" )
info "\n Sing-box $(text 28) $(text 37) \n"
esac
}
# 检测 Argo 的状态
check_argo_status() {
case "${STATUS[1]}" in
"$(text 26)" )
error "\n Argo $(text 28) $(text 38) \n"
;;
"$(text 27)" )
cmd_systemctl enable argo
[ "$(systemctl is-active argo)" = 'active' ] && info "\n Argo $(text 28) $(text 37) \n" || error "\n Argo $(text 28) $(text 38) \n"
;;
"$(text 28)" )
info "\n Argo $(text 28) $(text 37) \n"
esac
}
# 为了适配 alpine,定义 cmd_systemctl 的函数
cmd_systemctl() {
local ENABLE_DISABLE=$1
local APP=$2
if [ "$ENABLE_DISABLE" = 'enable' ]; then
if [ "$SYSTEM" = 'Alpine' ]; then
systemctl start $APP
cat > /etc/local.d/$APP.start << EOF
#!/usr/bin/env bash
systemctl start $APP
EOF
chmod +x /etc/local.d/$APP.start
rc-update add local >/dev/null 2>&1
elif [ "$IS_CENTOS" = 'CentOS7' ]; then
systemctl enable --now $APP
[ "$APP" = 'sing-box' ] && [[ "${IS_SUB}" = 'is_sub' || "${IS_ARGO}" = 'is_argo' ]] && $(type -p nginx) -c ${WORK_DIR}/nginx.conf
else
systemctl enable --now $APP
fi
elif [ "$ENABLE_DISABLE" = 'disable' ]; then
if [ "$SYSTEM" = 'Alpine' ]; then
systemctl stop $APP
[ "$APP" = 'sing-box' ] && [[ "${IS_SUB}" = 'is_sub' || "${IS_ARGO}" = 'is_argo' ]] && [ -s ${WORK_DIR}/nginx.conf ] && ss -nltp | grep $(awk '/listen/{print $2; exit}' ${WORK_DIR}/nginx.conf) | tr ',' '\n' | awk -F '=' '/pid/{print $2}' | sort -u | xargs kill -15 >/dev/null 2>&1
rm -f /etc/local.d/$APP.start
elif [ "$IS_CENTOS" = 'CentOS7' ]; then
systemctl disable --now $APP
[ "$APP" = 'sing-box' ] && [[ "${IS_SUB}" = 'is_sub' || "${IS_ARGO}" = 'is_argo' ]] && [ -s ${WORK_DIR}/nginx.conf ] && ss -nltp | grep $(awk '/listen/{print $2; exit}' ${WORK_DIR}/nginx.conf) | tr ',' '\n' | awk -F '=' '/pid/{print $2}' | sort -u | xargs kill -15 >/dev/null 2>&1
else
systemctl disable --now $APP
fi
fi
}
check_system_info() {
# 判断虚拟化
if [ -x "$(type -p systemd-detect-virt)" ]; then
VIRT=$(systemd-detect-virt)
elif [ -x "$(type -p hostnamectl)" ]; then
VIRT=$(hostnamectl | awk '/Virtualization/{print $NF}')
elif [ -x "$(type -p virt-what)" ]; then
VIRT=$(virt-what)
fi
[ -s /etc/os-release ] && SYS="$(awk -F '"' 'tolower($0) ~ /pretty_name/{print $2}' /etc/os-release)"
[[ -z "$SYS" && -x "$(type -p hostnamectl)" ]] && SYS="$(hostnamectl | awk -F ': ' 'tolower($0) ~ /operating system/{print $2}')"
[[ -z "$SYS" && -x "$(type -p lsb_release)" ]] && SYS="$(lsb_release -sd)"
[[ -z "$SYS" && -s /etc/lsb-release ]] && SYS="$(awk -F '"' 'tolower($0) ~ /distrib_description/{print $2}' /etc/lsb-release)"
[[ -z "$SYS" && -s /etc/redhat-release ]] && SYS="$(cat /etc/redhat-release)"
[[ -z "$SYS" && -s /etc/issue ]] && SYS="$(sed -E '/^$|^\\/d' /etc/issue | awk -F '\\' '{print $1}' | sed 's/[ ]*$//g')"
REGEX=("debian" "ubuntu" "centos|red hat|kernel|alma|rocky" "arch linux" "alpine" "fedora")
RELEASE=("Debian" "Ubuntu" "CentOS" "Arch" "Alpine" "Fedora")
EXCLUDE=("")
MAJOR=("9" "16" "7" "3" "" "37")
PACKAGE_UPDATE=("apt -y update" "apt -y update" "yum -y update --skip-broken" "pacman -Sy" "apk update -f" "dnf -y update")
PACKAGE_INSTALL=("apt -y install" "apt -y install" "yum -y install" "pacman -S --noconfirm" "apk add --no-cache" "dnf -y install")
PACKAGE_UNINSTALL=("apt -y autoremove" "apt -y autoremove" "yum -y autoremove" "pacman -Rcnsu --noconfirm" "apk del -f" "dnf -y autoremove")
for int in "${!REGEX[@]}"; do
[[ "${SYS,,}" =~ ${REGEX[int]} ]] && SYSTEM="${RELEASE[int]}" && break
done
# 针对各厂商的订制系统
if [ -z "$SYSTEM" ]; then
[ -x "$(type -p yum)" ] && int=2 && SYSTEM='CentOS' || error " $(text 5) "
fi
# 先排除 EXCLUDE 里包括的特定系统,其他系统需要作大发行版本的比较
for ex in "${EXCLUDE[@]}"; do [[ ! "{$SYS,,}" =~ $ex ]]; done &&
[[ "$(echo "$SYS" | sed "s/[^0-9.]//g" | cut -d. -f1)" -lt "${MAJOR[int]}" ]] && error " $(text 6) "
# 针对部分系统作特殊处理
[ "$SYSTEM" = 'CentOS' ] && IS_CENTOS="CentOS$(echo "$SYS" | sed "s/[^0-9.]//g" | cut -d. -f1)"
}
# 删除端口跳跃
del_port_hopping_nat(){
check_port_hopping_nat
if [ "$SYSTEM" = 'Alpine' ]; then
iptables --table nat -D PREROUTING -p udp --dport ${PORT_HOPPING_START}:${PORT_HOPPING_END} -m comment --comment "NAT ${PORT_HOPPING_START}:${PORT_HOPPING_END} to ${PORT_HOPPING_TARGET} (Sing-box Family Bucket)" -j DNAT --to-destination :${PORT_HOPPING_TARGET} 2>/dev/null
ip6tables --table nat -D PREROUTING -p udp --dport ${PORT_HOPPING_START}:${PORT_HOPPING_END} -m comment --comment "NAT ${PORT_HOPPING_START}:${PORT_HOPPING_END} to ${PORT_HOPPING_TARGET} (Sing-box Family Bucket)" -j DNAT --to-destination :${PORT_HOPPING_TARGET} 2>/dev/null
elif [ "$(systemctl is-active firewalld)" = 'active' ]; then
firewall-cmd --permanent --remove-forward-port=port=${PORT_HOPPING_START}-${PORT_HOPPING_END}:proto=udp:toport=${PORT_HOPPING_TARGET} >/dev/null 2>&1
firewall-cmd --reload >/dev/null 2>&1
else
iptables --table nat -D PREROUTING -p udp --dport ${PORT_HOPPING_START}:${PORT_HOPPING_END} -m comment --comment "NAT ${PORT_HOPPING_START}:${PORT_HOPPING_END} to ${PORT_HOPPING_TARGET} (Sing-box Family Bucket)" -j DNAT --to-destination :${PORT_HOPPING_TARGET} 2>/dev/null
ip6tables --table nat -D PREROUTING -p udp --dport ${PORT_HOPPING_START}:${PORT_HOPPING_END} -m comment --comment "NAT ${PORT_HOPPING_START}:${PORT_HOPPING_END} to ${PORT_HOPPING_TARGET} (Sing-box Family Bucket)" -j DNAT --to-destination :${PORT_HOPPING_TARGET} 2>/dev/null
[ "$(systemctl is-active netfilter-persistent)" = 'active' ] && netfilter-persistent save 2>/dev/null
fi
}
# 添加端口跳跃
add_port_hopping_nat() {
local PORT_HOPPING_START=$1
local PORT_HOPPING_END=$2
local PORT_HOPPING_TARGET=$3
# 检测防火墙依赖和状态
if [ "$SYSTEM" = 'Alpine' ]; then
# 添加防火墙规则
iptables --table nat -A PREROUTING -p udp --dport ${PORT_HOPPING_START}:${PORT_HOPPING_END} -m comment --comment "NAT ${PORT_HOPPING_START}:${PORT_HOPPING_END} to ${PORT_HOPPING_TARGET} (Sing-box Family Bucket)" -j DNAT --to-destination :${PORT_HOPPING_TARGET} 2>/dev/null
ip6tables --table nat -A PREROUTING -p udp --dport ${PORT_HOPPING_START}:${PORT_HOPPING_END} -m comment --comment "NAT ${PORT_HOPPING_START}:${PORT_HOPPING_END} to ${PORT_HOPPING_TARGET} (Sing-box Family Bucket)" -j DNAT --to-destination :${PORT_HOPPING_TARGET} 2>/dev/null
# 将 iptables, ip6tables 添加到默认运行级别
rc-update show default | grep -q 'iptables' || rc-update add iptables >/dev/null 2>&1
rc-update show default | grep -q 'ip6tables' || rc-update add ip6tables >/dev/null 2>&1
rc-update show default | grep -q 'iptables' && rc-update show default | grep -q 'ip6tables' || warning "\n $(text 96) \n"
# 保存当前的 iptables, ip6tables 规则集,以便在开机时恢复
rc-service iptables save >/dev/null 2>&1
rc-service ip6tables save >/dev/null 2>&1
elif [ -x "$(type -p firewalld)" ]; then
[ "$(systemctl is-active firewalld)" != 'active' ] && systemctl enable --now firewalld >/dev/null 2>&1
if [ "$(firewall-cmd --query-masquerade --permanent)" != 'yes' ] ; then
firewall-cmd --add-masquerade --permanent >/dev/null 2>&1
firewall-cmd --reload >/dev/null 2>&1
[ "$(firewall-cmd --query-masquerade --permanent)" = 'yes' ] && info "\n firewalld masquerade $(text 28) $(text 37) \n" || warning "\n firewalld masquerade $(text 28) $(text 38) \n"
fi
# 添加防火墙规则
firewall-cmd --add-forward-port=port=$PORT_HOPPING_START-$PORT_HOPPING_END:proto=udp:toport=${PORT_HOPPING_TARGET} --permanent >/dev/null 2>&1
firewall-cmd --reload >/dev/null 2>&1
else
if [ ! -x "$(type -p netfilter-persistent)" ]; then
info "\n $(text 7) iptables-persistent"
${PACKAGE_INSTALL[int]} iptables-persistent >/dev/null 2>&1
fi
[ -x "$(type -p netfilter-persistent)" ] || warning "\n $(text 95) \n"
# 添加防火墙规则
iptables --table nat -A PREROUTING -p udp --dport ${PORT_HOPPING_START}:${PORT_HOPPING_END} -m comment --comment "NAT ${PORT_HOPPING_START}:${PORT_HOPPING_END} to ${PORT_HOPPING_TARGET} (Sing-box Family Bucket)" -j DNAT --to-destination :${PORT_HOPPING_TARGET} 2>/dev/null
ip6tables --table nat -A PREROUTING -p udp --dport ${PORT_HOPPING_START}:${PORT_HOPPING_END} -m comment --comment "NAT ${PORT_HOPPING_START}:${PORT_HOPPING_END} to ${PORT_HOPPING_TARGET} (Sing-box Family Bucket)" -j DNAT --to-destination :${PORT_HOPPING_TARGET} 2>/dev/null
# 保存当前的 iptables, ip6tables 规则集,以便在开机时恢复
[ "$(systemctl is-active netfilter-persistent)" != 'active' ] && warning "\n $(text 96) \n" || netfilter-persistent save 2>/dev/null
fi
}
# 查端口跳跃的 dnat 端口
check_port_hopping_nat() {
PORT_HOPPING_TARGET=$(awk -F [:,] '/"listen_port"/{print $2}' ${WORK_DIR}/conf/*${NODE_TAG[1]}_inbounds.json)
if [ "$SYSTEM" = 'Alpine' ]; then
local IPTABLES_PREROUTING_LIST=$(iptables --table nat --list-rules PREROUTING 2>/dev/null | grep 'Sing-box Family Bucket')
[ -n "$IPTABLES_PREROUTING_LIST" ] && PORT_HOPPING_RANGE=$(awk '{for (i=0; i<NF; i++) if ($i=="--dport") {print $(i+1); exit}}' <<< "$IPTABLES_PREROUTING_LIST") && PORT_HOPPING_TARGET=$(awk '{for (i=0; i<NF; i++) if ($i=="to") {print $(i+1); exit}}' <<< "$IPTABLES_PREROUTING_LIST")
[ -n "$PORT_HOPPING_RANGE" ] && PORT_HOPPING_START=${PORT_HOPPING_RANGE%:*} && PORT_HOPPING_END=${PORT_HOPPING_RANGE#*:}
elif [ "$(systemctl is-active firewalld)" = 'active' ]; then
local FIREWALL_LIST=$(firewall-cmd --list-all --permanent | grep "toport=${PORT_HOPPING_TARGET}")
[ -n "$FIREWALL_LIST" ] && PORT_HOPPING_START=$(sed "s/.*port=\([^-]\+\)-.*toport.*/\1/" <<< "$FIREWALL_LIST") &&
PORT_HOPPING_END=$(sed "s/.*port=$PORT_HOPPING_START-\([^:]\+\):.*toport.*/\1/" <<< "$FIREWALL_LIST") &&
PORT_HOPPING_TARGET=$(sed "s/.*toport=\([^:]\+\):.*/\1/" <<< "$FIREWALL_LIST")
else
local IPTABLES_PREROUTING_LIST=$(iptables --table nat --list-rules PREROUTING 2>/dev/null | grep 'Sing-box Family Bucket')
[ -n "$IPTABLES_PREROUTING_LIST" ] && PORT_HOPPING_RANGE=$(awk '{for (i=0; i<NF; i++) if ($i=="--dport") {print $(i+1); exit}}' <<< "$IPTABLES_PREROUTING_LIST") && PORT_HOPPING_TARGET=$(awk '{for (i=0; i<NF; i++) if ($i=="to") {print $(i+1); exit}}' <<< "$IPTABLES_PREROUTING_LIST")
[ -n "$PORT_HOPPING_RANGE" ] && PORT_HOPPING_START=${PORT_HOPPING_RANGE%:*} && PORT_HOPPING_END=${PORT_HOPPING_RANGE#*:}
fi
}
# 检测 IPv4 IPv6 信息
check_system_ip() {
[ "$L" = 'C' ] && local IS_CHINESE='?lang=zh-CN'
local DEFAULT_LOCAL_INTERFACE4=$(ip -4 route show default | awk '/default/ {for (i=0; i<NF; i++) if ($i=="dev") {print $(i+1); exit}}')
local DEFAULT_LOCAL_INTERFACE6=$(ip -6 route show default | awk '/default/ {for (i=0; i<NF; i++) if ($i=="dev") {print $(i+1); exit}}')
if [ -n ""${DEFAULT_LOCAL_INTERFACE4}${DEFAULT_LOCAL_INTERFACE6}"" ]; then
local DEFAULT_LOCAL_IP4=$(ip -4 addr show $DEFAULT_LOCAL_INTERFACE4 | sed -n 's#.*inet \([^/]\+\)/[0-9]\+.*global.*#\1#gp')
local DEFAULT_LOCAL_IP6=$(ip -6 addr show $DEFAULT_LOCAL_INTERFACE6 | sed -n 's#.*inet6 \([^/]\+\)/[0-9]\+.*global.*#\1#gp')
[ -n "$DEFAULT_LOCAL_IP4" ] && local BIND_ADDRESS4="--bind-address=$DEFAULT_LOCAL_IP4"
[ -n "$DEFAULT_LOCAL_IP6" ] && local BIND_ADDRESS6="--bind-address=$DEFAULT_LOCAL_IP6"
fi
WAN4=$(wget $BIND_ADDRESS4 -qO- --no-check-certificate --tries=2 --timeout=2 http://api-ipv4.ip.sb)
[ -n "$WAN4" ] && local IP4_JSON=$(wget -qO- --no-check-certificate --tries=2 --timeout=2 https://ip.forvps.gq/${WAN4}${IS_CHINESE}) &&
COUNTRY4=$(sed -En 's/.*"country":[ ]*"([^"]+)".*/\1/p' <<< "$IP4_JSON") &&
ASNORG4=$(sed -En 's/.*"(isp|asn_org)":[ ]*"([^"]+)".*/\2/p' <<< "$IP4_JSON")
WAN6=$(wget $BIND_ADDRESS6 -qO- --no-check-certificate --tries=2 --timeout=2 http://api-ipv6.ip.sb)
[ -n "$WAN6" ] && local IP6_JSON=$(wget -qO- --no-check-certificate --tries=2 --timeout=2 https://ip.forvps.gq/${WAN6}${IS_CHINESE}) &&
COUNTRY6=$(sed -En 's/.*"country":[ ]*"([^"]+)".*/\1/p' <<< "$IP6_JSON") &&
ASNORG6=$(sed -En 's/.*"(isp|asn_org)":[ ]*"([^"]+)".*/\2/p' <<< "$IP6_JSON")
}
# 输入起始 port 函数
input_start_port() {
local NUM=$1
local PORT_ERROR_TIME=6
while true; do
[ "$PORT_ERROR_TIME" -lt 6 ] && unset IN_USED START_PORT
(( PORT_ERROR_TIME-- )) || true
if [ "$PORT_ERROR_TIME" = 0 ]; then
error "\n $(text 3) \n"
else
[ -z "$START_PORT" ] && reading "\n $(text 11) " START_PORT
fi
START_PORT=${START_PORT:-"$START_PORT_DEFAULT"}
if [[ "$START_PORT" =~ ^[1-9][0-9]{2,4}$ && "$START_PORT" -ge "$MIN_PORT" && "$START_PORT" -le "$MAX_PORT" ]]; then
for port in $(eval echo {$START_PORT..$[START_PORT+NUM-1]}); do
ss -nltup | grep -q ":$port" && IN_USED+=("$port")
done
[ "${#IN_USED[*]}" -eq 0 ] && break || warning "\n $(text 44) \n"
fi
done
}
# 定义 Sing-box 变量
sing-box_variables() {
if grep -qi 'cloudflare' <<< "$ASNORG4$ASNORG6"; then
if grep -qi 'cloudflare' <<< "$ASNORG6" && [ -n "$WAN4" ] && ! grep -qi 'cloudflare' <<< "$ASNORG4"; then
SERVER_IP_DEFAULT=$WAN4
elif grep -qi 'cloudflare' <<< "$ASNORG4" && [ -n "$WAN6" ] && ! grep -qi 'cloudflare' <<< "$ASNORG6"; then
SERVER_IP_DEFAULT=$WAN6
else
local a=6
until [ -n "$SERVER_IP" ]; do
((a--)) || true
[ "$a" = 0 ] && error "\n $(text 3) \n"
reading "\n $(text 46) " SERVER_IP
done
fi
elif [ -n "$WAN4" ]; then
SERVER_IP_DEFAULT=$WAN4
elif [ -n "$WAN6" ]; then
SERVER_IP_DEFAULT=$WAN6
fi
# 选择安装的协议,由于选项 a 为全部协议,所以选项数不是从 a 开始,而是从 b 开始,处理输入:把大写全部变为小写,把不符合的选项去掉,把重复的选项合并
MAX_CHOOSE_PROTOCOLS=$(asc $[CONSECUTIVE_PORTS+96+1])
if [ -z "$CHOOSE_PROTOCOLS" ]; then
hint "\n $(text 49) "
for e in "${!PROTOCOL_LIST[@]}"; do
[[ "$e" =~ '6'|'7' ]] && hint " $(asc $[e+98]). ${PROTOCOL_LIST[e]} " || hint " $(asc $[e+98]). ${PROTOCOL_LIST[e]} "
done
reading "\n $(text 24) " CHOOSE_PROTOCOLS
fi
# 对选择协议的输入处理逻辑:先把所有的大写转为小写,并把所有没有去选项剔除掉,最后按输入的次序排序。如果选项为 a(all) 和其他选项并存,将会忽略 a,如 abc 则会处理为 bc
[[ ! "${CHOOSE_PROTOCOLS,,}" =~ [b-$MAX_CHOOSE_PROTOCOLS] ]] && INSTALL_PROTOCOLS=($(eval echo {b..$MAX_CHOOSE_PROTOCOLS})) || INSTALL_PROTOCOLS=($(grep -o . <<< "$CHOOSE_PROTOCOLS" | sed "/[^b-$MAX_CHOOSE_PROTOCOLS]/d" | awk '!seen[$0]++'))
# 显示选择协议及其次序,输入开始端口号
if [ -z "$START_PORT" ]; then
hint "\n $(text 60) "
for w in "${!INSTALL_PROTOCOLS[@]}"; do
[ "$w" -ge 9 ] && hint " $[w+1]. ${PROTOCOL_LIST[$(($(asc ${INSTALL_PROTOCOLS[w]}) - 98))]} " || hint " $[w+1] . ${PROTOCOL_LIST[$(($(asc ${INSTALL_PROTOCOLS[w]}) - 98))]} "
done
input_start_port ${#INSTALL_PROTOCOLS[@]}
fi
# 输出模式选择,输入用于订阅的 Nginx 服务端口号, 后台根据选择安装依赖
[[ "$IS_SUB" = 'is_sub' || "$IS_ARGO" = 'is_argo' ]] && input_nginx_port
# 输入服务器 IP,默认为检测到的服务器 IP,如果全部为空,则提示并退出脚本
[ -z "$SERVER_IP" ] && reading "\n $(text 10) " SERVER_IP
SERVER_IP=${SERVER_IP:-"$SERVER_IP_DEFAULT"} && WS_SERVER_IP_SHOW=$SERVER_IP
[ -z "$SERVER_IP" ] && error " $(text 47) "
# 根据服务 IP,使用不同的 warp 配置
if [[ "$SERVER_IP" =~ : ]]; then
WARP_ENDPOINT=2606:4700:d0::a29f:c101
DOMAIN_STRATEG=prefer_ipv6
else
WARP_ENDPOINT=162.159.193.10
DOMAIN_STRATEG=prefer_ipv4
fi
# 检测是否解锁 chatGPT
CHATGPT_OUT=warp-ep;
[ "$(check_chatgpt ${DOMAIN_STRATEG: -1})" = 'unlock' ] && CHATGPT_OUT=direct
# 如选择有 c. hysteria2 时,选择是否使用端口跳跃
[[ "${INSTALL_PROTOCOLS[@]}" =~ 'c' ]] && input_hopping_port
# 如选择有 h. vmess + ws 或 i. vless + ws 时,先检测是否有支持的 http 端口可用,如有则要求输入域名和 cdn
if [[ "${INSTALL_PROTOCOLS[@]}" =~ 'h' ]]; then
if [ "$IS_ARGO" = 'is_argo' ]; then
[ "$ARGO_READY" != 'argo_ready' ] && input_argo_auth is_install
local ARGO_READY=argo_ready
else
local DOMAIN_ERROR_TIME=5
until [ -n "$VMESS_HOST_DOMAIN" ]; do
(( DOMAIN_ERROR_TIME-- )) || true
[ "$DOMAIN_ERROR_TIME" != 0 ] && TYPE=VMESS && reading "\n $(text 50) " VMESS_HOST_DOMAIN || error "\n $(text 3) \n"
done
fi
fi
if [[ "${INSTALL_PROTOCOLS[@]}" =~ 'i' ]]; then
if [ "$IS_ARGO" = 'is_argo' ]; then
[ "$ARGO_READY" != 'argo_ready' ] && input_argo_auth is_install
local ARGO_READY=argo_ready
else
local DOMAIN_ERROR_TIME=5
until [ -n "$VLESS_HOST_DOMAIN" ]; do
(( DOMAIN_ERROR_TIME-- )) || true
[ "$DOMAIN_ERROR_TIME" != 0 ] && TYPE=VLESS && reading "\n $(text 50) " VLESS_HOST_DOMAIN || error "\n $(text 3) \n"
done
fi
fi
# 选择或者输入 cdn
[[ -z "$CDN" && -n "${VMESS_HOST_DOMAIN}${VLESS_HOST_DOMAIN}${ARGO_READY}" ]] && input_cdn
# 输入 UUID ,错误超过 5 次将会退出
UUID_DEFAULT=$(cat /proc/sys/kernel/random/uuid)
[ -z "$UUID_CONFIRM" ] && reading "\n $(text 12) " UUID_CONFIRM
local UUID_ERROR_TIME=5
until [[ -z "$UUID_CONFIRM" || "${UUID_CONFIRM,,}" =~ ^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$ ]]; do
(( UUID_ERROR_TIME-- )) || true
[ "$UUID_ERROR_TIME" = 0 ] && error "\n $(text 3) \n" || reading "\n $(text 4) \n" UUID_CONFIRM
done
UUID_CONFIRM=${UUID_CONFIRM:-"$UUID_DEFAULT"}
# 输入节点名,以系统的 hostname 作为默认
if [ -z "$NODE_NAME_CONFIRM" ]; then
if [ -x "$(type -p hostname)" ]; then
NODE_NAME_DEFAULT="$(hostname)"
elif [ -s /etc/hostname ]; then
NODE_NAME_DEFAULT="$(cat /etc/hostname)"
else
NODE_NAME_DEFAULT="Sing-Box"
fi
reading "\n $(text 13) " NODE_NAME_CONFIRM
NODE_NAME_CONFIRM="${NODE_NAME_CONFIRM:-"$NODE_NAME_DEFAULT"}"
fi
}
check_dependencies() {
# 如果是 Alpine,先升级 wget ,安装 systemctl-py 版
if [ "$SYSTEM" = 'Alpine' ]; then
local CHECK_WGET=$(wget 2>&1 | head -n 1)
grep -qi 'busybox' <<< "$CHECK_WGET" && ${PACKAGE_INSTALL[int]} wget >/dev/null 2>&1
local DEPS_CHECK=("bash" "rc-update" "virt-what" "python3" "iptables" "ip6tables")
local DEPS_INSTALL=("bash" "openrc" "virt-what" "python3" "iptables" "ip6tables")
for g in "${!DEPS_CHECK[@]}"; do
[ ! -x "$(type -p ${DEPS_CHECK[g]})" ] && DEPS_ALPINE+=(${DEPS_INSTALL[g]})
done
if [ "${#DEPS_ALPINE[@]}" -ge 1 ]; then
info "\n $(text 7) $(sed "s/ /,&/g" <<< ${DEPS_ALPINE[@]}) \n"
${PACKAGE_UPDATE[int]} >/dev/null 2>&1
${PACKAGE_INSTALL[int]} ${DEPS_ALPINE[@]} >/dev/null 2>&1
[[ -z "$VIRT" && "${DEPS_ALPINE[@]}" =~ 'virt-what' ]] && VIRT=$(virt-what | tr '\n' ' ')
fi
[ ! -x "$(type -p systemctl)" ] && wget --no-check-certificate --quiet ${GH_PROXY}https://raw.githubusercontent.com/gdraheim/docker-systemctl-replacement/master/files/docker/systemctl3.py -O /bin/systemctl && chmod a+x /bin/systemctl
fi
# 检测 Linux 系统的依赖,升级库并重新安装依赖
local DEPS_CHECK=("wget" "tar" "systemctl" "ss" "bash" "openssl")
local DEPS_INSTALL=("wget" "tar" "systemctl" "iproute2" "bash" "openssl")
for g in "${!DEPS_CHECK[@]}"; do
[ ! -x "$(type -p ${DEPS_CHECK[g]})" ] && DEPS+=(${DEPS_INSTALL[g]})
done
if [ "$SYSTEM" = 'CentOS' ]; then
if [ "$IS_CENTOS" = 'CentOS7' ]; then
yum repolist | grep -q epef || DEPS+=(epel-release)
fi
[ ! -x "$(type -p firewalld)" ] && DEPS+=(firewalld)
else
[ ! -x "$(type -p iptables)" ] && DEPS+=(iptables)
[ ! -x "$(type -p ip6tables)" ] && DEPS+=(ip6tables)
fi
# 如需要安装的依赖大于0,就更新库并安装
if [[ "${#DEPS[@]}" > 0 ]]; then
[[ ! "$SYSTEM" =~ Alpine|CentOS ]] && ${PACKAGE_UPDATE[int]} >/dev/null 2>&1
${PACKAGE_INSTALL[int]} ${DEPS[@]} >/dev/null 2>&1
# 如新安装 firewalld,设置允许所有端口的 TCP 和 UDP 入站连接
if [[ "${DEPS[@]}" =~ 'firewalld' ]]; then
firewall-cmd --add-port=0-65535/tcp --permanent >/dev/null 2>&1
firewall-cmd --add-port=0-65535/udp --permanent >/dev/null 2>&1
firewall-cmd --reload >/dev/null 2>&1
fi
else
info "\n $(text 8) \n"
fi
}
# 检查并安装 nginx
check_nginx() {
if [ ! -x "$(type -p nginx)" ]; then
info "\n $(text 7) nginx \n"
${PACKAGE_UPDATE[int]} >/dev/null 2>&1
${PACKAGE_INSTALL[int]} nginx >/dev/null 2>&1
# 如果新安装的 Nginx ,先停掉服务
systemctl disable --now nginx >/dev/null 2>&1
fi
}
# Json 生成两个配置文件
export_argo_json_file() {
local FILE_PATH=$1
[[ -z "$PORT_NGINX" && -s ${WORK_DIR}/nginx.conf ]] && local PORT_NGINX=$(awk '/listen/{print $2; exit}' ${WORK_DIR}/nginx.conf)
[ ! -s $FILE_PATH/tunnel.json ] && echo $ARGO_JSON > $FILE_PATH/tunnel.json
[ ! -s $FILE_PATH/tunnel.yml ] && cat > $FILE_PATH/tunnel.yml << EOF
tunnel: $(awk -F '"' '{print $12}' <<< "$ARGO_JSON")
credentials-file: ${WORK_DIR}/tunnel.json
ingress:
- hostname: ${ARGO_DOMAIN}
service: http://localhost:${PORT_NGINX}