347 Commits

Author SHA1 Message Date
MacRimi
5cb9e13ca7 Update CHANGELOG.md 2025-09-10 17:37:51 +02:00
MacRimi
0187010f94 Create main-menu.png 2025-09-10 16:28:47 +02:00
ProxMenuxBot
2c2ed21e59 Update helpers_cache.json 2025-09-10 12:24:52 +00:00
MacRimi
f8b2ccec40 Update commands_share.sh 2025-09-10 13:48:17 +02:00
MacRimi
e858dc582d Update commands_share.sh 2025-09-10 13:47:13 +02:00
MacRimi
dd737f4b46 Update commands_share.sh 2025-09-10 13:46:02 +02:00
MacRimi
f0bc238b6d Update commands_share.sh 2025-09-10 13:35:16 +02:00
MacRimi
af55424850 Update lxc-mount-manager_minimal.sh 2025-09-10 13:16:54 +02:00
MacRimi
902534baff update menu shared 2025-09-10 12:57:17 +02:00
MacRimi
6daa630040 Update nfs_host.sh 2025-09-10 12:53:09 +02:00
MacRimi
0b2b86673b Update lxc-mount-manager_minimal.sh 2025-09-10 12:31:49 +02:00
MacRimi
6aa5b58208 Update share_menu.sh 2025-09-10 12:17:03 +02:00
MacRimi
4430201cd2 Update lxc-mount-manager_minimal.sh 2025-09-10 12:16:25 +02:00
MacRimi
7c7963a83e Create lxc-mount-manager_minimal.sh 2025-09-10 11:10:02 +02:00
ProxMenuxBot
e2202cd2d8 Update helpers_cache.json 2025-09-09 18:17:13 +00:00
MacRimi
a931be83bc Update share-common.func 2025-09-09 19:20:15 +02:00
MacRimi
7350bea345 Update auto_post_install.sh 2025-09-09 19:16:13 +02:00
MacRimi
9b1e39dbb4 Update update-pve.sh 2025-09-09 19:14:27 +02:00
MacRimi
15cd118845 Update auto_post_install.sh 2025-09-09 19:06:33 +02:00
MacRimi
d58dff047c Update auto_post_install.sh 2025-09-08 19:24:49 +02:00
MacRimi
a2f83c896c Update common-functions.sh 2025-09-08 19:14:41 +02:00
MacRimi
6ef77c731c Update guia.md 2025-09-08 17:44:17 +02:00
MacRimi
29b0f61958 Update guia.md 2025-09-08 17:43:39 +02:00
MacRimi
e944b2ecdd Update guia.md 2025-09-08 17:24:23 +02:00
MacRimi
41819c46a3 Update guia.md 2025-09-08 17:21:58 +02:00
MacRimi
13f391a6f0 Update guia.md 2025-09-08 17:14:52 +02:00
MacRimi
85a3d44f2c Update guia.md 2025-09-08 17:05:26 +02:00
MacRimi
0792392058 Update guia.md 2025-09-08 16:42:46 +02:00
MacRimi
ff5083ada0 Update guia.md 2025-09-08 16:20:10 +02:00
MacRimi
62841677bc Update guia.md 2025-09-08 16:06:50 +02:00
MacRimi
1761cf53a2 Update guia.md 2025-09-08 15:27:32 +02:00
MacRimi
a771efc5fa Update guia.md 2025-09-08 15:08:02 +02:00
MacRimi
ed049da76a Update guia.md 2025-09-08 15:03:10 +02:00
MacRimi
5d1d357a2e Merge branch 'main' of https://github.com/MacRimi/ProxMenux 2025-09-08 15:01:02 +02:00
MacRimi
30d0706a1c Create guia.md 2025-09-08 15:01:00 +02:00
ProxMenuxBot
e9667e1266 Update helpers_cache.json 2025-09-08 12:27:18 +00:00
MacRimi
73109483e7 Update share-common.func 2025-09-08 11:29:02 +02:00
MacRimi
a9c1acf204 Update lxc-mount-manager.sh 2025-09-08 11:15:52 +02:00
MacRimi
81c4f5814c Update samba_host.sh 2025-09-08 10:40:59 +02:00
MacRimi
c595f6d781 Update nfs_host.sh 2025-09-08 10:36:57 +02:00
MacRimi
24bb6b1d3d Update lxc-mount-manager.sh 2025-09-07 19:10:23 +02:00
MacRimi
49eeb6020d Update install_proxmenux.sh 2025-09-07 17:24:29 +02:00
MacRimi
7c272bd2a2 Update install_proxmenux.sh 2025-09-07 17:22:47 +02:00
MacRimi
cfbd865937 Update share-common.func 2025-09-07 09:29:43 +02:00
MacRimi
fe472f33ef Update lxc-mount-manager.sh 2025-09-07 09:28:09 +02:00
MacRimi
8d6b3d650f update menu share 2025-09-07 09:19:53 +02:00
MacRimi
3b0d5b5eb7 Update nfs_lxc_server.sh 2025-09-07 09:16:22 +02:00
MacRimi
875e8a99bd Update samba_client.sh 2025-09-07 09:06:47 +02:00
MacRimi
6c19d81844 Update samba_client.sh 2025-09-07 09:05:23 +02:00
MacRimi
ba535a931f Update nfs_client.sh 2025-09-07 09:04:13 +02:00
MacRimi
45dca5218d Merge branch 'main' of https://github.com/MacRimi/ProxMenux 2025-09-07 09:00:39 +02:00
MacRimi
da3cb9971b Update share-common.func 2025-09-07 09:00:37 +02:00
ProxMenuxBot
b39270dc1e Update helpers_cache.json 2025-09-07 01:03:30 +00:00
MacRimi
ae8a7d0de9 Update commands_share.sh 2025-09-06 23:59:23 +02:00
MacRimi
2d501415bf Update commands_share.sh 2025-09-06 23:50:57 +02:00
MacRimi
da639ccaac Update commands_share.sh 2025-09-06 23:35:46 +02:00
MacRimi
a352770e2d Update share_menu.sh 2025-09-06 23:20:59 +02:00
MacRimi
e3e1899466 update menu share 2025-09-06 23:18:11 +02:00
MacRimi
e67288e623 Update lxc-mount-manager.sh 2025-09-06 22:55:28 +02:00
MacRimi
4019e49b07 Update share-common.func 2025-09-06 22:51:57 +02:00
MacRimi
cd8711f3bc Update lxc-mount-manager.sh 2025-09-06 22:50:30 +02:00
MacRimi
0d119379de Update lxc-mount-manager.sh 2025-09-06 22:46:52 +02:00
MacRimi
aa2b6ff112 Update lxc-mount-manager.sh 2025-09-06 22:44:09 +02:00
MacRimi
3482f7dc98 Update share-common.func 2025-09-06 22:42:17 +02:00
MacRimi
16c321f114 Update lxc-mount-manager.sh 2025-09-06 22:33:19 +02:00
MacRimi
a81e7f3c44 Update lxc-mount-manager.sh 2025-09-06 22:25:50 +02:00
MacRimi
d7cc001521 Update lxc-mount-manager.sh 2025-09-06 22:20:34 +02:00
MacRimi
eb11962231 Update share-common.func 2025-09-06 22:19:52 +02:00
MacRimi
9f73b8f159 Update lxc-mount-manager.sh 2025-09-06 22:17:08 +02:00
MacRimi
873a4abe24 Update share-common.func 2025-09-06 22:16:40 +02:00
MacRimi
56bc584f5e Update lxc-mount-manager.sh 2025-09-06 22:10:01 +02:00
MacRimi
2a9f2f3c2e Update lxc-mount-manager.sh 2025-09-06 22:09:11 +02:00
MacRimi
ee719cdd39 Update share-common.func 2025-09-06 22:06:02 +02:00
MacRimi
a571b57b30 Update share-common.func 2025-09-06 21:48:39 +02:00
MacRimi
5ee7a23bea Update share-common.func 2025-09-06 21:47:20 +02:00
MacRimi
fe159ea195 Update share-common.func 2025-09-06 21:42:30 +02:00
MacRimi
8fcdf6176b Update share-common.func 2025-09-06 21:31:03 +02:00
MacRimi
715166bbca Update share-common.func 2025-09-06 21:22:40 +02:00
MacRimi
1d58072c70 Update share-common.func 2025-09-06 21:21:39 +02:00
MacRimi
d667cde699 Update lxc-mount-manager.sh 2025-09-06 21:19:55 +02:00
MacRimi
4cd8889c38 Update share-common.func 2025-09-06 21:14:54 +02:00
MacRimi
93896f6fb7 Update share-common.func 2025-09-06 21:07:12 +02:00
MacRimi
3b3f0387bb Update share-common.func 2025-09-06 21:05:32 +02:00
MacRimi
2875c9af95 Update lxc-mount-manager.sh 2025-09-06 20:59:03 +02:00
MacRimi
93ef1bfccc update share menu 2025-09-06 20:57:04 +02:00
MacRimi
a886af1d87 Update samba_client.sh 2025-09-06 20:48:37 +02:00
MacRimi
d731ff3ae6 Update samba_host.sh 2025-09-06 20:40:45 +02:00
MacRimi
d44864637d Update nfs_host_auto.sh 2025-09-06 20:37:23 +02:00
MacRimi
674ee34ec6 Update nfs_host_auto.sh 2025-09-06 20:30:04 +02:00
MacRimi
a93eeda243 Update share-common.func 2025-09-06 19:56:24 +02:00
MacRimi
80fd92e2a1 Update share-common.func 2025-09-06 19:55:01 +02:00
MacRimi
d4ff2da473 Update share-common.func 2025-09-06 19:16:15 +02:00
MacRimi
9b7b271580 update menu shared 2025-09-06 19:13:52 +02:00
MacRimi
e1b340966a Update share-common.func 2025-09-06 18:56:10 +02:00
MacRimi
7ec4c331af Update nfs_client.sh 2025-09-06 18:39:35 +02:00
MacRimi
3102d596ee Update nfs_lxc_server.sh 2025-09-06 18:37:28 +02:00
MacRimi
af56dc546e Update nfs_host_auto.sh 2025-09-06 18:26:13 +02:00
MacRimi
15d47499fa Update nfs_host_auto.sh 2025-09-06 18:22:02 +02:00
MacRimi
53a34d0470 update nfs menu 2025-09-06 18:20:20 +02:00
MacRimi
3ee675cefe Update nfs_lxc_server.sh 2025-09-06 18:08:48 +02:00
MacRimi
d98c7bdc03 Update share_menu.sh 2025-09-06 16:48:07 +02:00
MacRimi
bb4f1ebed6 Update share_menu.sh 2025-09-06 16:44:58 +02:00
MacRimi
c8f73ea23b Merge branch 'main' of https://github.com/MacRimi/ProxMenux 2025-09-06 16:41:55 +02:00
MacRimi
8292b12787 Create nfs_lxc_server.sh 2025-09-06 16:41:54 +02:00
ProxMenuxBot
0f518e3c35 Update helpers_cache.json 2025-09-06 12:22:12 +00:00
MacRimi
1c2f67d43d Update lxc-mount-manager.sh 2025-09-06 11:55:23 +02:00
MacRimi
a5560a3123 Update share-common.func 2025-09-06 11:50:48 +02:00
MacRimi
1332096360 Update share-common.func 2025-09-06 11:39:17 +02:00
MacRimi
80381a6375 Update share-common.func 2025-09-06 11:32:07 +02:00
MacRimi
acf92bd005 Update share-common.func 2025-09-06 11:30:23 +02:00
MacRimi
da4f8a3a19 Update share-common.func 2025-09-06 11:11:19 +02:00
MacRimi
3a332192e3 Update share-common.func 2025-09-06 11:08:23 +02:00
MacRimi
1fdb1d87cc Update share-common.func 2025-09-06 11:01:35 +02:00
MacRimi
b99aa55d7a Update share-common.func 2025-09-06 10:46:22 +02:00
MacRimi
de20da2dad Update lxc-mount-manager.sh 2025-09-06 10:10:37 +02:00
MacRimi
9444f0a68b Update nfs_host_auto.sh 2025-09-06 08:51:41 +02:00
MacRimi
48fd223a28 Update samba.sh 2025-09-04 20:38:11 +02:00
MacRimi
0845efe419 Update samba_client.sh 2025-09-04 20:37:18 +02:00
MacRimi
57b7ba91bc Update share_menu.sh 2025-09-04 20:34:43 +02:00
MacRimi
97af8a4892 Update share_menu.sh 2025-09-04 20:31:33 +02:00
MacRimi
d6f237e289 Update share_menu.sh 2025-09-04 20:30:36 +02:00
MacRimi
aba7109b35 Update samba_host.sh 2025-09-04 20:28:16 +02:00
MacRimi
d3ec71052e Update samba_client.sh 2025-09-04 20:26:51 +02:00
MacRimi
1be63f396b Update nfs_client.sh 2025-09-04 20:26:26 +02:00
MacRimi
9308742146 Update samba_client.sh 2025-09-04 20:23:00 +02:00
MacRimi
b32241082d Update share_menu.sh 2025-09-04 20:12:24 +02:00
MacRimi
1f8504d685 update shared 2025-09-04 20:04:08 +02:00
MacRimi
97c5c48150 Update nfs_host.sh 2025-09-03 23:13:21 +02:00
MacRimi
afe84dc46a Update nfs_client.sh 2025-09-03 23:10:01 +02:00
MacRimi
ffafd42f03 Update nfs.sh 2025-09-03 23:06:47 +02:00
MacRimi
7dca715c91 Update nfs.sh 2025-09-03 23:04:46 +02:00
MacRimi
7695e1d8dd Update nfs.sh 2025-09-03 22:55:45 +02:00
MacRimi
84b86d1db7 Update nfs.sh 2025-09-03 22:14:29 +02:00
MacRimi
bae3ef6460 Update nfs.sh 2025-09-03 22:06:34 +02:00
MacRimi
97c6ec8875 Update share-common.func 2025-09-03 16:47:03 +02:00
MacRimi
d33128dc26 Update share_menu.sh 2025-09-03 12:27:59 +02:00
MacRimi
10bdecabb6 Update share_menu.sh 2025-09-03 12:25:35 +02:00
MacRimi
de88f530c8 Update share_menu.sh 2025-09-03 12:23:54 +02:00
MacRimi
fb511b7596 Update share_menu.sh 2025-09-03 12:22:49 +02:00
MacRimi
322665ce91 Update share_menu.sh 2025-09-03 12:21:21 +02:00
MacRimi
baeca1fcfb Update share-common.func 2025-09-03 11:35:38 +02:00
MacRimi
095b98c36a Update share-common.func 2025-09-03 11:28:37 +02:00
MacRimi
29bb7e7608 Update share-common.func 2025-09-03 11:16:34 +02:00
MacRimi
e3d137efba Update share-common.func 2025-09-02 22:56:46 +02:00
MacRimi
207e915393 Update share-common.func 2025-09-02 22:45:33 +02:00
MacRimi
614e629a2b Update share-common.func 2025-09-02 22:44:42 +02:00
MacRimi
f35de5c749 Update share-common.func 2025-09-02 21:34:13 +02:00
MacRimi
c1623bd4df Update share-common.func 2025-09-02 21:23:57 +02:00
ProxMenuxBot
8690da5017 Update helpers_cache.json 2025-09-02 18:17:01 +00:00
MacRimi
696adcdc24 Update share-common.func 2025-09-02 18:48:57 +02:00
MacRimi
2756bd06c1 Update share-common.func 2025-09-02 18:48:20 +02:00
MacRimi
4893f6ea00 Update lxc-mount-manager.sh 2025-09-02 18:45:53 +02:00
MacRimi
35a7348197 Update lxc-mount-manager.sh 2025-09-02 18:45:04 +02:00
MacRimi
cdd6333d0a Update share_menu.sh 2025-09-02 18:43:27 +02:00
MacRimi
54399b5b5d Update share-common.func 2025-09-02 18:25:39 +02:00
MacRimi
f6b192cc1e Update lxc-mount-manager.sh 2025-09-02 18:22:34 +02:00
MacRimi
cd231b90d8 Update share-common.func 2025-09-02 17:21:09 +02:00
ProxMenuxBot
87fe788358 Update helpers_cache.json 2025-09-02 12:26:27 +00:00
MacRimi
3e9bd21ea8 update share menu 2025-09-02 00:02:16 +02:00
MacRimi
b6d4029797 Update local-shared-manager.sh 2025-09-01 19:10:46 +02:00
MacRimi
ec65e96148 Update share_menu.sh 2025-09-01 19:08:48 +02:00
MacRimi
926f1f971f update share menu 2025-09-01 19:06:52 +02:00
MacRimi
5d69fad73f Update share_menu.sh 2025-09-01 18:45:14 +02:00
MacRimi
a796761023 Update share-common.func 2025-09-01 17:39:45 +02:00
MacRimi
5d1338e485 Merge branch 'main' of https://github.com/MacRimi/ProxMenux 2025-09-01 17:21:41 +02:00
MacRimi
ce25a167f1 Update auto_post_install.sh 2025-09-01 17:21:39 +02:00
MacRimi
1c44969580 Update share_menu.sh 2025-09-01 14:43:46 +02:00
MacRimi
b6e04e3ede Update share-common.func 2025-09-01 14:30:16 +02:00
ProxMenuxBot
84c26be703 Update helpers_cache.json 2025-09-01 12:26:47 +00:00
MacRimi
d201160722 Update share-common.func 2025-09-01 14:08:10 +02:00
MacRimi
e112361b43 Update share-common.func 2025-09-01 13:53:54 +02:00
MacRimi
3e69795c9d Update lxc-mount-manager.sh 2025-09-01 13:33:48 +02:00
MacRimi
b11baf2e5d Update auto_post_install.sh 2025-09-01 12:42:46 +02:00
MacRimi
233770b553 Update auto_post_install.sh 2025-09-01 12:41:26 +02:00
MacRimi
187db73798 Update zimaos.sh 2025-09-01 12:20:45 +02:00
MacRimi
0e3fc6f682 Update zimaos.sh 2025-09-01 12:15:30 +02:00
MacRimi
d11e3a4ac4 Update auto_post_install.sh 2025-08-31 23:45:37 +02:00
MacRimi
d3b4ca3e66 Update customizable_post_install.sh 2025-08-31 21:46:28 +02:00
MacRimi
f37fbbfb8b Update menu share 2025-08-30 18:56:49 +02:00
MacRimi
52b7aac424 Update share-common.func 2025-08-30 18:05:02 +02:00
MacRimi
d42f3f8f0c Update share-common.func 2025-08-30 18:02:20 +02:00
MacRimi
91b5c7c9bc Update share-common.func 2025-08-30 17:51:45 +02:00
MacRimi
48feebc092 Update share-common.func 2025-08-30 16:59:46 +02:00
MacRimi
14e2d66d96 Update share-common.func 2025-08-30 11:43:02 +02:00
MacRimi
10d844a195 Update share-common.func 2025-08-30 11:36:52 +02:00
MacRimi
bbf91ae5d6 Update main_menu.sh 2025-08-30 09:40:42 +02:00
ProxMenuxBot
cb82eda49a Update helpers_cache.json 2025-08-30 00:57:37 +00:00
MacRimi
bc1dbb1c27 Update help_info_menu.sh 2025-08-30 00:17:51 +02:00
ProxMenuxBot
9496a7f1ce Update helpers_cache.json 2025-08-28 18:19:20 +00:00
ProxMenuxBot
7241fa31b4 Update helpers_cache.json 2025-08-28 12:26:37 +00:00
MacRimi
fed7216436 Update share-common.func 2025-08-27 18:15:26 +02:00
MacRimi
ffe7d7c4c6 Create group_manager.sh 2025-08-27 10:54:03 +02:00
MacRimi
f430ac8d6c Update upgrade_pve8_to_pve9.sh 2025-08-26 19:40:38 +02:00
MacRimi
70dfd7c9a3 Update upgrade_pve8_to_pve9.sh 2025-08-26 19:25:59 +02:00
MacRimi
ed3140932b Update upgrade_pve8_to_pve9.sh 2025-08-26 19:24:41 +02:00
MacRimi
3cd2bd6ce8 Update share-common.func 2025-08-26 17:46:48 +02:00
MacRimi
982bf45fc4 Update share-common.func 2025-08-26 17:37:09 +02:00
MacRimi
aaba8569fc Update share-common.func 2025-08-26 17:19:11 +02:00
MacRimi
4111e15eb9 Update share-common.func 2025-08-26 17:15:47 +02:00
MacRimi
2012478f26 Update share-common.func 2025-08-26 17:05:17 +02:00
MacRimi
88869d3239 Update share-common.func 2025-08-26 17:02:24 +02:00
ProxMenuxBot
f3c2549b18 Update helpers_cache.json 2025-08-26 12:28:24 +00:00
MacRimi
57e3b839d0 Update share-common.func 2025-08-26 13:56:00 +02:00
MacRimi
faf3f43413 Create share-common.func 2025-08-26 13:26:22 +02:00
ProxMenuxBot
52e5bb3386 Update helpers_cache.json 2025-08-26 01:02:34 +00:00
ProxMenuxBot
89405f6670 Update helpers_cache.json 2025-08-25 18:20:02 +00:00
ProxMenuxBot
73111c4139 Update helpers_cache.json 2025-08-25 12:27:14 +00:00
ProxMenuxBot
04e9c5db8c Update helpers_cache.json 2025-08-24 12:24:09 +00:00
MacRimi
69278902de Update customizable_post_install.sh 2025-08-24 10:55:25 +02:00
MacRimi
efa95b0858 Update customizable_post_install.sh 2025-08-24 10:29:00 +02:00
MacRimi
660128cd5c Update customizable_post_install.sh 2025-08-24 10:28:14 +02:00
MacRimi
ef1e052e47 Update customizable_post_install.sh 2025-08-24 10:06:20 +02:00
ProxMenuxBot
0b346bc343 Update helpers_cache.json 2025-08-24 06:19:28 +00:00
MacRimi
2272eaf833 Update lxc-unprivileged-to-privileged.sh 2025-08-22 19:08:03 +02:00
MacRimi
4adee98bce new menu lxc 2025-08-22 19:05:36 +02:00
MacRimi
cbdb2c0705 Rename lxc-manual-guide.sh to lxc-manual-guide.sh 2025-08-22 19:04:10 +02:00
MacRimi
4f438aabbf update manual lxc guide 2025-08-22 19:03:08 +02:00
MacRimi
b6ccc06963 Update lxc_menu.sh 2025-08-22 18:58:10 +02:00
MacRimi
5b89a15bfc menu lxc 2025-08-22 18:57:00 +02:00
MacRimi
5596ae551d Update storage_menu.sh 2025-08-22 18:34:12 +02:00
MacRimi
1360df592a Create backup_host4.sh 2025-08-22 18:17:01 +02:00
MacRimi
13684ff83c Update share_menu.sh 2025-08-22 18:10:34 +02:00
MacRimi
ae88f7870e Update share_menu.sh 2025-08-22 18:08:20 +02:00
MacRimi
810b6da60c Share menu 2025-08-22 18:05:14 +02:00
ProxMenuxBot
7bdf3e08f9 Update helpers_cache.json 2025-08-21 18:19:22 +00:00
MacRimi
fdad2a087f Update zimaos.sh 2025-08-21 19:39:50 +02:00
MacRimi
c437a8c426 Update zimaos.sh 2025-08-21 19:36:28 +02:00
MacRimi
ef861e6d1d Update zimaos.sh 2025-08-21 19:02:58 +02:00
MacRimi
928a008688 Update zimaos.sh 2025-08-21 18:59:54 +02:00
MacRimi
638a124adb Update zimaos.sh 2025-08-21 18:31:32 +02:00
MacRimi
c2a63ae9bb Update zimaos.sh 2025-08-21 18:30:43 +02:00
MacRimi
28cf31e6e7 Update zimaos.sh 2025-08-21 18:27:30 +02:00
MacRimi
3cf416167d Update select_nas_iso.sh 2025-08-21 18:22:57 +02:00
MacRimi
ebf03923a0 Update select_nas_iso.sh 2025-08-21 18:14:58 +02:00
MacRimi
82797d2421 Update select_nas_iso.sh 2025-08-21 18:05:45 +02:00
MacRimi
52b6be946c Create zimaos.sh 2025-08-21 18:00:58 +02:00
MacRimi
dc46724d7b Update select_linux_iso.sh 2025-08-20 23:02:38 +02:00
MacRimi
ed7d43b6a9 Update select_linux_iso.sh 2025-08-20 22:58:14 +02:00
MacRimi
6f3fc51278 Update system_utils.sh 2025-08-20 22:40:42 +02:00
MacRimi
a446acc282 Update customizable_post_install.sh 2025-08-20 22:39:28 +02:00
MacRimi
d987d639ab Update version.txt 2025-08-20 21:38:45 +02:00
MacRimi
e7e180e468 Update CHANGELOG.md 2025-08-20 21:38:20 +02:00
MacRimi
76770f82cd Update system_utils.sh 2025-08-20 21:22:49 +02:00
MacRimi
4079d4fd7c Update customizable_post_install.sh 2025-08-20 21:21:13 +02:00
MacRimi
ac48178369 Update lxc-privileged-to-unprivileged.sh 2025-08-20 21:10:05 +02:00
MacRimi
c2e9f038ee Update lxc-privileged-to-unprivileged.sh 2025-08-20 21:04:50 +02:00
MacRimi
70220d9829 Update lxc-privileged-to-unprivileged.sh 2025-08-20 21:03:57 +02:00
MacRimi
b9a1f378ec Update lxc_menu.sh 2025-08-20 20:59:58 +02:00
MacRimi
f6bc090a98 Update lxc_menu.sh 2025-08-20 20:55:43 +02:00
MacRimi
be519f3932 Create lxc_menu.sh 2025-08-20 20:46:51 +02:00
MacRimi
0a46f77555 Merge branch 'main' of https://github.com/MacRimi/ProxMenux 2025-08-20 20:43:56 +02:00
MacRimi
0e6cc0c7e5 Create lxc-privileged-to-unprivileged.sh 2025-08-20 20:43:54 +02:00
ProxMenuxBot
11cd425162 Update helpers_cache.json 2025-08-20 18:19:44 +00:00
MacRimi
aa269688d6 Update utilities_menu.sh 2025-08-20 19:51:54 +02:00
MacRimi
4c9e94768e Update upgrade_pve8_to_pve9.sh 2025-08-20 19:48:25 +02:00
MacRimi
581157fa82 Update upgrade_pve8_to_pve9.sh 2025-08-20 19:46:30 +02:00
MacRimi
e748e479cc Update utilities_menu.sh 2025-08-20 19:44:16 +02:00
MacRimi
5c9e4eea1e Update upgrade_pve8_to_pve9.sh 2025-08-20 19:41:09 +02:00
MacRimi
0c1189b233 Update pve8to9_check.sh 2025-08-20 19:37:14 +02:00
MacRimi
5ec9b82b4a Update upgrade_pve8_to_pve9.sh 2025-08-20 19:22:22 +02:00
MacRimi
c84ec533da Create pve8to9_check.sh 2025-08-20 19:22:12 +02:00
MacRimi
fb80c6ad7a Update upgrade_pve8_to_pve9.sh 2025-08-20 19:09:52 +02:00
ProxMenuxBot
2e3bfff6a4 Update helpers_cache.json 2025-08-19 18:18:30 +00:00
ProxMenuxBot
e96ce30891 Update helpers_cache.json 2025-08-19 12:26:48 +00:00
MacRimi
10de5b2e5f Update customizable_post_install.sh 2025-08-19 14:15:34 +02:00
MacRimi
1966081239 Update auto_post_install.sh 2025-08-19 14:13:56 +02:00
MacRimi
b48d806d53 Update system_utils.sh 2025-08-19 13:42:42 +02:00
MacRimi
97784d74e7 Update customizable_post_install.sh 2025-08-19 13:38:59 +02:00
ProxMenuxBot
c42e92b07d Update helpers_cache.json 2025-08-19 01:03:35 +00:00
MacRimi
2c52943b54 Update customizable_post_install.sh 2025-08-18 23:36:30 +02:00
MacRimi
4ccb1902cb Update customizable_post_install.sh 2025-08-18 23:30:59 +02:00
MacRimi
349b0572cd Update customizable_post_install.sh 2025-08-18 23:23:35 +02:00
MacRimi
87fae8a9eb Update customizable_post_install.sh 2025-08-18 23:18:26 +02:00
ProxMenuxBot
a77a097f47 Update helpers_cache.json 2025-08-18 18:20:28 +00:00
MacRimi
a84d81143e Update auto_post_install.sh 2025-08-18 15:10:23 +02:00
MacRimi
d9cee50ef3 Update auto_post_install.sh 2025-08-18 15:08:56 +02:00
MacRimi
0fc414e5e9 Update customizable_post_install.sh 2025-08-18 15:08:25 +02:00
ProxMenuxBot
e18f20ce4c Update helpers_cache.json 2025-08-18 12:29:04 +00:00
MacRimi
c12af4060c Update system_utils.sh 2025-08-18 14:20:47 +02:00
MacRimi
9992ea0dee Update customizable_post_install.sh 2025-08-18 14:15:19 +02:00
MacRimi
b8310f1c5d Update main_menu.sh 2025-08-18 09:37:06 +02:00
MacRimi
78f66af702 Update auto_post_install.sh 2025-08-17 15:16:25 +02:00
MacRimi
4d7564094e Update auto_post_install.sh 2025-08-17 15:14:46 +02:00
MacRimi
370f4694d1 Update customizable_post_install.sh 2025-08-17 15:14:20 +02:00
MacRimi
fc7c740691 Merge branch 'main' of https://github.com/MacRimi/ProxMenux 2025-08-17 15:05:42 +02:00
MacRimi
8e1f955519 Update auto_post_install.sh 2025-08-17 15:04:27 +02:00
MacRimi
aa3d16d981 Update auto_post_install.sh 2025-08-17 15:02:42 +02:00
MacRimi
5447f0e4df Update auto_post_install.sh 2025-08-17 15:00:06 +02:00
MacRimi
97294df208 Update customizable_post_install.sh 2025-08-17 14:21:37 +02:00
MacRimi
c6f53629da Update auto_post_install.sh 2025-08-17 14:20:58 +02:00
MacRimi
fcba907658 Update upgrade_pve8_to_pve9.sh 2025-08-17 13:24:32 +02:00
MacRimi
f481df7b8d Update configure_igpu_lxc.sh 2025-08-17 13:08:39 +02:00
MacRimi
81079a35d9 Update uninstall-tools.sh 2025-08-17 11:23:13 +02:00
MacRimi
bda344c382 Update uninstall-tools.sh 2025-08-17 11:01:21 +02:00
MacRimi
c4ebc396af Update customizable_post_install.sh 2025-08-17 10:36:03 +02:00
MacRimi
4cdbf1231b Update customizable_post_install.sh 2025-08-16 18:32:19 +02:00
MacRimi
92db58a9f6 Update auto_post_install.sh 2025-08-16 18:31:10 +02:00
MacRimi
5f07f47308 Update configure_igpu_lxc.sh 2025-08-16 18:12:14 +02:00
MacRimi
2132ae79a6 Update configure_igpu_lxc.sh 2025-08-16 17:34:34 +02:00
MacRimi
bda7834a4f Update disk-passthrough_ct.sh 2025-08-16 17:22:33 +02:00
MacRimi
7693f313c4 Update disk-passthrough_ct.sh 2025-08-16 17:13:45 +02:00
MacRimi
d2200a64e0 Update upgrade_pve8_to_pve9.sh 2025-08-16 17:04:59 +02:00
MacRimi
a5c46ab837 Update upgrade_pve8_to_pve9.sh 2025-08-16 16:43:44 +02:00
MacRimi
a389282e23 Update upgrade_pve8_to_pve9.sh 2025-08-16 11:54:11 +02:00
MacRimi
1f90b5b739 Update upgrade_pve8_to_pve9.sh 2025-08-16 11:46:09 +02:00
MacRimi
2c59500046 Update configure_igpu_lxc.sh 2025-08-16 11:15:41 +02:00
MacRimi
a59a056e12 Update configure_igpu_lxc.sh 2025-08-16 11:11:43 +02:00
ProxMenuxBot
235364013b Update helpers_cache.json 2025-08-16 01:03:53 +00:00
MacRimi
1049ac6eac Update upgrade_pve8_to_pve9.sh 2025-08-15 12:15:42 +02:00
MacRimi
04dc7af25c Create remove-banner-pve9.sh 2025-08-15 12:06:54 +02:00
MacRimi
f62ea3ad04 update remove-banner-pve9.sh 2025-08-15 12:05:24 +02:00
MacRimi
14c75f2cd9 Update proxmox-upgrade-pve8-to-pve9-manual-guide.sh 2025-08-15 11:15:17 +02:00
MacRimi
9ae68b9653 Update customizable_post_install.sh 2025-08-15 10:22:57 +02:00
MacRimi
b7ab4c4568 Update customizable_post_install.sh 2025-08-15 10:13:44 +02:00
MacRimi
7d0b3a0c87 Update system_utils.sh 2025-08-15 09:55:22 +02:00
MacRimi
0d38f7f290 Merge branch 'main' of https://github.com/MacRimi/ProxMenux 2025-08-15 09:54:02 +02:00
MacRimi
12e5ef4231 Update customizable_post_install.sh 2025-08-15 09:54:00 +02:00
ProxMenuxBot
f3aa1f7414 Update helpers_cache.json 2025-08-15 01:06:47 +00:00
MacRimi
f2eaec6e02 Update upgrade_pve8_to_pve9.sh 2025-08-14 20:08:22 +02:00
MacRimi
0654a3ed55 Update upgrade_pve8_to_pve9.sh 2025-08-14 20:03:47 +02:00
MacRimi
9a27138d96 Update proxmox-upgrade-pve8-to-pve9-manual-guide.sh 2025-08-14 19:46:13 +02:00
MacRimi
b3c9f71c02 Update proxmox-upgrade-pve8-to-pve9-manual-guide.sh 2025-08-14 19:41:22 +02:00
MacRimi
e9c9b957db Delete upgrade_pve8_topve9.sh 2025-08-14 19:39:36 +02:00
MacRimi
29cdf6fa48 Create upgrade_pve8_to_pve9.sh 2025-08-14 19:39:12 +02:00
MacRimi
8466a8e21e Update upgrade_pve8_topve9.sh 2025-08-14 19:37:15 +02:00
MacRimi
1523b6b8a8 Update proxmox-upgrade-pve8-to-pve9-manual-guide.sh 2025-08-14 19:34:42 +02:00
MacRimi
33205e1008 Create upgrade_pve8_topve9.sh 2025-08-14 19:24:29 +02:00
MacRimi
537af385f8 Update main menu PVE 9 2025-08-14 17:58:59 +02:00
MacRimi
7259b0a850 Update install_proxmenux.sh 2025-08-14 17:52:57 +02:00
MacRimi
11fbfda6bf Update cache.json 2025-08-13 22:46:58 +02:00
MacRimi
4f0353d0fb Create proxmox-upgrade-pve8-to-pve9-manual-guide.sh 2025-08-13 22:46:38 +02:00
MacRimi
a605d68d73 Merge branch 'main' of https://github.com/MacRimi/ProxMenux 2025-08-13 20:29:26 +02:00
MacRimi
237b7fbf1b Update cache.json 2025-08-13 20:29:24 +02:00
ProxMenuxBot
4a7e21f6b4 Update helpers_cache.json 2025-08-13 18:19:42 +00:00
MacRimi
b7017573b8 Update update-pve8.sh 2025-08-13 19:45:31 +02:00
MacRimi
a98b087c5d Update log2RAM 2025-08-13 15:55:22 +02:00
MacRimi
161c840136 Update utils.sh 2025-08-11 08:57:26 +02:00
MacRimi
4dd2abc202 Update utils.sh 2025-08-10 21:56:07 +02:00
MacRimi
cc0e9f61a7 Update utils.sh 2025-08-10 21:43:05 +02:00
MacRimi
21a658f1f4 Update utils.sh 2025-08-10 21:23:19 +02:00
MacRimi
b99f391c2a Merge branch 'main' of https://github.com/MacRimi/ProxMenux 2025-08-10 21:09:25 +02:00
MacRimi
9abe25b91a Update README.md 2025-08-10 21:09:01 +02:00
ProxMenuxBot
2531fc6dac Update helpers_cache.json 2025-08-09 12:25:42 +00:00
ProxMenuxBot
e5551cb179 Update helpers_cache.json 2025-08-09 01:05:21 +00:00
MacRimi
4728b7a8b7 Update utils.sh 2025-08-08 18:26:32 +02:00
ProxMenuxBot
2863921e15 Update helpers_cache.json 2025-08-08 12:29:54 +00:00
ProxMenuxBot
b93668edfe Update helpers_cache.json 2025-08-08 06:23:01 +00:00
52 changed files with 16957 additions and 416 deletions

View File

@@ -1,3 +1,104 @@
## 2025-01-10
### New version v1.1.6
![Shared Resources Menu](https://macrimi.github.io/ProxMenux/share/main-menu.png)
### Added
- **New Menu: Mount and Share Manager**
Introduced a comprehensive new menu for managing shared resources between Proxmox host and LXC containers:
**Host Configuration Options:**
1. **Configure NFS Shared on Host** - Add, view, and remove NFS shared resources on the Proxmox server with automatic export management
2. **Configure Samba Shared on Host** - Add, view, and remove Samba/CIFS shared resources on the Proxmox server with share configuration
3. **Configure Local Shared on Host** - Create and manage local shared directories with proper permissions on the Proxmox host
**LXC Integration Options:**
4. **Configure LXC Mount Points (Host ↔ Container)** - **Core feature** that enables mounting host directories into LXC containers with automatic permission handling. Includes the ability to **view existing mount points** for each container in a clear, organized way and **remove mount points** with proper verification that the process completed successfully. Especially optimized for **unprivileged containers** where UID/GID mapping is critical.
5. **Configure NFS Client in LXC** - Set up NFS client inside privileged containers
6. **Configure Samba Client in LXC** - Set up Samba client inside privileged containers
7. **Configure NFS Server in LXC** - Install NFS server inside privileged containers
8. **Configure Samba Server in LXC** - Install Samba server inside privileged containers
**Documentation & Support:**
- **Help & Info (commands)** - Comprehensive guides with step-by-step manual instructions for all sharing scenarios
The entire system is built around the **LXC Mount Points** functionality, which automatically detects filesystem types, handles permission mapping between host and container users, and provides seamless integration for both privileged and unprivileged containers.
---
### Improved
- **Log2RAM Auto-Detection Enhancement**
In the automatic post-install script, the Log2RAM installation function now prompts the user when automatic disk ssd/m2 detection fails.
This ensures Log2RAM can still be installed on systems where automatic disk detection doesn't work properly.
---
### Fixed
- **Proxmox Update Repository Verification**
Fixed an issue in the Proxmox update function where empty repository source files would cause errors during conflict verification. The function now properly handles empty `/etc/apt/sources.list.d/` files without throwing false warnings.
Thanks to **@JF_Car** for reporting this issue.
---
### Acknowledgments
Special thanks to **@JF_Car**, **@ghosthvj**, and **@jonatanc** for their testing, valuable feedback, and suggestions that helped refine the shared resources functionality and improve the overall user experience.
## 2025-08-20
### New version v1.1.5
### Added
- **New Script: Upgrade PVE 8 to PVE 9**
Added a full upgrade tool located under `Utilities and Tools`. It provides:
1. **Automatic upgrade** from PVE 8 to 9
2. **Interactive upgrade** with step-by-step confirmations
3. **Check-only mode** using `check-pve8to9`
4. **Manual instructions** shown in order for users who prefer to upgrade manually
- **New Tools in System Utilities**
- [`s-tui`](https://github.com/amanusk/s-tui): Terminal-based CPU monitoring with graphs
- [`intel-gpu-tools`](https://gitlab.freedesktop.org/drm/igt-gpu-tools): Useful for Intel GPU diagnostics
---
### Improved
- **APT Upgrade Handling**
The PVE upgrade function now blocks the process if any package prompts for manual confirmation. This avoids partial upgrades and ensures consistency.
- **Network Optimization (sysctl)**
- Obsolete kernel parameters removed (e.g., `tcp_tw_recycle`, `nf_conntrack_helper`) to prevent warnings in **Proxmox 9 / kernel 6.14**
- Now generates only valid, up-to-date sysctl parameters
- **AMD CPU Patch Handling**
- Now applies correct `idle=nomwait` and KVM options (`ignore_msrs=1`, `report_ignored_msrs=0`)
- Expected warning is now documented and safely handled for stability with Ryzen/EPYC
- **Timezone & NTP Fixes**
- Automatically detects timezone using public IP geolocation
- Falls back to UTC if detection fails
- Restarts Postfix after timezone set → resolves `/var/spool/postfix/etc/localtime` mismatch warning
- **Repository & Package Installer Logic**
- Now verifies that working repositories exist before installing any package
- If none are available, adds a fallback **Debian stable** repository
- Replaces deprecated `mlocate` with `plocate` (compatible with Debian 13 and Proxmox 9)
- **Improved Logs and User Feedback**
- Actions that fail now provide precise messages (instead of falsely marking as success)
- Helps users clearly understand what's been applied or skipped
## 2025-08-06
### New version v1.1.4

View File

@@ -59,7 +59,7 @@ Then, follow the on-screen options to manage your Proxmox server efficiently.
## 📌 System Requirements
🖥 **Compatible with:**
- Proxmox VE 8.x**
- Proxmox VE 8.x and 9.x
📦 **Dependencies:**
- `bash`, `curl`, `wget`, `jq`, `whiptail`, `python3-venv` (These dependencies are installed automatically during setup.)

View File

@@ -106,12 +106,12 @@ uninstall_proxmenu() {
local force_clean="$2"
if [ "$force_clean" != "force" ]; then
if ! whiptail --title "Uninstall ProxMenu" --yesno "Are you sure you want to uninstall ProxMenu?" 10 60; then
if ! whiptail --title "Uninstall ProxMenux" --yesno "Are you sure you want to uninstall ProxMenux?" 10 60; then
return 1
fi
fi
echo "Uninstalling ProxMenu..."
echo "Uninstalling ProxMenux..."
if [ -f "$VENV_PATH/bin/activate" ]; then
echo "Removing googletrans and virtual environment..."
@@ -151,7 +151,7 @@ uninstall_proxmenu() {
sed -i '/This system is optimised by: ProxMenux/d' /etc/motd
fi
echo "ProxMenu has been uninstalled."
echo "ProxMenux has been uninstalled."
return 0
}
@@ -221,7 +221,7 @@ show_progress() {
local total="$2"
local message="$3"
echo -e "\n${BOLD}${BL}${TAB}Installing ProxMenu: Step $step of $total${CL}"
echo -e "\n${BOLD}${BL}${TAB}Installing ProxMenux: Step $step of $total${CL}"
echo
msg_info2 "$message"
}
@@ -526,6 +526,7 @@ show_installation_options() {
"1" "Normal Version (English only)" 3>&1 1>&2 2>&3)
if [ -z "$INSTALL_TYPE" ]; then
show_proxmenux_logo
msg_warn "Installation cancelled."
exit 1
fi
@@ -535,6 +536,7 @@ show_installation_options() {
"2" "Translation Version (Multi-language support)" 3>&1 1>&2 2>&3)
if [ -z "$INSTALL_TYPE" ]; then
show_proxmenux_logo
msg_warn "Installation cancelled."
exit 1
fi
@@ -543,6 +545,7 @@ show_installation_options() {
if [ -z "$INSTALL_TYPE" ]; then
show_proxmenux_logo
msg_warn "Installation cancelled."
exit 1
fi
@@ -550,12 +553,14 @@ show_installation_options() {
# For new installations, show confirmation with details
if [ "$current_install_type" = "none" ]; then
if ! show_installation_confirmation "$INSTALL_TYPE"; then
show_proxmenux_logo
msg_warn "Installation cancelled."
exit 1
fi
fi
if ! handle_installation_change "$current_install_type" "$INSTALL_TYPE"; then
show_proxmenux_logo
msg_warn "Installation cancelled."
exit 1
fi
@@ -594,4 +599,4 @@ if [ "$(id -u)" -ne 0 ]; then
fi
cleanup_corrupted_files
install_proxmenu
install_proxmenu

View File

@@ -2132,6 +2132,439 @@
"de": "Zum Beenden von iptraf-ng, x drücken",
"it": "Per uscire da iptraf-ng, premi x",
"pt": "Para sair do iptraf-ng, pressione x"
},
"Proxmox VE 8 to 9 Manual Upgrade Guide": {
"es": "Guía de actualización manual de Proxmox VE 8 a 9",
"fr": "Guide de mise à niveau manuelle de Proxmox VE 8 vers 9",
"de": "Manuelles Upgrade-Handbuch: Proxmox VE 8 auf 9",
"it": "Guida all'aggiornamento manuale da Proxmox VE 8 a 9",
"pt": "Guia de atualização manual do Proxmox VE 8 para 9"
},
"Source:": {
"es": "Fuente:",
"fr": "Source :",
"de": "Quelle:",
"it": "Fonte:",
"pt": "Fonte:"
},
"IMPORTANT PREREQUISITES:": {
"es": "REQUISITOS PREVIOS IMPORTANTES:",
"fr": "PRÉREQUIS IMPORTANTS :",
"de": "WICHTIGE VORAUSSETZUNGEN:",
"it": "PREREQUISITI IMPORTANTI:",
"pt": "PRÉ-REQUISITOS IMPORTANTES:"
},
"System must be updated to latest PVE 8.4+ before starting": {
"es": "El sistema debe estar actualizado a la versión PVE 8.4+ antes de comenzar",
"fr": "Le système doit être mis à jour vers PVE 8.4+ avant de commencer",
"de": "Das System muss vor dem Start auf PVE 8.4+ aktualisiert sein",
"it": "Il sistema deve essere aggiornato a PVE 8.4+ prima di iniziare",
"pt": "O sistema deve estar atualizado para PVE 8.4+ antes de começar"
},
"Use SSH or terminal access (SSH recommended)": {
"es": "Use acceso por SSH o terminal (se recomienda SSH)",
"fr": "Utilisez un accès SSH ou terminal (SSH recommandé)",
"de": "SSH- oder Terminalzugang verwenden (SSH empfohlen)",
"it": "Usa accesso SSH o terminale (consigliato SSH)",
"pt": "Use acesso por SSH ou terminal (SSH recomendado)"
},
"Use tmux or screen to avoid interruptions": {
"es": "Use tmux o screen para evitar interrupciones",
"fr": "Utilisez tmux ou screen pour éviter les interruptions",
"de": "Verwenden Sie tmux oder screen, um Unterbrechungen zu vermeiden",
"it": "Usa tmux o screen per evitare interruzioni",
"pt": "Use tmux ou screen para evitar interrupções"
},
"Have valid backups of all VMs and containers": {
"es": "Tenga copias de seguridad válidas de todas las VMs y contenedores",
"fr": "Ayez des sauvegardes valides de toutes les VM et conteneurs",
"de": "Halten Sie gültige Backups aller VMs und Container bereit",
"it": "Avere backup validi di tutte le VM e i container",
"pt": "Tenha backups válidos de todas as VMs e contêineres"
},
"At least 5GB free space on root filesystem": {
"es": "Al menos 5 GB de espacio libre en el sistema de archivos raíz",
"fr": "Au moins 5 Go despace libre sur le système de fichiers racine",
"de": "Mindestens 5 GB freier Speicherplatz auf dem Root-Dateisystem",
"it": "Almeno 5 GB di spazio libero sul filesystem root",
"pt": "Pelo menos 5 GB de espaço livre no sistema de arquivos raiz"
},
"Do not run the upgrade from the Web UI virtual console (it will disconnect)": {
"es": "No ejecute la actualización desde la consola virtual de la interfaz web (se desconectará)",
"fr": "Nexécutez pas la mise à niveau depuis la console virtuelle de linterface Web (elle se déconnectera)",
"de": "Führen Sie das Upgrade nicht über die virtuelle Konsole der Web-UI aus (die Verbindung wird getrennt)",
"it": "Non eseguire laggiornamento dalla console virtuale dellinterfaccia Web (si disconnetterà)",
"pt": "Não execute a atualização pelo console virtual da interface Web (a conexão será interrompida)"
},
"Update system to latest PVE 8.4+ (if not done already):": {
"es": "Actualizar el sistema a PVE 8.4+ (si aún no se ha hecho):",
"fr": "Mettre à jour le système vers PVE 8.4+ (si ce nest pas déjà fait) :",
"de": "System auf PVE 8.4+ aktualisieren (falls noch nicht geschehen):",
"it": "Aggiornare il sistema a PVE 8.4+ (se non già fatto):",
"pt": "Atualizar o sistema para PVE 8.4+ (se ainda não foi feito):"
},
"Or use ProxMenux update function": {
"es": "O use la función de actualización de ProxMenux",
"fr": "Ou utilisez la fonction de mise à jour de ProxMenux",
"de": "Oder verwenden Sie die Aktualisierungsfunktion von ProxMenux",
"it": "Oppure usa la funzione di aggiornamento di ProxMenux",
"pt": "Ou use a função de atualização do ProxMenux"
},
"Verify PVE version (must be 8.4.1 or newer):": {
"es": "Verificar la versión de PVE (debe ser 8.4.1 o superior):",
"fr": "Vérifier la version de PVE (doit être 8.4.1 ou plus récente) :",
"de": "PVE-Version prüfen (muss 8.4.1 oder neuer sein):",
"it": "Verificare la versione di PVE (deve essere 8.4.1 o successiva):",
"pt": "Verificar a versão do PVE (deve ser 8.4.1 ou superior):"
},
"If this node runs hyper-converged Ceph: ensure Ceph is 19.x (Squid) BEFORE upgrading PVE.": {
"es": "Si este nodo ejecuta Ceph hiperconvergente: asegúrese de que Ceph sea 19.x (Squid) ANTES de actualizar PVE.",
"fr": "Si ce nœud exécute un Ceph hyperconvergé : assurez-vous que Ceph est en 19.x (Squid) AVANT de mettre à niveau PVE.",
"de": "Wenn dieser Knoten hyperkonvergentes Ceph betreibt: Stellen Sie sicher, dass Ceph 19.x (Squid) ist, BEVOR Sie PVE aktualisieren.",
"it": "Se questo nodo esegue Ceph iperconvergente: assicurarsi che Ceph sia 19.x (Squid) PRIMA di aggiornare PVE.",
"pt": "Se este nó executa Ceph hiperconvergente: certifique-se de que o Ceph esteja em 19.x (Squid) ANTES de atualizar o PVE."
},
"If not 19.x, upgrade Ceph (Reef→Squid) first per the official guide:": {
"es": "Si no es 19.x, actualice primero Ceph (Reef→Squid) según la guía oficial:",
"fr": "Si ce nest pas 19.x, mettez dabord à niveau Ceph (Reef→Squid) selon le guide officiel :",
"de": "Wenn nicht 19.x, aktualisieren Sie Ceph zuerst (Reef→Squid) gemäß der offiziellen Anleitung:",
"it": "Se non è 19.x, aggiornare prima Ceph (Reef→Squid) secondo la guida ufficiale:",
"pt": "Se não for 19.x, atualize primeiro o Ceph (Reef→Squid) conforme o guia oficial:"
},
"Run upgrade checklist script:": {
"es": "Ejecutar el script de la lista de verificación de actualización:",
"fr": "Exécuter le script de liste de vérification de mise à niveau :",
"de": "Upgrade-Checklisten-Skript ausführen:",
"it": "Eseguire lo script della checklist di aggiornamento:",
"pt": "Executar o script da lista de verificação de atualização:"
},
"If it warns about 'systemd-boot' meta-package, remove it:": {
"es": "Si advierte sobre el metapaquete 'systemd-boot', elimínelo:",
"fr": "Sil signale le méta-paquet « systemd-boot », supprimez-le :",
"de": "Wenn vor dem Metapaket „systemd-boot“ gewarnt wird, entfernen Sie es:",
"it": "Se avvisa del metapacchetto 'systemd-boot', rimuoverlo:",
"pt": "Se alertar sobre o metapacote 'systemd-boot', remova-o:"
},
"Start terminal multiplexer (recommended):": {
"es": "Iniciar un multiplexor de terminal (recomendado):",
"fr": "Démarrer un multiplexeur de terminal (recommandé) :",
"de": "Terminal-Multiplexer starten (empfohlen):",
"it": "Avviare un multiplexer di terminale (consigliato):",
"pt": "Iniciar um multiplexador de terminal (recomendado):"
},
"# Recommended: avoids disconnection during upgrade": {
"es": "# Recomendado: evita desconexiones durante la actualización",
"fr": "# Recommandé : évite les déconnexions pendant la mise à niveau",
"de": "# Empfohlen: vermeidet Verbindungsabbrüche während des Upgrades",
"it": "# Consigliato: evita disconnessioni durante laggiornamento",
"pt": "# Recomendado: evita desconexões durante a atualização"
},
"# Alternative if you prefer screen": {
"es": "# Alternativa si prefiere screen",
"fr": "# Alternative si vous préférez screen",
"de": "# Alternative, wenn Sie screen bevorzugen",
"it": "# Alternativa se preferisci screen",
"pt": "# Alternativa se preferir screen"
},
"Update Debian repositories to Trixie:": {
"es": "Actualizar los repositorios de Debian a Trixie:",
"fr": "Mettre à jour les dépôts Debian vers Trixie :",
"de": "Debian-Repositories auf Trixie aktualisieren:",
"it": "Aggiornare i repository Debian a Trixie:",
"pt": "Atualizar os repositórios Debian para Trixie:"
},
"Update PVE enterprise repository (Only if using enterprise):": {
"es": "Actualizar el repositorio empresarial de PVE (solo si usa enterprise):",
"fr": "Mettre à jour le dépôt entreprise de PVE (uniquement si vous utilisez enterprise) :",
"de": "PVE-Enterprise-Repository aktualisieren (nur bei Verwendung von Enterprise):",
"it": "Aggiornare il repository enterprise di PVE (solo se si usa enterprise):",
"pt": "Atualizar o repositório enterprise do PVE (apenas se usar enterprise):"
},
"Skip this step if using no-subscription repository": {
"es": "Omita este paso si usa el repositorio sin suscripción",
"fr": "Ignorez cette étape si vous utilisez le dépôt sans abonnement",
"de": "Überspringen Sie diesen Schritt, wenn Sie das No-Subscription-Repository verwenden",
"it": "Saltare questo passaggio se si usa il repository senza sottoscrizione",
"pt": "Pule esta etapa se usar o repositório sem assinatura"
},
"Add new PVE 9 enterprise repository (deb822 format) (Only if using enterprise):": {
"es": "Agregar el nuevo repositorio empresarial de PVE 9 (formato deb822) (solo si usa enterprise):",
"fr": "Ajouter le nouveau dépôt entreprise de PVE 9 (format deb822) (uniquement si vous utilisez enterprise) :",
"de": "Neues PVE-9-Enterprise-Repository (deb822-Format) hinzufügen (nur bei Enterprise):",
"it": "Aggiungere il nuovo repository enterprise di PVE 9 (formato deb822) (solo se si usa enterprise):",
"pt": "Adicionar o novo repositório enterprise do PVE 9 (formato deb822) (apenas se usar enterprise):"
},
"Only if using enterprise subscription": {
"es": "Solo si utiliza suscripción empresarial",
"fr": "Uniquement si vous avez un abonnement entreprise",
"de": "Nur bei vorhandener Enterprise-Abonnement",
"it": "Solo se si dispone di una sottoscrizione enterprise",
"pt": "Apenas se você tiver assinatura enterprise"
},
"OR add new PVE 9 no-subscription repository:": {
"es": "O agregar el nuevo repositorio de PVE 9 sin suscripción:",
"fr": "OU ajouter le nouveau dépôt PVE 9 sans abonnement :",
"de": "ODER das neue PVE-9-No-Subscription-Repository hinzufügen:",
"it": "OPPURE aggiungere il nuovo repository PVE 9 senza sottoscrizione:",
"pt": "OU adicionar o novo repositório PVE 9 sem assinatura:"
},
"Only if using no-subscription repository": {
"es": "Solo si usa el repositorio sin suscripción",
"fr": "Uniquement si vous utilisez le dépôt sans abonnement",
"de": "Nur bei Verwendung des No-Subscription-Repository",
"it": "Solo se si usa il repository senza sottoscrizione",
"pt": "Apenas se usar o repositório sem assinatura"
},
"Refresh APT index and verify repositories:": {
"es": "Actualizar el índice de APT y verificar los repositorios:",
"fr": "Actualiser lindex APT et vérifier les dépôts :",
"de": "APT-Index aktualisieren und Repositories prüfen:",
"it": "Aggiornare lindice APT e verificare i repository:",
"pt": "Atualizar o índice do APT e verificar os repositórios:"
},
"Ensure there are no errors and that proxmox-ve candidate shows 9.x": {
"es": "Asegúrese de que no haya errores y de que el candidato de proxmox-ve sea 9.x",
"fr": "Assurez-vous quil ny a aucune erreur et que le candidat proxmox-ve indique 9.x",
"de": "Stellen Sie sicher, dass keine Fehler auftreten und der proxmox-ve-Kandidat 9.x anzeigt",
"it": "Assicurarsi che non vi siano errori e che il candidato di proxmox-ve sia 9.x",
"pt": "Certifique-se de que não há erros e que o candidato proxmox-ve mostre 9.x"
},
"Update Ceph repository (Only if using Ceph):": {
"es": "Actualizar el repositorio de Ceph (solo si usa Ceph):",
"fr": "Mettre à jour le dépôt Ceph (uniquement si vous utilisez Ceph) :",
"de": "Ceph-Repository aktualisieren (nur bei Verwendung von Ceph):",
"it": "Aggiornare il repository di Ceph (solo se si usa Ceph):",
"pt": "Atualizar o repositório do Ceph (apenas se usar Ceph):"
},
"Use enterprise URL if you have subscription.": {
"es": "Use la URL enterprise si dispone de suscripción.",
"fr": "Utilisez lURL enterprise si vous avez un abonnement.",
"de": "Verwenden Sie die Enterprise-URL, wenn Sie ein Abonnement haben.",
"it": "Usa lURL enterprise se hai una sottoscrizione.",
"pt": "Use a URL enterprise se tiver assinatura."
},
"Remove old repository files:": {
"es": "Eliminar los archivos de repositorio antiguos:",
"fr": "Supprimer les anciens fichiers de dépôt :",
"de": "Alte Repository-Dateien entfernen:",
"it": "Rimuovere i vecchi file di repository:",
"pt": "Remover arquivos antigos de repositório:"
},
"Also comment any remaining 'bookworm' entries in *.list if present.": {
"es": "Comente también cualquier entrada restante de bookworm en *.list si existe.",
"fr": "Commentez également toute entrée bookworm restante dans *.list si présente.",
"de": "Kommentieren Sie außerdem verbleibende bookworm-Einträge in *.list, falls vorhanden.",
"it": "Commentare anche eventuali voci bookworm rimanenti in *.list se presenti.",
"pt": "Comente também quaisquer entradas bookworm restantes em *.list, se houver."
},
"Update package index:": {
"es": "Actualizar el índice de paquetes:",
"fr": "Mettre à jour lindex des paquets :",
"de": "Paketindex aktualisieren:",
"it": "Aggiornare lindice dei pacchetti:",
"pt": "Atualizar o índice de pacotes:"
},
"Disable kernel audit messages (optional but recommended):": {
"es": "Desactivar los mensajes de auditoría del kernel (opcional pero recomendado):",
"fr": "Désactiver les messages daudit du noyau (optionnel mais recommandé) :",
"de": "Kernel-Audit-Meldungen deaktivieren (optional, aber empfohlen):",
"it": "Disabilitare i messaggi di audit del kernel (opzionale ma consigliato):",
"pt": "Desativar as mensagens de auditoria do kernel (opcional, mas recomendado):"
},
"Start the main system upgrade:": {
"es": "Iniciar la actualización principal del sistema:",
"fr": "Démarrer la mise à niveau principale du système :",
"de": "Hauptsystem-Upgrade starten:",
"it": "Avviare laggiornamento principale del sistema:",
"pt": "Iniciar a atualização principal do sistema:"
},
"This will take time. Answer prompts carefully - see notes below.": {
"es": "Esto llevará tiempo. Responda a las indicaciones con cuidado (vea las notas a continuación).",
"fr": "Cela prendra du temps. Répondez aux invites avec attention (voir les notes ci-dessous).",
"de": "Dies wird einige Zeit dauern. Beantworten Sie Rückfragen sorgfältig (siehe Hinweise unten).",
"it": "Questo richiederà tempo. Rispondi con attenzione alle richieste (vedi note sotto).",
"pt": "Isso levará tempo. Responda às solicitações com atenção (veja as notas abaixo)."
},
"UPGRADE PROMPTS - RECOMMENDED ANSWERS:": {
"es": "INDICACIONES DE ACTUALIZACIÓN - RESPUESTAS RECOMENDADAS:",
"fr": "INVITES DE MISE À NIVEAU - RÉPONSES RECOMMANDÉES :",
"de": "UPGRADE-AUFFORDERUNGEN EMPFOHLENE ANTWORTEN:",
"it": "PROMPT DI AGGIORNAMENTO - RISPOSTE CONSIGLIATE:",
"pt": "PROMPTS DE ATUALIZAÇÃO - RESPOSTAS RECOMENDADAS:"
},
"Keep current version (N)": {
"es": "Mantener la versión actual (N)",
"fr": "Conserver la version actuelle (N)",
"de": "Aktuelle Version beibehalten (N)",
"it": "Mantieni la versione corrente (N)",
"pt": "Manter a versão atual (N)"
},
"Install maintainer's version (Y)": {
"es": "Instalar la versión del mantenedor del paquete (Y)",
"fr": "Installer la version du mainteneur du paquet (Y)",
"de": "Version des Paketbetreuers installieren (Y)",
"it": "Installare la versione del maintainer del pacchetto (Y)",
"pt": "Instalar a versão do mantenedor do pacote (Y)"
},
"Keep current version (N) if modified": {
"es": "Mantener la versión actual (N) si está modificada",
"fr": "Conserver la version actuelle (N) si elle a été modifiée",
"de": "Aktuelle Version beibehalten (N), falls angepasst",
"it": "Mantieni la versione corrente (N) se modificata",
"pt": "Manter a versão atual (N) se estiver modificada"
},
"Service restarts:": {
"es": "Reinicios de servicios:",
"fr": "Redémarrages de services :",
"de": "Neustarts von Diensten:",
"it": "Riavvii dei servizi:",
"pt": "Reinicializações de serviços:"
},
"Use default (Yes)": {
"es": "Usar la opción predeterminada (Sí)",
"fr": "Utiliser loption par défaut (Oui)",
"de": "Standardoption verwenden (Ja)",
"it": "Usare lopzione predefinita (Sì)",
"pt": "Usar a opção padrão (Sim)"
},
"Press 'q' to exit": {
"es": "Presione «q» para salir",
"fr": "Appuyez sur « q » pour quitter",
"de": "Drücken Sie „q“, um zu beenden",
"it": "Premi «q» per uscire",
"pt": "Pressione «q» para sair"
},
"If booting in EFI mode with root on LVM: install GRUB for EFI": {
"es": "Si inicia en modo EFI con root en LVM: instale GRUB para EFI",
"fr": "Si vous démarrez en mode EFI avec root sur LVM : installez GRUB pour EFI",
"de": "Wenn Sie im EFI-Modus mit Root auf LVM booten: GRUB für EFI installieren",
"it": "Se lavvio è in modalità EFI con root su LVM: installare GRUB per EFI",
"pt": "Se iniciar em modo EFI com root no LVM: instale o GRUB para EFI"
},
"Per official known issues; ensures proper boot after upgrade": {
"es": "Según las incidencias conocidas oficiales, garantiza un arranque correcto tras la actualización",
"fr": "Selon les problèmes connus officiels, cela garantit un démarrage correct après la mise à niveau",
"de": "Laut den offiziellen Known Issues sorgt dies für einen ordnungsgemäßen Start nach dem Upgrade",
"it": "Secondo le note ufficiali dei problemi noti, garantisce un avvio corretto dopo laggiornamento",
"pt": "De acordo com os problemas conhecidos oficiais, garante uma inicialização correta após a atualização"
},
"Run checklist again to verify upgrade:": {
"es": "Ejecutar de nuevo la lista de verificación para comprobar la actualización:",
"fr": "Relancer la liste de vérification pour valider la mise à niveau :",
"de": "Checkliste erneut ausführen, um das Upgrade zu verifizieren:",
"it": "Eseguire nuovamente la checklist per verificare laggiornamento:",
"pt": "Executar novamente a lista de verificação para validar a atualização:"
},
"Should show fewer or no issues": {
"es": "Debería mostrar menos problemas o ninguno",
"fr": "Devrait afficher moins de problèmes, voire aucun",
"de": "Sollte weniger oder keine Probleme anzeigen",
"it": "Dovrebbe mostrare meno problemi o nessuno",
"pt": "Deve mostrar menos problemas ou nenhum"
},
"Reboot the system:": {
"es": "Reiniciar el sistema:",
"fr": "Redémarrer le système :",
"de": "System neu starten:",
"it": "Riavviare il sistema:",
"pt": "Reiniciar o sistema:"
},
"After reboot, verify PVE version:": {
"es": "Después del reinicio, verificar la versión de PVE:",
"fr": "Après le redémarrage, vérifier la version de PVE :",
"de": "Nach dem Neustart die PVE-Version prüfen:",
"it": "Dopo il riavvio, verificare la versione di PVE:",
"pt": "Após reiniciar, verificar a versão do PVE:"
},
"Should show pve-manager/9.x.x": {
"es": "Debería mostrar pve-manager/9.x.x",
"fr": "Devrait afficher pve-manager/9.x.x",
"de": "Sollte pve-manager/9.x.x anzeigen",
"it": "Dovrebbe mostrare pve-manager/9.x.x",
"pt": "Deve mostrar pve-manager/9.x.x"
},
"Optional: Modernize repository sources:": {
"es": "Opcional: Modernizar las fuentes de repositorio:",
"fr": "Optionnel : Moderniser les sources des dépôts :",
"de": "Optional: Repository-Quellen modernisieren:",
"it": "Opzionale: Modernizzare le fonti del repository:",
"pt": "Opcional: Modernizar as fontes de repositório:"
},
"Converts to deb822; keeps .list backups as .bak": {
"es": "Convierte a deb822; mantiene copias .list como .bak",
"fr": "Convertit en deb822 ; conserve les sauvegardes .list en .bak",
"de": "Konvertiert zu deb822; behält .list-Backups als .bak bei",
"it": "Converte in deb822; mantiene i backup .list come .bak",
"pt": "Converte para deb822; mantém backups .list como .bak"
},
"CLUSTER UPGRADE NOTES:": {
"es": "NOTAS DE ACTUALIZACIÓN DEL CLÚSTER:",
"fr": "NOTES DE MISE À NIVEAU DU CLUSTER :",
"de": "HINWEISE ZUM CLUSTER-UPGRADE:",
"it": "NOTE DI AGGIORNAMENTO DEL CLUSTER:",
"pt": "NOTAS DE ATUALIZAÇÃO DO CLUSTER:"
},
"Upgrade one node at a time": {
"es": "Actualizar un nodo a la vez",
"fr": "Mettre à niveau un nœud à la fois",
"de": "Jeweils nur einen Knoten aktualisieren",
"it": "Aggiornare un nodo alla volta",
"pt": "Atualizar um nó por vez"
},
"Migrate VMs away from node being upgraded": {
"es": "Migrar las VMs fuera del nodo que se está actualizando",
"fr": "Migrer les VM hors du nœud en cours de mise à niveau",
"de": "VMs vom zu aktualisierenden Knoten weg migrieren",
"it": "Migrare le VM dal nodo in aggiornamento",
"pt": "Migrar as VMs para fora do nó em atualização"
},
"Wait for each node to complete before starting next": {
"es": "Esperar a que cada nodo termine antes de empezar con el siguiente",
"fr": "Attendre que chaque nœud soit terminé avant de commencer le suivant",
"de": "Warten, bis jeder Knoten abgeschlossen ist, bevor der nächste beginnt",
"it": "Attendere che ogni nodo termini prima di iniziare il successivo",
"pt": "Aguardar cada nó concluir antes de iniciar o próximo"
},
"HA groups will be migrated to HA rules automatically": {
"es": "Los grupos de HA se migrarán automáticamente a reglas de HA",
"fr": "Les groupes HA seront migrés automatiquement vers des règles HA",
"de": "HA-Gruppen werden automatisch in HA-Regeln migriert",
"it": "I gruppi HA verranno migrati automaticamente a regole HA",
"pt": "Grupos de HA serão migrados automaticamente para regras de HA"
},
"TROUBLESHOOTING:": {
"es": "SOLUCIÓN DE PROBLEMAS:",
"fr": "DÉPANNAGE :",
"de": "FEHLERBEHEBUNG:",
"it": "RISOLUZIONE DEI PROBLEMI:",
"pt": "SOLUÇÃO DE PROBLEMAS:"
},
"If upgrade fails:": {
"es": "Si la actualización falla:",
"fr": "Si la mise à niveau échoue :",
"de": "Wenn das Upgrade fehlschlägt:",
"it": "Se laggiornamento fallisce:",
"pt": "Se a atualização falhar:"
},
"If repositories error:": {
"es": "Si hay errores de repositorios:",
"fr": "En cas derreurs de dépôts :",
"de": "Bei Repository-Fehlern:",
"it": "In caso di errori dei repository:",
"pt": "Se houver erros nos repositórios:"
},
"If 'proxmox-ve' removal warning:": {
"es": "Si aparece advertencia de eliminación de proxmox-ve:",
"fr": "Si un avertissement de suppression de proxmox-ve apparaît :",
"de": "Bei Warnung zur Entfernung von proxmox-ve:",
"it": "Se compare un avviso di rimozione di proxmox-ve:",
"pt": "Se aparecer um aviso de remoção de proxmox-ve:"
},
"Emergency recovery:": {
"es": "Recuperación de emergencia:",
"fr": "Récupération durgence :",
"de": "Notfallwiederherstellung:",
"it": "Ripristino di emergenza:",
"pt": "Recuperação de emergência:"
}
}

View File

@@ -365,6 +365,22 @@
"notes": [],
"type": "ct"
},
{
"name": "Autocaliweb",
"slug": "autocaliweb",
"desc": "A modern web management system for eBooks, eComics and PDFs",
"script": "ct/autocaliweb.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/autocaliweb.sh",
"categories": [
13
],
"notes": [],
"type": "ct",
"default_credentials": {
"username": "admin",
"password": "admin123"
}
},
{
"name": "Baby Buddy",
"slug": "babybuddy",
@@ -498,7 +514,9 @@
"categories": [
13
],
"notes": [],
"notes": [
"Starting Booklore (Web UI) may take up to 2 minutes after a restart or fresh installation."
],
"type": "ct"
},
{
@@ -773,6 +791,20 @@
],
"type": "ct"
},
{
"name": "Copyparty",
"slug": "copyparty",
"desc": "Copyparty is a lightweight, portable HTTP file server with a browser-based interface. It supports drag-and-drop uploads, downloads, deduplication, media playback, and advanced search, making it ideal for quickly sharing and managing files.",
"script": "tools/addon/copyparty.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/addon/copyparty.sh",
"categories": [
11
],
"notes": [
"Execute within the Proxmox shell or in LXC"
],
"type": "addon"
},
{
"name": "Cosmos",
"slug": "cosmos",
@@ -899,6 +931,22 @@
"notes": [],
"type": "ct"
},
{
"name": "Debian 13",
"slug": "debian-13-vm",
"desc": "Debian 13 (Trixie) Linux is a distribution that emphasizes free software. It supports many hardware platforms",
"script": "vm/debian-13-vm.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-13-vm.sh",
"categories": [
2
],
"notes": [
"VM has no root password set. To login type in user `root` and just press enter",
"After installation, checkout: \u00b4https://github.com/community-scripts/ProxmoxVE/discussions/836\u00b4 for useful Debian commands",
"If you use Cloud-init, checkout after installation: \u00b4https://github.com/community-scripts/ProxmoxVE/discussions/272\u00b4"
],
"type": "vm"
},
{
"name": "Debian 12",
"slug": "debian-vm",
@@ -1008,7 +1056,7 @@
{
"name": "Docmost",
"slug": "docmost",
"desc": "Open-source collaborative wiki and documentation software Create, collaborate, and share knowledge seamlessly with Docmost. Ideal for managing your wiki, knowledge-base, documentation and a lot more.",
"desc": "Open-source collaborative wiki and documentation software. Create, collaborate, and share knowledge seamlessly with Docmost. Ideal for managing your wiki, knowledge-base, documentation and a lot more.",
"script": "ct/docmost.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/docmost.sh",
"categories": [
@@ -1274,7 +1322,9 @@
"categories": [
14
],
"notes": [],
"notes": [
"Flaresolverr is pinned to Version 3.3.25 because they add an breaking python package which doesn't work with debian 12.`"
],
"type": "ct"
},
{
@@ -1461,8 +1511,7 @@
9
],
"notes": [
"Execute within an existing LXC Console",
"WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing."
"Execute within an existing LXC Console (Debian / Ubuntu / Alpine supported)"
],
"type": "addon"
},
@@ -1643,6 +1692,21 @@
],
"type": "ct"
},
{
"name": "Healthchecks",
"slug": "healthchecks",
"desc": "Healthchecks is a cron job monitoring service. It listens for HTTP requests and email messages (\"pings\") from your cron jobs and scheduled tasks (\"checks\"). When a ping does not arrive on time, Healthchecks sends out alerts. Healthchecks comes with a web dashboard, API, 25+ integrations for delivering notifications, monthly email reports, WebAuthn 2FA support, team management features: projects, team members, read-only access.",
"script": "ct/healthchecks.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/healthchecks.sh",
"categories": [
9
],
"notes": [
"if you change your LXC-IP, you need to update /etc/caddy/Caddyfile & /opt/healthchecks/hc/local_settings.py",
"Show credentials: `cat ~/healthchecks.creds`"
],
"type": "ct"
},
{
"name": "Heimdall Dashboard",
"slug": "heimdall-dashboard",
@@ -1696,23 +1760,6 @@
"notes": [],
"type": "ct"
},
{
"name": "Home Assistant Core",
"slug": "homeassistant-core",
"desc": "A standalone installation of Home Assistant Core refers to a setup where the Home Assistant Core software is installed directly on a device or operating system, without the use of Docker containers. This provides a simpler, but less flexible and scalable solution, as the software is tightly coupled with the underlying system.",
"script": "ct/homeassistant-core.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/homeassistant-core.sh",
"categories": [
16
],
"notes": [
"If the LXC is created Privileged, the script will automatically set up USB passthrough.",
"Requires PVE 8.2.2 with kernel 6.8.4-3-pve or newer",
"Deprecation-Warning: This Core-based setup will be deprecated by August 2025. Use Home Assistant OS is strongly recommended to ensure long-term stability and updates.",
"config path: `/root/.homeassistant`"
],
"type": "ct"
},
{
"name": "Home Assistant Container",
"slug": "homeassistant",
@@ -1787,6 +1834,20 @@
],
"type": "ct"
},
{
"name": "HortusFox",
"slug": "hortusfox",
"desc": "HortusFox is a collaborative plant management system for plant enthusiasts. Manage, document and track your entire plant collection \u2013 self-hosted and privacy-friendly.",
"script": "ct/hortusfox.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/hortusfox.sh",
"categories": [
24
],
"notes": [
"Login Credentials : `cat ~/hortusfox.creds`"
],
"type": "ct"
},
{
"name": "Proxmox VE Host Backup",
"slug": "host-backup",
@@ -1848,6 +1909,7 @@
13
],
"notes": [
"Please be aware that Immich releases are pinned to specific versions until compatibility has been confirmed by the Community Scripts maintainers; as a result, the version installed by the helper script may not be the most current version of Immich",
"During installation, you will be prompted with the option to install Intel OpenVINO for hardware-accelerated machine-learning. If you opt in, increase your LXC RAM after installation, as OpenVINO is memory-intensive",
"HW-accelerated video transcoding is supported, but must be enabled in Immich Settings",
"To change upload location, edit 'IMMICH_MEDIA_LOCATION' in `/opt/immich/.env`, and create the symlink 'upload' in /opt/immich/app & /opt/immich/app/machine-learning to your new upload location",
@@ -2010,7 +2072,7 @@
"script": "ct/jenkins.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/jenkins.sh",
"categories": [
22
20
],
"notes": [],
"type": "ct"
@@ -2065,7 +2127,7 @@
"notes": [
"WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing.",
"Kasm needs swap (on Proxmox host) and activated FUSE to be installed successfully!",
"Show password: `cat ~/kasm.creds`"
"Show credentials: `cat ~/kasm.creds`"
],
"type": "ct"
},
@@ -2122,7 +2184,6 @@
],
"notes": [
"First start can take a few minutes",
"This script requires some extra steps after the installation, Please checkout the `https://github.com/community-scripts/ProxmoxVE/discussions/193`",
"When updating, if you had modified cache-ispn.xml: Re-apply your changes to the new file, otherwise leave it unchanged."
],
"type": "ct",
@@ -2198,7 +2259,7 @@
3
],
"notes": [
"After the initial installation: Enter your desired admin user and password and then click on Sign Up"
"For admin username and password type `cat ~/komodo.creds` inside LXC."
],
"type": "ct"
},
@@ -2226,6 +2287,18 @@
"notes": [],
"type": "ct"
},
{
"name": "Leantime",
"slug": "leantime",
"desc": "Leantime is a goals focused project management system for non-project managers. Building with ADHD, Autism, and dyslexia in mind. ",
"script": "ct/leantime.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/leantime.sh",
"categories": [
12
],
"notes": [],
"type": "ct"
},
{
"name": "Librespeed Rust",
"slug": "librespeed-rust",
@@ -2304,6 +2377,24 @@
"notes": [],
"type": "ct"
},
{
"name": "LiteLLM",
"slug": "litellm",
"desc": "LLM proxy to call 100+ LLMs in a unified interface & track spend, set budgets per virtual key/user",
"script": "ct/litellm.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/litellm.sh",
"categories": [
20
],
"notes": [
"Update master key in the config file"
],
"type": "ct",
"default_credentials": {
"username": "admin",
"password": "sk-1234"
}
},
{
"name": "lldap",
"slug": "lldap",
@@ -2454,6 +2545,26 @@
"notes": [],
"type": "ct"
},
{
"name": "MediaManager",
"slug": "mediamanager",
"desc": "A modern selfhosted media management system for your media library",
"script": "ct/mediamanager.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/mediamanager.sh",
"categories": [
14,
13
],
"notes": [
"During the installation, provide the email address of the first admin user",
"You're probably going to want to use a bind mount for the media directories"
],
"type": "ct",
"default_credentials": {
"username": "<email address>",
"password": "admin"
}
},
{
"name": "MediaMTX",
"slug": "mediamtx",
@@ -2939,11 +3050,11 @@
"notes": [
"This uses Docker under the hood, as this can not easily be installed bare-metal. ",
"The initial starting process can be take 1-2min. ",
"Application credentials: `cat /opt/.npm_pwd`"
"Application credentials: `cat /opt/.npm_pwd` - if file not exist in LXC check docker logs for password with `docker logs npmplus`"
],
"type": "ct",
"default_credentials": {
"username": "root",
"username": "admin@example.org",
"password": null
}
},
@@ -3207,7 +3318,9 @@
4,
2
],
"notes": [],
"notes": [
"If you use VLANs (default LAN is set to VLAN 999), make sure the Proxmox Linux Bridge is configured as VLAN-aware, otherwise the VM may fail to start."
],
"type": "vm"
},
{
@@ -3326,6 +3439,22 @@
"notes": [],
"type": "ct"
},
{
"name": "Palmr",
"slug": "palmr",
"desc": "Palmr is a fast and secure platform for sharing files, built with performance and privacy in mind.",
"script": "ct/palmr.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/palmr.sh",
"categories": [
11
],
"notes": [
"This LXC is very memory-hungry when updating; it requires at least 6GB RAM, but RAM may be reduced to as low as 2GB when running normally",
"To use a bind mount for storage, create symlinks to your mount for both `uploads` and `temp-uploads` in `/opt/palmr_data`",
"To use Palmr with a reverse proxy, uncomment `SECURE_SITE` in `/opt/palmr/apps/server/.env`"
],
"type": "ct"
},
{
"name": "PaperlessAI",
"slug": "paperless-ai",
@@ -3362,7 +3491,7 @@
12
],
"notes": [
"Show Login Credentials, type `update` in the LXC console",
"Show Login Credentials, type `cat ~/paperless-ngx.creds` in the LXC console",
"Script installs English as default OCR language. To install additional languages, use `apt-get install tesseract-ocr-[lang]`, where [lang] is the language code (e.g. `apt-get install tesseract-ocr-deu`)."
],
"type": "ct"
@@ -3414,6 +3543,23 @@
],
"type": "pve"
},
{
"name": "PBS 4 Upgrade",
"slug": "pbs4-upgrade",
"desc": "This script guides you through upgrading Proxmox Backup Server from version 3.x (Debian 12 Bookworm) to version 4.0 (Debian 13 Trixie). It adjusts the Debian base sources, configures PBS 4 repositories in deb822 format, updates enterprise/no-subscription/test repos, runs a full system upgrade, and finalizes with a reboot.",
"script": "tools/pve/pbs4-upgrade.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/pbs4-upgrade.sh",
"categories": [
1
],
"notes": [
"Execute this script directly on the PBS 3.x host as root.",
"Ensure you have a verified backup of /etc/proxmox-backup before starting.",
"Do not run this on an already upgraded PBS 4.x system.",
"A reboot is strongly recommended after upgrade to activate the new kernel and services."
],
"type": "pve"
},
{
"name": "PeaNUT",
"slug": "peanut",
@@ -3823,7 +3969,7 @@
],
"notes": [
"Set a root password if using autologin. This will be the PBS password. `passwd root`",
"Advanced Install is only possible without root password and root SSH access, you can configure this after installation."
"Advanced Install is only possible with disabled IPV6! Otherwise the installation sometimes stuck."
],
"type": "ct",
"default_credentials": {
@@ -3917,7 +4063,8 @@
],
"notes": [
"Create Proxmox-API-Token first: `https://github.com/rcourtman/Pulse?tab=readme-ov-file#creating-api-token`",
"After installation, access the web interface to configure your Proxmox connection details through the built-in setup wizard"
"After installation, access the web interface to configure your Proxmox connection details through the built-in setup wizard",
"Configure authentication in the Web UI => Settings => Security"
],
"type": "ct"
},
@@ -3963,7 +4110,11 @@
18
],
"notes": [],
"type": "ct"
"type": "ct",
"default_credentials": {
"username": "proxmox",
"password": "proxmox"
}
},
{
"name": "Radarr",
@@ -4069,6 +4220,32 @@
],
"type": "ct"
},
{
"name": "Redlib",
"slug": "alpine-redlib",
"desc": "An alternative private front-end to Reddit. Redlib hopes to provide an easier way to browse Reddit, without the ads, trackers, and bloat.",
"script": "ct/alpine-redlib.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/alpine-redlib.sh",
"categories": [
10
],
"notes": [],
"type": "ct"
},
{
"name": "Resilio Sync",
"slug": "resiliosync",
"desc": "Fast, reliable, and simple file sync and share solution, powered by P2P technology. Sync files across all your devices without storing them in the cloud.",
"script": "ct/resilio-sync.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/resilio-sync.sh",
"categories": [
11
],
"notes": [
"After free registration, you will receive a license keyfile to your email address. Upload it into any LXC directory and select on first run."
],
"type": "ct"
},
{
"name": "RevealJS",
"slug": "revealjs",
@@ -4108,7 +4285,8 @@
],
"notes": [
"Check our configuration guide for help: `https://github.com/community-scripts/ProxmoxVE/discussions/2388`",
"Login credentials: `cat ~/rustdesk.creds`"
"To set admin password on Debian, type `cd /var/lib/rustdesk-api && rustdesk-api reset-admin-pwd <yournewpasswordhere>` inside LXC.",
"To see admin password on Alpine, type `cat ~/rustdesk.creds` inside LXC."
],
"type": "ct"
},
@@ -4150,6 +4328,18 @@
],
"type": "pve"
},
{
"name": "SearXNG",
"slug": "searxng",
"desc": "SearXNG is a free internet metasearch engine which aggregates results from up to 215 search services. Users are neither tracked nor profiled. Additionally, SearXNG can be used over Tor for online anonymity.",
"script": "ct/searxng.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/searxng.sh",
"categories": [
0
],
"notes": [],
"type": "ct"
},
{
"name": "seelf",
"slug": "seelf",
@@ -4356,6 +4546,23 @@
],
"type": "ct"
},
{
"name": "Swizzin",
"slug": "swizzin",
"desc": "Swizzin is a light-weight, modular, and user-friendly seedbox solution for Debian-based servers. It allows for the easy installation and management of a wide variety of applications commonly used for torrenting and media management, such as rTorrent, Sonarr, Radarr, and Plex, all accessible through a command-line utility or a web-based dashboard.",
"script": "ct/swizzin.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/swizzin.sh",
"categories": [
13
],
"notes": [
"Installation might take a long time if choosing to install many apps. Be patient.",
"Swizzin is a management suite, not a single application. Use the 'box' command inside the container to install/manage individual apps like rTorrent, Sonarr, etc. A full list can be found in documentation.",
"It is very recommended to install at least the 'panel' for web access, and 'nginx' for easy access to other apps.",
"WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing."
],
"type": "ct"
},
{
"name": "Syncthing",
"slug": "syncthing",
@@ -4454,9 +4661,7 @@
"categories": [
5
],
"notes": [
"WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing."
],
"notes": [],
"type": "ct"
},
{
@@ -4527,6 +4732,20 @@
"notes": [],
"type": "ct"
},
{
"name": "Tracktor",
"slug": "tracktor",
"desc": "Tracktor is an open-source web application for comprehensive vehicle management.\nEasily track fuel consumption, maintenance, insurance, and regulatory documents for all your vehicles in one place.",
"script": "ct/tracktor.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/tracktor.sh",
"categories": [
9
],
"notes": [
"Please check and update the '/opt/tracktor/app/backend/.env' file if using behind reverse proxy."
],
"type": "ct"
},
{
"name": "Traefik",
"slug": "traefik",
@@ -4596,6 +4815,21 @@
],
"type": "turnkey"
},
{
"name": "twingate-connector",
"slug": "twingate-connector",
"desc": "Twingate Connectors are lightweight software components that establish secure, least-privileged access between private network resources and authorized users without exposing the network to the internet. They act as outbound-only bridges between your protected resources and the Twingate infrastructure, ensuring zero-trust access without the need for a VPN.",
"script": "ct/twingate-connector.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/twingate-connector.sh",
"categories": [
4
],
"notes": [
"You can get your Twingate access or refresh tokens from the Twingate Admin Console. `https://auth.twingate.com/signup-v2`",
"If you need to update your access or refresh tokens, they can be found in /etc/twingate/connector.conf"
],
"type": "ct"
},
{
"name": "TypeSense",
"slug": "typesense",
@@ -5010,7 +5244,8 @@
4
],
"notes": [
"Wireguard and WGDashboard are not the same. More info: `https://docs.wgdashboard.dev/what-is-wireguard-what-is-wgdashboard.html`"
"Wireguard and WGDashboard are not the same. More info: `https://docs.wgdashboard.dev/what-is-wireguard-what-is-wgdashboard.html`",
"WGDashboard installation is optional.`"
],
"type": "ct",
"default_credentials": {

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,8 @@
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.0
# Last Updated: 28/01/2025
# Version : 1.1
# Last Updated: 17/08/2025
# ==========================================================
# Description:
# This script automates the process of enabling and configuring Intel Integrated GPU (iGPU) support in Proxmox VE LXC containers.
@@ -32,7 +32,7 @@ initialize_cache
# Select LXC container
select_container() {
CONTAINERS=$(pct list | awk 'NR>1 {print $1, $3}' | xargs -n2)
@@ -59,14 +59,14 @@ select_container() {
# Validate that the selected container is valid
validate_container_id() {
if [ -z "$CONTAINER_ID" ]; then
msg_error "$(translate 'Container ID not defined. Make sure to select a container first.')"
exit 1
fi
# Check if the container is running and stop it before configuration
if pct status "$CONTAINER_ID" | grep -q "running"; then
msg_info "$(translate 'Stopping the container before applying configuration...')"
pct stop "$CONTAINER_ID"
@@ -76,77 +76,103 @@ validate_container_id() {
# Configure LXC for Coral TPU and iGPU
configure_lxc_for_igpu() {
validate_container_id
CONFIG_FILE="/etc/pve/lxc/${CONTAINER_ID}.conf"
if [ ! -f "$CONFIG_FILE" ]; then
msg_error "$(translate 'Configuration file for container') $CONTAINER_ID $(translate 'not found.')"
exit 1
validate_container_id
CONFIG_FILE="/etc/pve/lxc/${CONTAINER_ID}.conf"
[[ -f "$CONFIG_FILE" ]] || { msg_error "$(translate 'Configuration file for container') $CONTAINER_ID $(translate 'not found.')"; exit 1; }
if [[ ! -d /dev/dri ]]; then
modprobe i915 2>/dev/null || true
for _ in {1..5}; do
[[ -d /dev/dri ]] && break
sleep 1
done
fi
CT_TYPE=$(pct config "$CONTAINER_ID" | awk '/^unprivileged:/ {print $2}')
[[ -z "$CT_TYPE" ]] && CT_TYPE="0"
msg_info "$(translate 'Configuring Intel iGPU passthrough for container...')"
for rn in /dev/dri/renderD*; do
[[ -e "$rn" ]] || continue
chmod 660 "$rn" 2>/dev/null || true
chgrp render "$rn" 2>/dev/null || true
done
mapfile -t RENDER_NODES < <(find /dev/dri -maxdepth 1 -type c -name 'renderD*' 2>/dev/null || true)
mapfile -t CARD_NODES < <(find /dev/dri -maxdepth 1 -type c -name 'card*' 2>/dev/null || true)
FB_NODE=""
[[ -e /dev/fb0 ]] && FB_NODE="/dev/fb0"
if [[ ${#RENDER_NODES[@]} -eq 0 && ${#CARD_NODES[@]} -eq 0 && -z "$FB_NODE" ]]; then
msg_warn "$(translate 'No VA-API devices found on host (/dev/dri*, /dev/fb0). Is i915 loaded?')"
return 0
fi
if grep -q "^unprivileged: 1" "$CONFIG_FILE"; then
msg_info "$(translate 'The container is unprivileged. Changing to privileged...')"
sed -i "s/^unprivileged: 1/unprivileged: 0/" "$CONFIG_FILE"
STORAGE_TYPE=$(pct config "$CONTAINER_ID" | grep "^rootfs:" | awk -F, '{print $2}' | cut -d'=' -f2)
if [[ "$STORAGE_TYPE" == "dir" ]]; then
STORAGE_PATH=$(pct config "$CONTAINER_ID" | grep "^rootfs:" | awk '{print $2}' | cut -d',' -f1)
chown -R root:root "$STORAGE_PATH"
fi
msg_ok "$(translate 'Container changed to privileged.')"
fi
if grep -q "^lxc.apparmor.profile" "$CONFIG_FILE"; then
msg_info "$(translate 'Disabling AppArmor profile to avoid conflicts...')"
sed -i "/^lxc.apparmor.profile/d" "$CONFIG_FILE"
msg_ok "$(translate 'AppArmor profile removed.')"
fi
# Configure iGPU
if ! grep -q "features: nesting=1" "$CONFIG_FILE"; then
if grep -q '^features:' "$CONFIG_FILE"; then
grep -Eq '^features:.*(^|,)\s*nesting=1(\s|,|$)' "$CONFIG_FILE" || sed -i 's/^features:\s*/&nesting=1, /' "$CONFIG_FILE"
else
echo "features: nesting=1" >> "$CONFIG_FILE"
fi
if ! grep -q "c 226:0 rwm" "$CONFIG_FILE"; then
echo "lxc.cgroup2.devices.allow: c 226:0 rwm # iGPU" >> "$CONFIG_FILE"
echo "lxc.cgroup2.devices.allow: c 226:128 rwm # iGPU" >> "$CONFIG_FILE"
if [[ "$CT_TYPE" == "0" ]]; then
sed -i '/^lxc\.cgroup2\.devices\.allow:\s*c\s*226:/d' "$CONFIG_FILE"
sed -i '\|^lxc\.mount\.entry:\s*/dev/dri|d' "$CONFIG_FILE"
sed -i '\|^lxc\.mount\.entry:\s*/dev/fb0|d' "$CONFIG_FILE"
echo "lxc.cgroup2.devices.allow: c 226:* rwm" >> "$CONFIG_FILE"
echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >> "$CONFIG_FILE"
echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >> "$CONFIG_FILE"
fi
if ! grep -q "c 29:0 rwm # Framebuffer" "$CONFIG_FILE"; then
echo "lxc.cgroup2.devices.allow: c 29:0 rwm # Framebuffer" >> "$CONFIG_FILE"
fi
if ! grep -q "lxc.mount.entry: /dev/fb0" "$CONFIG_FILE"; then
echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >> "$CONFIG_FILE"
fi
[[ -n "$FB_NODE" ]] && echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >> "$CONFIG_FILE"
msg_ok "$(translate 'Coral TPU and iGPU configuration added to container') $CONTAINER_ID."
else
sed -i '/^dev[0-9]\+:/d' "$CONFIG_FILE"
idx=0
for c in "${CARD_NODES[@]}"; do
echo "dev${idx}: $c,gid=44" >> "$CONFIG_FILE"
idx=$((idx+1))
done
for r in "${RENDER_NODES[@]}"; do
echo "dev${idx}: $r,gid=104" >> "$CONFIG_FILE"
idx=$((idx+1))
done
fi
msg_ok "$(translate 'iGPU configuration added to container') $CONTAINER_ID."
}
# Install iGPU drivers in the container
install_igpu_in_container() {
msg_info2 "$(translate 'Installing iGPU drivers inside the container...')"
tput sc
LOG_FILE=$(mktemp)
msg_info "$(translate 'Installing iGPU drivers...')"
pct start "$CONTAINER_ID" >/dev/null 2>&1
script -q -c "pct exec \"$CONTAINER_ID\" -- bash -c '
set -e
getent group video >/dev/null || groupadd -g 44 video
getent group render >/dev/null || groupadd -g 104 render
usermod -aG video,render root || true
apt-get update >/dev/null 2>&1
apt-get install -y va-driver-all ocl-icd-libopencl1 intel-opencl-icd vainfo intel-gpu-tools
chgrp video /dev/dri && chmod 755 /dev/dri
adduser root video && adduser root render
chgrp video /dev/dri 2>/dev/null || true
chmod 755 /dev/dri 2>/dev/null || true
'" "$LOG_FILE"
if [ $? -eq 0 ]; then
@@ -167,6 +193,7 @@ install_igpu_in_container() {
select_container
show_proxmenux_logo
msg_title "$(translate "Add HW iGPU acceleration to an LXC")"
configure_lxc_for_igpu
install_igpu_in_container

View File

@@ -187,7 +187,12 @@ is_disk_in_use() {
FREE_DISKS=()
LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -n1 readlink -f | sort -u)
LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -r -n1 readlink -f | sort -u)
if [[ -n "$LVM_DEVICES" ]] && echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then
IS_MOUNTED=true
fi
RAID_ACTIVE=$(grep -Po 'md\d+\s*:\s*active\s+raid[0-9]+' /proc/mdstat | awk '{print $1}' | sort -u)
while read -r DISK; do

View File

@@ -92,6 +92,9 @@ cleanup_duplicate_repos_pve9() {
local cleaned_count=0
declare -A seen_repos
if [ ! -s "$sources_file" ]; then
return 0
fi
while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# ==========================================================
# Remove Subscription Banner - Proxmox VE 9.x ONLY
# Remove Subscription Banner - Proxmox VE 9.x
# ==========================================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
@@ -15,7 +15,7 @@ fi
load_language
initialize_cache
# Tool registration system
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
@@ -33,7 +33,7 @@ remove_subscription_banner_pve9() {
local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script"
# Verify PVE 9.x
local pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1)
local pve_major=$(echo "$pve_version" | cut -d. -f1)
@@ -44,45 +44,49 @@ remove_subscription_banner_pve9() {
msg_info "Detected Proxmox VE $pve_version - Applying PVE 9.x patches"
# Verify that the file exists
if [ ! -f "$JS_FILE" ]; then
msg_error "JavaScript file not found: $JS_FILE"
return 1
fi
# Create backup of original file
local backup_file="${JS_FILE}.backup.pve9.$(date +%Y%m%d_%H%M%S)"
cp "$JS_FILE" "$backup_file"
# Clean any existing problematic APT hooks
for f in /etc/apt/apt.conf.d/*nag*; do
[[ -e "$f" ]] && rm -f "$f"
done
[[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE"
[[ -f "$MIN_JS_FILE" ]] && rm -f "$MIN_JS_FILE"
# Main subscription check patches for PVE 9
find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/cache/nginx/ -type f -delete 2>/dev/null || true
sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE"
sed -i "s/subscriptionActive: ''/subscriptionActive: true/g" "$JS_FILE"
sed -i "s/title: gettext('No valid subscription')/title: gettext('Community Edition')/g" "$JS_FILE"
# Additional UX improvements for PVE 9
sed -i "s/You do not have a valid subscription for this server/Community Edition - No subscription required/g" "$JS_FILE"
sed -i "s/Enterprise repository needs valid subscription/Enterprise repository configured/g" "$JS_FILE"
sed -i "s/icon: Ext\.Msg\.WARNING/icon: Ext.Msg.INFO/g" "$JS_FILE"
# Additional subscription patterns that may exist in PVE 9
sed -i "s/subscription = !(/subscription = false \&\& (/g" "$JS_FILE"
# Remove compressed/minified files to force regeneration
[[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE"
[[ -f "$MIN_JS_FILE" ]] && rm -f "$MIN_JS_FILE"
if grep -q "res\.data\.status\.toLowerCase() !== 'active'" "$JS_FILE"; then
msg_warn "Some patches may not have applied correctly, retrying..."
sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE"
fi
# Clear various caches
find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
# Create PVE 9.x specific APT hook
[[ -f "$APT_HOOK" ]] && rm -f "$APT_HOOK"
cat > "$APT_HOOK" << 'EOF'
DPkg::Post-Invoke {
@@ -96,7 +100,7 @@ EOF
chmod 644 "$APT_HOOK"
# Verify APT hook syntax
if ! apt-config dump >/dev/null 2>&1; then
msg_warn "APT hook has syntax issues, removing..."
rm -f "$APT_HOOK"
@@ -104,16 +108,17 @@ EOF
msg_ok "APT hook created successfully"
fi
systemctl reload nginx 2>/dev/null || true
msg_ok "Subscription banner removed successfully for Proxmox VE $pve_version"
msg_ok "Banner removal process completed"
msg_ok "Banner removal process completed - refresh your browser to see changes"
register_tool "subscription_banner" true
}
# Execute function if called directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
remove_subscription_banner_pve9
fi

View File

@@ -0,0 +1,119 @@
#!/bin/bash
# ==========================================================
# Remove Subscription Banner - Proxmox VE 9.x ONLY
# ==========================================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# Tool registration system
ensure_tools_json() {
[ -f "$TOOLS_JSON" ] || echo "{}" > "$TOOLS_JSON"
}
register_tool() {
local tool="$1"
local state="$2"
ensure_tools_json
jq --arg t "$tool" --argjson v "$state" '.[$t]=$v' "$TOOLS_JSON" > "$TOOLS_JSON.tmp" && mv "$TOOLS_JSON.tmp" "$TOOLS_JSON"
}
remove_subscription_banner_pve9() {
local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
local MIN_JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js"
local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script"
# Verify PVE 9.x
local pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1)
local pve_major=$(echo "$pve_version" | cut -d. -f1)
if [ "$pve_major" -lt 9 ] 2>/dev/null; then
msg_error "This script is for PVE 9.x only. Detected PVE $pve_version"
return 1
fi
msg_info "Detected Proxmox VE $pve_version - Applying PVE 9.x patches"
# Verify that the file exists
if [ ! -f "$JS_FILE" ]; then
msg_error "JavaScript file not found: $JS_FILE"
return 1
fi
# Create backup of original file
local backup_file="${JS_FILE}.backup.pve9.$(date +%Y%m%d_%H%M%S)"
cp "$JS_FILE" "$backup_file"
# Clean any existing problematic APT hooks
for f in /etc/apt/apt.conf.d/*nag*; do
[[ -e "$f" ]] && rm -f "$f"
done
# Main subscription check patches for PVE 9
sed -i "s/res\.data\.status\.toLowerCase() !== 'active'/false/g" "$JS_FILE"
sed -i "s/subscriptionActive: ''/subscriptionActive: true/g" "$JS_FILE"
sed -i "s/title: gettext('No valid subscription')/title: gettext('Community Edition')/g" "$JS_FILE"
# Additional UX improvements for PVE 9
sed -i "s/You do not have a valid subscription for this server/Community Edition - No subscription required/g" "$JS_FILE"
sed -i "s/Enterprise repository needs valid subscription/Enterprise repository configured/g" "$JS_FILE"
sed -i "s/icon: Ext\.Msg\.WARNING/icon: Ext.Msg.INFO/g" "$JS_FILE"
# Additional subscription patterns that may exist in PVE 9
sed -i "s/subscription = !(/subscription = false \&\& (/g" "$JS_FILE"
# Remove compressed/minified files to force regeneration
[[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE"
[[ -f "$MIN_JS_FILE" ]] && rm -f "$MIN_JS_FILE"
# Clear various caches
find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
# Create PVE 9.x specific APT hook
[[ -f "$APT_HOOK" ]] && rm -f "$APT_HOOK"
cat > "$APT_HOOK" << 'EOF'
DPkg::Post-Invoke {
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/res\\.data\\.status\\.toLowerCase() !== '\''active'\''/false/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscriptionActive: '\'\'\''/subscriptionActive: true/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/title: gettext('\''No valid subscription'\'')/title: gettext('\''Community Edition'\'')/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"test -e /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && sed -i 's/subscription = !(/subscription = false \\&\\& (/g' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js || true";
"rm -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.min.js /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz || true";
};
EOF
chmod 644 "$APT_HOOK"
# Verify APT hook syntax
if ! apt-config dump >/dev/null 2>&1; then
msg_warn "APT hook has syntax issues, removing..."
rm -f "$APT_HOOK"
else
msg_ok "APT hook created successfully"
fi
msg_ok "Subscription banner removed successfully for Proxmox VE $pve_version"
msg_ok "Banner removal process completed"
register_tool "subscription_banner" true
}
# Execute function if called directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
remove_subscription_banner_pve9
fi

View File

@@ -0,0 +1,901 @@
#!/usr/bin/env bash
# ==========================================================
# ProxMenux - Global Share Functions (reusable)
# File: scripts/global/share_common.func
# ==========================================================
if [[ -n "${__PROXMENUX_SHARE_COMMON__}" ]]; then
return 0
fi
__PROXMENUX_SHARE_COMMON__=1
: "${PROXMENUX_DEFAULT_SHARE_GROUP:=sharedfiles}"
: "${PROXMENUX_SHARE_MAP_DB:=/usr/local/share/proxmenux/share-map.db}"
mkdir -p "$(dirname "$PROXMENUX_SHARE_MAP_DB")" 2>/dev/null || true
touch "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null || true
pmx_share_map_get() {
local key="$1"
awk -F'=' -v k="$key" '$1==k {print $2}' "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null | tail -n1
}
pmx_share_map_set() {
local key="$1" val="$2"
sed -i "\|^${key}=|d" "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null || true
echo "${key}=${val}" >> "$PROXMENUX_SHARE_MAP_DB"
}
pmx_choose_or_create_group() {
local default_group="${1:-$PROXMENUX_DEFAULT_SHARE_GROUP}"
local choice group_name groups menu_args gid_min
gid_min="$(awk '/^\s*GID_MIN\s+[0-9]+/ {print $2}' /etc/login.defs 2>/dev/null | tail -n1)"
[[ -z "$gid_min" ]] && gid_min=1000
choice=$(whiptail --title "$(translate "Shared Group")" \
--menu "$(translate "Choose a group policy for this shared directory:")" 18 78 6 \
"1" "$(translate "Use default group:") $default_group $(translate "(recommended)")" \
"2" "$(translate "Create a new group for isolation")" \
"3" "$(translate "Select an existing group")" \
3>&1 1>&2 2>&3) || { echo ""; return 1; }
case "$choice" in
1)
pmx_ensure_host_group "$default_group" >/dev/null || { echo ""; return 1; }
echo "$default_group"
;;
2)
group_name=$(whiptail --inputbox "$(translate "Enter new group name:")" 10 70 "sharedfiles-project" \
--title "$(translate "New Group")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
if [[ -z "$group_name" ]]; then
msg_error "$(translate "Group name cannot be empty.")"
echo ""; return 1
fi
if ! [[ "$group_name" =~ ^[a-zA-Z_][a-zA-Z0-9_-]*$ ]]; then
msg_error "$(translate "Invalid group name. Use letters, digits, underscore or hyphen, and start with a letter or underscore.")"
echo ""; return 1
fi
pmx_ensure_host_group "$group_name" >/dev/null || { echo ""; return 1; }
echo "$group_name"
;;
3)
groups=$(getent group | awk -F: -v MIN="$gid_min" '
$3 >= MIN && $1 != "nogroup" && $1 !~ /^pve/ {print $0}
' | sort -t: -k1,1)
if [[ -z "$groups" ]]; then
whiptail --title "$(translate "Groups")" --msgbox "$(translate "No user groups found.")" 8 60
echo ""; return 1
fi
menu_args=()
while IFS=: read -r gname _ gid members; do
menu_args+=("$gname" "GID=$gid")
done <<< "$groups"
group_name=$(whiptail --title "$(translate "Existing Groups")" \
--menu "$(translate "Select an existing group:")" 20 70 12 \
"${menu_args[@]}" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
pmx_ensure_host_group "$group_name" >/dev/null || { echo ""; return 1; }
echo "$group_name"
;;
*)
echo ""; return 1
;;
esac
}
pmx_ensure_host_group() {
local group_name="$1"
local suggested_gid="${2:-}"
local base_gid=101000
local new_gid gid
if getent group "$group_name" >/dev/null 2>&1; then
gid="$(getent group "$group_name" | cut -d: -f3)"
echo "$gid"
return 0
fi
if [[ -n "$suggested_gid" ]]; then
if getent group "$suggested_gid" >/dev/null 2>&1; then
msg_error "$(translate "GID already in use:") $suggested_gid"
echo ""
return 1
fi
if ! groupadd -g "$suggested_gid" "$group_name" >/dev/null 2>&1; then
msg_error "$(translate "Failed to create group:") $group_name"
echo ""
return 1
fi
msg_ok "$(translate "Group created:") $group_name"
else
new_gid="$base_gid"
while getent group "$new_gid" >/dev/null 2>&1; do
new_gid=$((new_gid+1))
done
if ! groupadd -g "$new_gid" "$group_name" >/dev/null 2>&1; then
msg_error "$(translate "Failed to create group:") $group_name"
echo ""
return 1
fi
msg_ok "$(translate "Group created:") $group_name"
fi
gid="$(getent group "$group_name" | cut -d: -f3)"
if [[ -z "$gid" ]]; then
msg_error "$(translate "Failed to resolve group GID for") $group_name"
echo ""
return 1
fi
echo "$gid"
return 0
}
pmx_prepare_host_shared_dir() {
local dir="$1" group_name="$2"
[[ -z "$dir" || -z "$group_name" ]] && { msg_error "$(translate "Internal error: missing arguments in pmx_prepare_host_shared_dir")"; return 1; }
if [[ ! -d "$dir" ]]; then
if mkdir -p "$dir" 2>/dev/null; then
msg_ok "$(translate "Created directory on host:") $dir"
else
msg_error "$(translate "Failed to create directory on host:") $dir"
return 1
fi
fi
chown -R root:"$group_name" "$dir" 2>/dev/null || true
chmod -R 2775 "$dir" 2>/dev/null || true
if command -v setfacl >/dev/null 2>&1; then
setfacl -R -m d:g:"$group_name":rwx -m d:o::rx -m g:"$group_name":rwx "$dir" 2>/dev/null || true
msg_ok "$(translate "Default ACLs applied for group inheritance.")"
fi
return 0
}
pmx_select_host_mount_point() {
local title="${1:-$(translate "Select Mount Point")}"
local default_path="${2:-/mnt/shared}"
local context="${3:-local}"
local choice folder_name result existing_dirs mount_point
while true; do
choice=$(whiptail --title "$title" --menu "$(translate "Where do you want the host folder?")" 16 76 3 \
"1" "$(translate "Create new folder in /mnt")" \
"2" "$(translate "Enter custom pathr")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
case "$choice" in
1)
folder_name=$(whiptail --inputbox "$(translate "Enter folder name for /mnt:")" 10 70 "$(basename "$default_path")" --title "$(translate "Folder Name")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
[[ -z "$folder_name" ]] && continue
mount_point="/mnt/$folder_name"
echo "$mount_point"; return 0
;;
2)
result=$(whiptail --inputbox "$(translate "Enter full path:")" 10 80 "$default_path" --title "$(translate "Custom Path")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
[[ -z "$result" ]] && continue
echo "$result"; return 0
;;
esac
done
}
select_host_directory_() {
local method choice result
method=$(whiptail --title "$(translate "Select Host Directory")" --menu "$(translate "How do you want to select the HOST folder to mount?")" 15 70 4 \
"mnt" "$(translate "Select from /mnt directories")" \
"manual" "$(translate "Enter path manually")" 3>&1 1>&2 2>&3) || return 1
case "$method" in
mnt|srv|media)
local base_path="/$method"
local host_dirs=("$base_path"/*)
local options=()
for dir in "${host_dirs[@]}"; do
if [[ -d "$dir" ]]; then
options+=("$dir" "$(basename "$dir")")
fi
done
if [[ ${#options[@]} -eq 0 ]]; then
msg_error "$(translate "No directories found in") $base_path"
return 1
fi
result=$(whiptail --title "$(translate "Select Host Folder")" \
--menu "$(translate "Select the folder to mount:")" 20 80 10 "${options[@]}" 3>&1 1>&2 2>&3)
;;
manual)
result=$(whiptail --title "$(translate "Enter Path")" \
--inputbox "$(translate "Enter the full path to the host folder:")" 10 70 "/mnt/" 3>&1 1>&2 2>&3)
;;
esac
if [[ -z "$result" ]]; then
return 1
fi
if [[ ! -d "$result" ]]; then
msg_error "$(translate "The selected path is not a valid directory:") $result"
return 1
fi
echo "$result"
}
select_host_directory__() {
local method result
method=$(whiptail --title "$(translate "Select Host Directory")" \
--menu "$(translate "How do you want to select the HOST folder to mount?")" 15 70 4 \
"mnt" "$(translate "Select from /mnt directories")" \
"manual" "$(translate "Enter path manually")" \
3>&1 1>&2 2>&3) || return 1
case "$method" in
mnt|srv|media)
local base_path="/$method"
local host_dirs=("$base_path"/*)
local options=()
for dir in "${host_dirs[@]}"; do
[[ -d "$dir" ]] && options+=("$dir" "$(basename "$dir")")
done
if [[ ${#options[@]} -eq 0 ]]; then
msg_error "$(translate "No directories found in") $base_path"
return 1
fi
result=$(whiptail --title "$(translate "Select Host Folder")" \
--menu "$(translate "Select the folder to mount:")" 20 80 10 \
"${options[@]}" 3>&1 1>&2 2>&3) || return 1
;;
manual)
result=$(whiptail --title "$(translate "Enter Path")" \
--inputbox "$(translate "Enter the full path to the host folder:")" \
10 70 "/mnt/" 3>&1 1>&2 2>&3) || return 1
;;
*)
return 1
;;
esac
[[ -z "$result" ]] && return 1
[[ ! -d "$result" ]] && {
msg_error "$(translate "The selected path is not a valid directory:") $result"
return 1
}
echo "$result"
}
select_host_directory() {
local method result
method=$(whiptail --title "$(translate "Select Host Directory")" \
--menu "$(translate "How do you want to select the HOST folder to mount?")" 15 70 4 \
"mnt" "$(translate "Select from /mnt directories")" \
"manual" "$(translate "Enter path manually")" \
3>&1 1>&2 2>&3) || return 1
case "$method" in
mnt|srv|media)
local base_path="/$method"
local host_dirs=("$base_path"/*)
local options=()
for dir in "${host_dirs[@]}"; do
[[ -d "$dir" ]] && options+=("$dir" "$(basename "$dir")")
done
if [[ ${#options[@]} -eq 0 ]]; then
msg_error "$(translate "No directories found in") $base_path"
return 1
fi
result=$(whiptail --title "$(translate "Select Host Folder")" \
--menu "$(translate "Select the folder to mount:")" 20 80 10 \
"${options[@]}" 3>&1 1>&2 2>&3) || return 1
;;
manual)
result=$(whiptail --title "$(translate "Enter Path")" \
--inputbox "$(translate "Enter the full path to the host folder:")" \
10 70 "/mnt/" 3>&1 1>&2 2>&3) || return 1
;;
*)
return 1
;;
esac
[[ -z "$result" ]] && return 1
[[ ! -d "$result" ]] && {
msg_error "$(translate "The selected path is not a valid directory:") $result"
return 1
}
echo "$result"
}
select_lxc_container() {
local ct_list ctid ct_status
ct_list=$(pct list | awk 'NR>1 {print $1, $2, $3}')
if [[ -z "$ct_list" ]]; then
dialog --title "$(translate "Error")" \
--msgbox "$(translate "No LXC containers available")" 8 50
return 1
fi
local options=()
while read -r id name status; do
if [[ -n "$id" ]]; then
options+=("$id" "$name ($status)")
fi
done <<< "$ct_list"
ctid=$(dialog --title "$(translate "Select LXC Container")" \
--menu "\n$(translate "Select container:")" 25 80 15 \
"${options[@]}" 3>&1 1>&2 2>&3)
if [[ -z "$ctid" ]]; then
return 1
fi
echo "$ctid"
return 0
}
select_container_mount_point_() {
local ctid="$1"
local host_dir="$2"
local choice mount_point existing_dirs options
while true; do
choice=$(whiptail --title "$(translate "Configure Mount Point inside LXC")" \
--menu "$(translate "Where to mount inside container?")" 18 70 5 \
"1" "$(translate "Create new directory in /mnt")" \
"2" "$(translate "Enter path manually")" \
"3" "$(translate "Cancel")" 3>&1 1>&2 2>&3) || return 1
case "$choice" in
1)
mount_point=$(whiptail --inputbox "$(translate "Enter folder name for /mnt:")" 10 60 "shared" 3>&1 1>&2 2>&3) || continue
[[ -z "$mount_point" ]] && continue
mount_point="/mnt/$mount_point"
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
;;
2)
mount_point=$(whiptail --inputbox "$(translate "Enter full path:")" 10 70 "/mnt/shared" 3>&1 1>&2 2>&3) || continue
[[ -z "$mount_point" ]] && continue
mount_point="/mnt/$mount_point"
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
;;
3)
return 1
;;
esac
if pct exec "$ctid" -- test -d "$mount_point" 2>/dev/null; then
echo "$mount_point"
return 0
else
whiptail --msgbox "$(translate "Could not create or access directory:") $mount_point" 8 70
continue
fi
done
}
select_container_mount_point() {
local ctid="$1"
local host_dir="$2"
local choice mount_point base_name
base_name=$(basename "$host_dir")
while true; do
choice=$(whiptail --title "$(translate "Configure Mount Point inside LXC")" \
--menu "$(translate "Where to mount inside container?")" 18 70 5 \
"1" "$(translate "Create new directory in /mnt")" \
"2" "$(translate "Enter path manually")" \
"3" "$(translate "Cancel")" 3>&1 1>&2 2>&3) || return 1
case "$choice" in
1)
mount_point=$(whiptail --inputbox "$(translate "Enter folder name for /mnt:")" \
10 60 "$base_name" 3>&1 1>&2 2>&3) || continue
[[ -z "$mount_point" ]] && continue
mount_point="/mnt/$mount_point"
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
;;
2)
mount_point=$(whiptail --inputbox "$(translate "Enter full path:")" \
10 70 "/mnt/$base_name" 3>&1 1>&2 2>&3) || continue
[[ -z "$mount_point" ]] && continue
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
;;
3)
return 1
;;
esac
if pct exec "$ctid" -- test -d "$mount_point" 2>/dev/null; then
echo "$mount_point"
return 0
else
whiptail --msgbox "$(translate "Could not create or access directory:") $mount_point" 8 70
continue
fi
done
}
# ==========================================================
# CLIENT MOUNT FUNCTIONS (NFS/SAMBA COMMON)
# ==========================================================
# Check if container is privileged (required for client mounts)
select_privileged_lxc() {
# === Select CT ===
local ct_list ctid ct_status conf unpriv
ct_list=$(pct list | awk 'NR>1 {print $1, $3}')
if [[ -z "$ct_list" ]]; then
dialog --backtitle "ProxMenux" --title "$(translate "Error")" \
--msgbox "$(translate "No CTs available in the system.")" 8 50
return 1
fi
ctid=$(dialog --backtitle "ProxMenux" --title "$(translate "Select CT")" \
--menu "$(translate "Select the CT to manage NFS/Samba client:")" 20 70 12 \
$ct_list 3>&1 1>&2 2>&3)
if [[ -z "$ctid" ]]; then
dialog --backtitle "ProxMenux" --title "$(translate "Error")" \
--msgbox "$(translate "No CT was selected.")" 8 50
return 1
fi
# === Start CT if not running ===
ct_status=$(pct status "$ctid" | awk '{print $2}')
if [[ "$ct_status" != "running" ]]; then
show_proxmenux_logo
echo -e
msg_info "$(translate "Starting CT") $ctid..."
pct start "$ctid"
sleep 2
if [[ "$(pct status "$ctid" | awk '{print $2}')" != "running" ]]; then
msg_error "$(translate "Failed to start the CT.")"
echo -e ""
msg_success "$(translate 'Press Enter to continue...')"
read -r
return 1
fi
msg_ok "$(translate "CT started successfully.")"
fi
# === Check privileged/unprivileged ===
conf="/etc/pve/lxc/${ctid}.conf"
unpriv=$(awk '/^unprivileged:/ {print $2}' "$conf" 2>/dev/null)
if [[ "$unpriv" == "1" ]]; then
dialog --backtitle "ProxMenux" --title "$(translate "Privileged Container Required")" \
--msgbox "\n$(translate "Network share mounting (NFS/Samba) requires a PRIVILEGED container.")\n\n$(translate "Selected container") $ctid $(translate "is UNPRIVILEGED.")\n\n$(translate "For unprivileged containers, use instead:")\n • $(translate "Configure LXC mount points")\n • $(translate "Mount shares on HOST first")\n • $(translate "Then bind-mount to container")" 15 75
exit 1
fi
# Export CTID if all good
echo "$ctid"
CTID="$ctid"
return 0
}
# Common mount point selection for containers
pmx_select_container_mount_point() {
local ctid="$1"
local share_name="${2:-shared}"
while true; do
local choice=$(whiptail --title "$(translate "Select Mount Point")" --menu "$(translate "Where do you want to mount inside container?")" 15 70 3 \
"existing" "$(translate "Select from existing folders in /mnt")" \
"new" "$(translate "Create new folder in /mnt")" \
"custom" "$(translate "Enter custom path")" 3>&1 1>&2 2>&3)
case "$choice" in
existing)
local existing_dirs=$(pct exec "$ctid" -- find /mnt -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort)
if [[ -z "$existing_dirs" ]]; then
whiptail --title "$(translate "No Folders")" --msgbox "$(translate "No folders found in /mnt. Please create a new folder.")" 8 60
continue
fi
local options=()
while IFS= read -r dir; do
if [[ -n "$dir" ]]; then
local name=$(basename "$dir")
if pct exec "$ctid" -- [ "$(ls -A "$dir" 2>/dev/null | wc -l)" -eq 0 ]; then
local status="$(translate "Empty")"
else
local status="$(translate "Contains files")"
fi
options+=("$dir" "$name ($status)")
fi
done <<< "$existing_dirs"
local mount_point=$(whiptail --title "$(translate "Select Existing Folder")" --menu "$(translate "Choose a folder to mount:")" 20 80 10 "${options[@]}" 3>&1 1>&2 2>&3)
if [[ -n "$mount_point" ]]; then
if pct exec "$ctid" -- [ "$(ls -A "$mount_point" 2>/dev/null | wc -l)" -gt 0 ]; then
local file_count=$(pct exec "$ctid" -- ls -A "$mount_point" 2>/dev/null | wc -l || true)
if ! whiptail --yesno "$(translate "WARNING: The selected directory is not empty!")\n\n$(translate "Directory:"): $mount_point\n$(translate "Contains:"): $file_count $(translate "files/folders")\n\n$(translate "Mounting here will hide existing files until unmounted.")\n\n$(translate "Do you want to continue?")" 14 70 --title "$(translate "Directory Not Empty")"; then
continue
fi
fi
echo "$mount_point"
return 0
fi
;;
new)
local folder_name=$(whiptail --inputbox "$(translate "Enter new folder name:")" 10 60 "$share_name" --title "$(translate "New Folder in /mnt")" 3>&1 1>&2 2>&3)
if [[ -n "$folder_name" ]]; then
local mount_point="/mnt/$folder_name"
echo "$mount_point"
return 0
fi
;;
custom)
local mount_point=$(whiptail --inputbox "$(translate "Enter full path for mount point:")" 10 70 "/mnt/${share_name}" --title "$(translate "Custom Path")" 3>&1 1>&2 2>&3)
if [[ -n "$mount_point" ]]; then
echo "$mount_point"
return 0
fi
;;
*)
return 1
;;
esac
done
}
# Common server discovery function
pmx_discover_network_servers() {
local service_type="$1" # "NFS" or "Samba"
local port="$2" # "2049" for NFS, "139,445" for Samba
local host_ip=$(hostname -I | awk '{print $1}')
local network=$(echo "$host_ip" | cut -d. -f1-3).0/24
# Install nmap if needed
if ! which nmap >/dev/null 2>&1; then
apt-get install -y nmap &>/dev/null
fi
local servers
if [[ "$service_type" == "Samba" ]]; then
servers=$(nmap -p 139,445 --open "$network" 2>/dev/null | grep -B 4 -E "(139|445)/tcp open" | grep "Nmap scan report" | awk '{print $5}' | sort -u || true)
else
servers=$(nmap -p 2049 --open "$network" 2>/dev/null | grep -B 4 "2049/tcp open" | grep "Nmap scan report" | awk '{print $5}' | sort -u || true)
fi
if [[ -z "$servers" ]]; then
whiptail --title "$(translate "No Servers Found")" --msgbox "$(translate "No") $service_type $(translate "servers found on the network.")\n\n$(translate "You can add servers manually.")" 10 60
return 1
fi
local options=()
while IFS= read -r server; do
if [[ -n "$server" ]]; then
if [[ "$service_type" == "Samba" ]]; then
# Try to get NetBIOS name for Samba
local nb_name=$(nmblookup -A "$server" 2>/dev/null | awk '/<00> -.*B <ACTIVE>/ {print $1; exit}')
if [[ -z "$nb_name" || "$nb_name" == "$server" || "$nb_name" == "address" || "$nb_name" == "-" ]]; then
nb_name="Unknown"
fi
options+=("$server" "$nb_name ($server)")
else
# For NFS, show export count
local exports_count=$(showmount -e "$server" 2>/dev/null | tail -n +2 | wc -l || echo "0")
options+=("$server" "NFS Server ($exports_count exports)")
fi
fi
done <<< "$servers"
if [[ ${#options[@]} -eq 0 ]]; then
whiptail --title "$(translate "No Valid Servers")" --msgbox "$(translate "No accessible") $service_type $(translate "servers found.")" 8 50
return 1
fi
local selected_server=$(whiptail --title "$(translate "Select") $service_type $(translate "Server")" --menu "$(translate "Choose a server:")" 20 80 10 "${options[@]}" 3>&1 1>&2 2>&3)
if [[ -n "$selected_server" ]]; then
echo "$selected_server"
return 0
else
return 1
fi
}
# Common server selection function
pmx_select_server() {
local service_type="$1" # "NFS" or "Samba"
local port="$2" # "2049" for NFS, "139,445" for Samba
local method=$(whiptail --title "$(translate "$service_type Server Selection")" --menu "$(translate "How do you want to select the") $service_type $(translate "server?")" 15 70 3 \
"auto" "$(translate "Auto-discover servers on network")" \
"manual" "$(translate "Enter server IP/hostname manually")" \
"recent" "$(translate "Select from recent servers")" 3>&1 1>&2 2>&3)
local result_code=$?
if [[ $result_code -ne 0 ]]; then
return 1
fi
case "$method" in
auto)
local discovered_server
discovered_server=$(pmx_discover_network_servers "$service_type" "$port")
local discover_result=$?
if [[ $discover_result -eq 0 && -n "$discovered_server" ]]; then
echo "$discovered_server"
return 0
else
return 1
fi
;;
manual)
local server=$(whiptail --inputbox "$(translate "Enter") $service_type $(translate "server IP or hostname:")" 10 60 --title "$(translate "$service_type Server")" 3>&1 1>&2 2>&3)
local input_result=$?
if [[ $input_result -eq 0 && -n "$server" ]]; then
echo "$server"
return 0
else
return 1
fi
;;
recent)
local fs_type
if [[ "$service_type" == "NFS" ]]; then
fs_type="nfs"
else
fs_type="cifs"
fi
# Fix the recent servers detection for NFS
local recent
if [[ "$service_type" == "NFS" ]]; then
recent=$(grep "$fs_type" /etc/fstab 2>/dev/null | awk '{print $1}' | cut -d: -f1 | sort -u || true)
else
recent=$(grep "$fs_type" /etc/fstab 2>/dev/null | awk '{print $1}' | cut -d/ -f3 | sort -u || true)
fi
if [[ -z "$recent" ]]; then
whiptail --title "$(translate "No Recent Servers")" --msgbox "\n$(translate "No recent") $service_type $(translate "servers found.")" 8 50
return 1
fi
local options=()
while IFS= read -r server; do
[[ -n "$server" ]] && options+=("$server" "$(translate "Recent") $service_type $(translate "server")")
done <<< "$recent"
local selected_server=$(whiptail --title "$(translate "Recent") $service_type $(translate "Servers")" --menu "$(translate "Choose a recent server:")" 20 70 10 "${options[@]}" 3>&1 1>&2 2>&3)
local select_result=$?
if [[ $select_result -eq 0 && -n "$selected_server" ]]; then
echo "$selected_server"
return 0
else
return 1
fi
;;
*)
return 1
;;
esac
}
# Common mount options configuration
pmx_configure_mount_options() {
local service_type="$1" # "NFS" or "CIFS"
local mount_type
if [[ "$service_type" == "NFS" ]]; then
mount_type=$(whiptail --title "$(translate "Mount Options")" --menu "$(translate "Select mount configuration:")" 15 70 4 \
"default" "$(translate "Default options")" \
"readonly" "$(translate "Read-only mount")" \
"performance" "$(translate "Performance optimized")" \
"custom" "$(translate "Custom options")" 3>&1 1>&2 2>&3)
case "$mount_type" in
default)
echo "rw,hard,intr,rsize=8192,wsize=8192,timeo=14"
;;
readonly)
echo "ro,hard,intr,rsize=8192,timeo=14"
;;
performance)
echo "rw,hard,intr,rsize=1048576,wsize=1048576,timeo=14,retrans=2"
;;
custom)
local options=$(whiptail --inputbox "$(translate "Enter custom mount options:")" 10 70 "rw,hard,intr" --title "$(translate "Custom Options")" 3>&1 1>&2 2>&3)
echo "${options:-rw,hard,intr}"
;;
*)
echo "rw,hard,intr,rsize=8192,wsize=8192,timeo=14"
;;
esac
else
# CIFS options
mount_type=$(whiptail --title "$(translate "Mount Options")" --menu "$(translate "Select mount configuration:")" 15 70 4 \
"default" "$(translate "Default options")" \
"readonly" "$(translate "Read-only mount")" \
"performance" "$(translate "Performance optimized")" \
"custom" "$(translate "Custom options")" 3>&1 1>&2 2>&3)
case "$mount_type" in
default)
echo "rw,file_mode=0664,dir_mode=0775,iocharset=utf8"
;;
readonly)
echo "ro,file_mode=0444,dir_mode=0555,iocharset=utf8"
;;
performance)
echo "rw,file_mode=0664,dir_mode=0775,iocharset=utf8,cache=strict,rsize=1048576,wsize=1048576"
;;
custom)
local options=$(whiptail --inputbox "$(translate "Enter custom mount options:")" 10 70 "rw,file_mode=0664,dir_mode=0775" --title "$(translate "Custom Options")" 3>&1 1>&2 2>&3)
echo "${options:-rw,file_mode=0664,dir_mode=0775}"
;;
*)
echo "rw,file_mode=0664,dir_mode=0775,iocharset=utf8"
;;
esac
fi
}
# Common permanent mount question
pmx_ask_permanent_mount() {
if whiptail --yesno "$(translate "Do you want to make this mount permanent?")\n\n$(translate "This will add the mount to /etc/fstab so it persists after reboot.")" 10 70 --title "$(translate "Permanent Mount")"; then
echo "true"
else
echo "false"
fi
}

View File

@@ -240,14 +240,16 @@ EOF
return 0
fi
msg_info "$(translate "Removing conflicting utilities...")"
msg_info "$(translate "Cleaning up unused time synchronization services...")"
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' purge ntp openntpd systemd-timesyncd > /dev/null 2>&1; then
msg_ok "$(translate "Conflicting utilities removed")"
msg_ok "$(translate "Old time services removed successfully")"
else
msg_warn "$(translate "Some conflicting utilities may not have been removed")"
msg_warn "$(translate "Some old time services could not be removed (not installed)")"
fi
msg_info "$(translate "Updating packages...")"
apt-get install pv -y > /dev/null 2>&1
msg_ok "$(translate "Packages updated successfully")"
@@ -330,6 +332,7 @@ EOF
echo -e "${TAB}${GN}🖥️ $(translate "Proxmox VE")${CL}: ${BL}$target_version (Debian $OS_CODENAME)${CL}"
msg_ok "$(translate "Proxmox VE 9.x configuration completed.")"
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then

View File

@@ -179,38 +179,42 @@ EOF
fi
export DEBIAN_FRONTEND=noninteractive
export APT_LISTCHANGES_FRONTEND=none
export NEEDRESTART_MODE=a
export UCF_FORCE_CONFOLD=1
export DPKG_OPTIONS="--force-confdef --force-confold"
msg_info "$(translate "Performing packages upgrade...")"
apt-get install pv -y > /dev/null 2>&1
total_packages=$(apt-get -s dist-upgrade | grep "^Inst" | wc -l)
msg_ok "$(translate "Packages upgrade successfull")"
if [ "$total_packages" -eq 0 ]; then
total_packages=1
total_packages=1
fi
tput civis
tput sc
tput sc
(
/usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' dist-upgrade 2>&1 | \
/usr/bin/env \
DEBIAN_FRONTEND=noninteractive \
APT_LISTCHANGES_FRONTEND=none \
NEEDRESTART_MODE=a \
UCF_FORCE_CONFOLD=1 \
apt-get -y \
-o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confold" \
dist-upgrade 2>&1 | \
while IFS= read -r line; do
if [[ "$line" =~ ^(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ]]; then
if [[ "$line" =~ ^(Setting\ up|Unpacking|Preparing\ to\ unpack|Processing\ triggers\ for) ]]; then
package_name=$(echo "$line" | sed -E 's/.*(Setting up|Unpacking|Preparing to unpack|Processing triggers for) ([^ ]+).*/\2/')
[ -z "$package_name" ] && package_name="$(translate "Unknown")"
tput rc
tput ed
row=$(( $(tput lines) - 6 ))
tput cup $row 0; echo "$(translate "Installing packages...")"
tput cup $((row + 1)) 0; echo "──────────────────────────────────────────────"
@@ -218,12 +222,10 @@ EOF
tput cup $((row + 3)) 0; echo "Progress: [ ] 0%"
tput cup $((row + 4)) 0; echo "──────────────────────────────────────────────"
for i in $(seq 1 10); do
progress=$((i * 10))
tput cup $((row + 3)) 9
tput cup $((row + 3)) 9
printf "[%-50s] %3d%%" "$(printf "#%.0s" $(seq 1 $((progress/2))))" "$progress"
done
fi
done
@@ -232,6 +234,7 @@ EOF
if [ $? -eq 0 ]; then
tput rc
tput ed
tput cnorm
msg_ok "$(translate "System upgrade completed")"
fi

View File

@@ -197,13 +197,12 @@ show_vm_ct_commands() {
echo -e "\n${YELLOW}$(translate 'Listing relevant CT users and their mapped UID/GID on host...')${NC}\n"
# Obtener el shift de UID del CT (por defecto 100000 si no está configurado)
UID_SHIFT=$(grep "^lxc.idmap" /etc/pve/lxc/"$id".conf | grep 'u 0' | awk '{print $5}')
UID_SHIFT=${UID_SHIFT:-100000}
# Obtener todos los usuarios y filtrar solo root o UID >= 1000
pct exec "$id" -- getent passwd | while IFS=: read -r username _ uid gid _ home _; do
if [ "$uid" -eq 0 ] || [ "$uid" -ge 1000 ]; then
if [ "$uid" -eq 0 ] || [ "$uid" -eq 65534 ] || [ "$uid" -ge 30 ]; then
real_uid=$((UID_SHIFT + uid))
real_gid=$((UID_SHIFT + gid))
echo -e "${GREEN}$(translate 'User')${NC}: $username"

View File

@@ -0,0 +1,284 @@
#!/bin/bash
# ==========================================================
# ProxMenux - Manual LXC Conversion Guide
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.0
# Last Updated: 19/08/2025
# ==========================================================
# Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
show_command() {
local step="$1"
local description="$2"
local command="$3"
local note="$4"
local command_extra="$5"
echo -e "${BGN}${step}.${CL} ${BL}${description}${CL}"
echo ""
echo -e "${TAB}${command}"
echo -e
[[ -n "$note" ]] && echo -e "${TAB}${DARK_GRAY}${note}${CL}"
[[ -n "$command_extra" ]] && echo -e "${TAB}${YW}${command_extra}${CL}"
echo ""
}
show_privileged_to_unprivileged_guide() {
clear
show_proxmenux_logo
msg_title "$(translate "Manual Guide: Convert LXC Privileged to Unprivileged")"
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "Source:")${CL} ${BL}https://forum.proxmox.com/threads/converting-between-privileged-and-unprivileged-containers.97243/${CL}"
echo -e
echo -e
echo -e "${TAB}${BOLD}$(translate "IMPORTANT PREREQUISITES:")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "Container must be stopped before conversion")${CL}"
echo -e "${TAB}${BGN}$(translate "Create a backup of your container before proceeding")${CL}"
echo -e "${TAB}${BGN}$(translate "This process changes file ownership inside the container")${CL}"
echo -e "${TAB}${BGN}$(translate "Process may take several minutes depending on container size")${CL}"
echo -e "${TAB}${BGN}$(translate "Works with LVM, ZFS, and BTRFS storage types")${CL}"
echo -e
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
show_command "1" \
"$(translate "List all containers to identify the privileged one:")" \
"pct list" \
"$(translate "Look for containers without 'unprivileged: 1' in their config")"
show_command "2" \
"$(translate "Stop the container if it's running:")" \
"pct stop <container-id>" \
"$(translate "Replace <container-id> with your actual container ID")" \
"$(translate "Example: pct stop 114")"
show_command "3" \
"$(translate "Create a backup of the container configuration:")" \
"cp /etc/pve/lxc/<container-id>.conf /etc/pve/lxc/<container-id>.conf.bak" \
"$(translate "This creates a backup in case you need to revert changes")" \
"$(translate "Example: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak")"
show_command "4" \
"$(translate "Get the container's storage information:")" \
"grep '^rootfs:' /etc/pve/lxc/<container-id>.conf" \
"$(translate "This shows the storage type and disk identifier")" \
"$(translate "Example output: rootfs: local-lvm:vm-114-disk-0,size=8G")"
show_command "5" \
"$(translate "Get the actual disk path:")" \
"pvesm path <storage-identifier>" \
"$(translate "Replace <storage-identifier> with the value from step 4")" \
"$(translate "Example: pvesm path local-lvm:vm-114-disk-0")"
echo -e "${TAB}${BOLD}$(translate "STEP 6: Choose commands based on your storage type")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "If pvesm path returned a DIRECTORY (ZFS/BTRFS):")${CL}"
echo -e "${TAB}${YW}$(translate "Example: /rpool/data/subvol-114-disk-0")${CL}"
echo -e
show_command "6a" \
"$(translate "For ZFS/BTRFS - Set the mount path:")" \
"MOUNT_PATH=\"/rpool/data/subvol-<container-id>-disk-0\"" \
"$(translate "Replace with your actual path from step 5")" \
"$(translate "Example: MOUNT_PATH=\"/rpool/data/subvol-114-disk-0\"")"
echo -e "${TAB}${BGN}$(translate "If pvesm path returned a DEVICE (LVM):")${CL}"
echo -e "${TAB}${YW}$(translate "Example: /dev/pve/vm-114-disk-0")${CL}"
echo -e
show_command "6b" \
"$(translate "For LVM - Create mount directory and mount:")" \
"mkdir -p /tmp/lxc_convert_<container-id>\nmount -o loop /dev/path/to/disk /tmp/lxc_convert_<container-id>\nMOUNT_PATH=\"/tmp/lxc_convert_<container-id>\"" \
"$(translate "Replace paths with your actual values from step 5")" \
"$(translate "Example: mkdir -p /tmp/lxc_convert_114")"
show_command "7" \
"$(translate "Convert file ownership (this takes time):")" \
"find \"\$MOUNT_PATH\" -type f | while read file; do\n if [ -e \"\$file\" ]; then\n CURRENT_UID=\$(stat -c '%u' \"\$file\")\n CURRENT_GID=\$(stat -c '%g' \"\$file\")\n NEW_UID=\$((100000 + CURRENT_UID))\n NEW_GID=\$((100000 + CURRENT_GID))\n chown \"\$NEW_UID:\$NEW_GID\" \"\$file\"\n fi\ndone" \
"$(translate "This converts all file UIDs/GIDs by adding 100000")" \
"$(translate "Process may take several minutes for large containers")"
show_command "8" \
"$(translate "Convert directory ownership:")" \
"find \"\$MOUNT_PATH\" -type d | while read dir; do\n if [ -e \"\$dir\" ]; then\n CURRENT_UID=\$(stat -c '%u' \"\$dir\")\n CURRENT_GID=\$(stat -c '%g' \"\$dir\")\n NEW_UID=\$((100000 + CURRENT_UID))\n NEW_GID=\$((100000 + CURRENT_GID))\n chown \"\$NEW_UID:\$NEW_GID\" \"\$dir\"\n fi\ndone" \
"$(translate "This converts all directory UIDs/GIDs by adding 100000")"
echo -e "${TAB}${BOLD}$(translate "STEP 9: Cleanup (LVM only)")${CL}"
echo -e "${TAB}${YW}$(translate "Only run this if you used LVM (step 6b):")${CL}"
echo -e
show_command "9" \
"$(translate "Unmount and cleanup (LVM only):")" \
"umount /tmp/lxc_convert_<container-id>\nrmdir /tmp/lxc_convert_<container-id>" \
"$(translate "Only needed if you mounted the filesystem in step 6b")" \
"$(translate "Skip this step for ZFS/BTRFS")"
show_command "10" \
"$(translate "Add unprivileged flag to container configuration:")" \
"echo 'unprivileged: 1' >> /etc/pve/lxc/<container-id>.conf" \
"$(translate "This marks the container as unprivileged")"
show_command "11" \
"$(translate "Start the converted container:")" \
"pct start <container-id>" \
"$(translate "The container should now start as unprivileged")"
show_command "12" \
"$(translate "Verify the conversion:")" \
"pct config <container-id> | grep unprivileged" \
"$(translate "Should show 'unprivileged: 1'")"
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
echo -e "${TAB}${BOLD}$(translate "STORAGE TYPE IDENTIFICATION:")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "LVM:")${CL} ${YW}pvesm path returns /dev/xxx (block device)${CL}"
echo -e "${TAB}${BGN}$(translate "ZFS:")${CL} ${YW}pvesm path returns /rpool/xxx (directory)${CL}"
echo -e "${TAB}${BGN}$(translate "BTRFS:")${CL} ${YW}pvesm path returns directory path${CL}"
echo -e
echo -e "${TAB}${BOLD}$(translate "TROUBLESHOOTING:")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "If mount fails (LVM):")${CL} ${YW}Check that the container is stopped and disk path is correct${CL}"
echo -e "${TAB}${BGN}$(translate "If path not accessible (ZFS/BTRFS):")${CL} ${YW}Verify the dataset/subvolume exists and is mounted${CL}"
echo -e "${TAB}${BGN}$(translate "If container won't start:")${CL} ${YW}Check /var/log/pve/tasks/ for detailed error messages${CL}"
echo -e "${TAB}${BGN}$(translate "To revert changes:")${CL} ${YW}cp /etc/pve/lxc/<container-id>.conf.bak /etc/pve/lxc/<container-id>.conf${CL}"
echo -e
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
echo -e
read -r
}
show_unprivileged_to_privileged_guide() {
clear
show_proxmenux_logo
msg_title "$(translate "Manual Guide: Convert LXC Unprivileged to Privileged")"
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
echo -e "${TAB}${RD}$(translate "SECURITY WARNING:")${CL} ${YW}$(translate "Privileged containers have full root access to the host system!")${CL}"
echo -e "${TAB}${YW}$(translate "Only convert to privileged if absolutely necessary for your use case.")${CL}"
echo -e
echo -e
echo -e "${TAB}${BOLD}$(translate "IMPORTANT PREREQUISITES:")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "Container must be stopped before conversion")${CL}"
echo -e "${TAB}${BGN}$(translate "Create a backup of your container before proceeding")${CL}"
echo -e "${TAB}${BGN}$(translate "Understand the security implications of privileged containers")${CL}"
echo -e "${TAB}${BGN}$(translate "This is a simple configuration change")${CL}"
echo -e
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
show_command "1" \
"$(translate "List all containers to identify the unprivileged one:")" \
"pct list" \
"$(translate "Look for containers with 'unprivileged: 1' in their config")"
show_command "2" \
"$(translate "Check if container is unprivileged:")" \
"pct config <container-id> | grep unprivileged" \
"$(translate "Should show 'unprivileged: 1' if it's unprivileged")" \
"$(translate "Example: pct config 110 | grep unprivileged")"
show_command "3" \
"$(translate "Stop the container if it's running:")" \
"pct stop <container-id>" \
"$(translate "Replace <container-id> with your actual container ID")" \
"$(translate "Example: pct stop 110")"
show_command "4" \
"$(translate "Create a backup of the container configuration:")" \
"cp /etc/pve/lxc/<container-id>.conf /etc/pve/lxc/<container-id>.conf.bak" \
"$(translate "This creates a backup in case you need to revert changes")" \
"$(translate "Example: cp /etc/pve/lxc/110.conf /etc/pve/lxc/110.conf.bak")"
show_command "5" \
"$(translate "Remove the unprivileged flag from configuration:")" \
"sed -i '/^unprivileged: 1/d' /etc/pve/lxc/<container-id>.conf" \
"$(translate "This removes the 'unprivileged: 1' line from the config")" \
"$(translate "Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf")"
show_command "6" \
"$(translate "Add explicit privileged flag (optional but recommended):")" \
"echo 'unprivileged: 0' >> /etc/pve/lxc/<container-id>.conf" \
"$(translate "This explicitly marks the container as privileged")"
show_command "7" \
"$(translate "Start the converted container:")" \
"pct start <container-id>" \
"$(translate "The container should now start as privileged")"
show_command "8" \
"$(translate "Verify the conversion:")" \
"pct config <container-id> | grep unprivileged" \
"$(translate "Should show 'unprivileged: 0' or no unprivileged line")"
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
echo -e
echo -e "${TAB}${BOLD}$(translate "SECURITY CONSIDERATIONS:")${CL}"
echo -e
echo -e "${TAB}${RD}$(translate "Privileged containers can access host devices directly")${CL}"
echo -e "${TAB}${RD}$(translate "Root inside container = root on host system")${CL}"
echo -e "${TAB}${RD}$(translate "Use only when unprivileged containers cannot meet your needs")${CL}"
echo -e "${TAB}${RD}$(translate "Consider security implications for production environments")${CL}"
echo -e
echo -e
echo -e "${TAB}${BOLD}$(translate "TROUBLESHOOTING:")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "If container won't start:")${CL} ${YW}Check /var/log/pve/tasks/ for detailed error messages${CL}"
echo -e "${TAB}${BGN}$(translate "To revert changes:")${CL} ${YW}cp /etc/pve/lxc/<container-id>.conf.bak /etc/pve/lxc/<container-id>.conf${CL}"
echo -e "${TAB}${BGN}$(translate "If config issues occur:")${CL} ${YW}Manually edit /etc/pve/lxc/<container-id>.conf${CL}"
echo -e
echo -e
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
echo -e
read -r
}
show_lxc_conversion_manual_menu() {
while true; do
CHOICE=$(dialog --title "$(translate "LXC Conversion Manual Guides")" \
--menu "$(translate "Select conversion guide:")" 18 70 10 \
"1" "$(translate "Convert Privileged to Unprivileged")" \
"2" "$(translate "Convert Unprivileged to Privileged")" \
"3" "$(translate "Return to Main Menu")" \
3>&1 1>&2 2>&3)
case $CHOICE in
1) show_privileged_to_unprivileged_guide ;;
2) show_unprivileged_to_privileged_guide ;;
3) return ;;
*) return ;;
esac
done
}
# Main execution
show_lxc_conversion_manual_menu

View File

@@ -0,0 +1,271 @@
#!/bin/bash
# ==========================================================
# ProxMenu - LXC Privileged to Unprivileged Converter
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.1
# Last Updated: 19/08/2025
# ==========================================================
# Description:
# This script converts a privileged LXC container to an unprivileged one
# using the direct conversion method (mount and chown).
# ==========================================================
# Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
select_privileged_container() {
CONTAINERS=$(pct list | awk 'NR>1 {print $1, $3}' | while read id name; do
if pct config "$id" | grep -q "^unprivileged: 0" || ! pct config "$id" | grep -q "^unprivileged:"; then
echo "$id" "$name"
fi
done | xargs -n2)
if [ -z "$CONTAINERS" ]; then
msg_error "$(translate 'No privileged containers available in Proxmox.')"
exit 1
fi
cleanup
CONTAINER_ID=$(whiptail --title "$(translate 'Select Privileged Container')" \
--menu "$(translate 'Select the privileged LXC container to convert:')" 20 70 10 $CONTAINERS 3>&1 1>&2 2>&3)
if [ -z "$CONTAINER_ID" ]; then
msg_error "$(translate 'No container selected. Exiting.')"
exit 1
fi
msg_ok "$(translate 'Privileged container selected:') $CONTAINER_ID"
}
validate_container_id() {
if [ -z "$CONTAINER_ID" ]; then
msg_error "$(translate 'Container ID not defined. Make sure to select a container first.')"
exit 1
fi
if pct config "$CONTAINER_ID" | grep -q "^unprivileged: 1"; then
msg_error "$(translate 'Container') $CONTAINER_ID $(translate 'is already unprivileged.')"
exit 1
fi
if pct status "$CONTAINER_ID" | grep -q "running"; then
msg_info "$(translate 'Stopping the container before conversion...')"
pct stop "$CONTAINER_ID"
msg_ok "$(translate 'Container stopped.')"
fi
}
show_backup_warning() {
local message="$(translate 'It is strongly recommended to create a backup of your container before proceeding with the conversion.')"
message="$message\n\n$(translate 'Do you want to continue with the conversion now, or exit to create a backup first?')"
message="$message\n\n$(translate 'Continue: Proceed with conversion')"
message="$message\n$(translate 'Exit: Stop to create backup manually')"
if whiptail --title "$(translate 'Backup Recommendation')" \
--yes-button "$(translate 'Continue')" \
--no-button "$(translate 'Exit')" \
--yesno "$message" 18 80; then
return 0
else
msg_info2 "$(translate 'User chose to exit for manual backup creation.')"
exit 0
fi
}
convert_direct_method() {
msg_info2 "$(translate 'Starting direct conversion of container') $CONTAINER_ID..."
TEMP_DIR="/tmp/lxc_convert_$CONTAINER_ID"
mkdir -p "$TEMP_DIR"
ROOTFS_CONFIG=$(pct config "$CONTAINER_ID" | grep "^rootfs:")
if [ -z "$ROOTFS_CONFIG" ]; then
msg_error "$(translate 'Could not find rootfs configuration for container.')"
exit 1
fi
STORAGE_DISK=$(echo "$ROOTFS_CONFIG" | awk '{print $2}' | cut -d, -f1)
msg_ok "$(translate 'Storage disk identifier:') $STORAGE_DISK"
DISK_PATH=$(pvesm path "$STORAGE_DISK" 2>/dev/null)
if [ -n "$DISK_PATH" ] && [ -e "$DISK_PATH" ]; then
msg_ok "$(translate 'Disk path resolved via pvesm:') $DISK_PATH"
else
STORAGE_NAME=$(echo "$STORAGE_DISK" | cut -d: -f1)
DISK_NAME=$(echo "$STORAGE_DISK" | cut -d: -f2)
msg_info2 "$(translate 'pvesm path failed, trying manual detection...')"
msg_info2 "$(translate 'Storage:') $STORAGE_NAME, $(translate 'Disk:') $DISK_NAME"
for vg in pve $(vgs --noheadings -o vg_name 2>/dev/null | tr -d ' '); do
if [ -e "/dev/$vg/$DISK_NAME" ]; then
DISK_PATH="/dev/$vg/$DISK_NAME"
break
fi
done
if [ -z "$DISK_PATH" ] || [ ! -e "$DISK_PATH" ]; then
ZFS_PATH="/dev/zvol/$STORAGE_NAME/$DISK_NAME"
if [ -e "$ZFS_PATH" ]; then
DISK_PATH="$ZFS_PATH"
fi
fi
fi
if [ -z "$DISK_PATH" ] || [ ! -e "$DISK_PATH" ]; then
msg_error "$(translate 'Could not determine disk path for:') $STORAGE_DISK"
msg_error "$(translate 'Tried pvesm path and manual detection methods')"
msg_info2 "$(translate 'Available storage information:')"
pvesm status 2>/dev/null || msg_error "$(translate 'pvesm status failed')"
rmdir "$TEMP_DIR"
exit 1
fi
msg_ok "$(translate 'Mounting container filesystem')"
if ! mount "$DISK_PATH" "$TEMP_DIR" 2>/dev/null; then
if ! mount -o loop "$DISK_PATH" "$TEMP_DIR" 2>/dev/null; then
msg_error "$(translate 'Failed to mount container filesystem.')"
msg_error "$(translate 'Disk path:') $DISK_PATH"
msg_success "$(translate "Press Enter to return")"
read -r
rmdir "$TEMP_DIR"
exit 1
fi
fi
msg_info "$(translate 'Converting file ownership (this may take several minutes)...')"
find "$TEMP_DIR" -type f -print0 | while IFS= read -r -d '' S; do
if [ ! -e "$S" ] || [ ! -r "$S" ]; then
continue
fi
if STAT_OUTPUT=$(stat -c "%u %g" "$S" 2>/dev/null); then
U=$(echo "$STAT_OUTPUT" | awk '{print $1}')
G=$(echo "$STAT_OUTPUT" | awk '{print $2}')
F=100000
NEW_UID=$((F + U))
NEW_GID=$((F + G))
if ! chown "$NEW_UID:$NEW_GID" "$S" 2>/dev/null; then
continue
fi
fi
done
find "$TEMP_DIR" -type d -print0 | while IFS= read -r -d '' S; do
if [ ! -e "$S" ] || [ ! -r "$S" ]; then
continue
fi
if STAT_OUTPUT=$(stat -c "%u %g" "$S" 2>/dev/null); then
U=$(echo "$STAT_OUTPUT" | awk '{print $1}')
G=$(echo "$STAT_OUTPUT" | awk '{print $2}')
F=100000
NEW_UID=$((F + U))
NEW_GID=$((F + G))
if ! chown "$NEW_UID:$NEW_GID" "$S" 2>/dev/null; then
continue
fi
fi
done
[ -e "$TEMP_DIR/var/spool/postfix/dev/-random" ] && rm -f "$TEMP_DIR/var/spool/postfix/dev/-random"
[ -e "$TEMP_DIR/var/spool/postfix/dev/-urandom" ] && rm -f "$TEMP_DIR/var/spool/postfix/dev/-urandom"
[ -e "$TEMP_DIR/usr/bin/sudo" ] && chmod u+s "$TEMP_DIR/usr/bin/sudo"
umount "$TEMP_DIR"
rmdir "$TEMP_DIR"
CONFIG_FILE="/etc/pve/lxc/$CONTAINER_ID.conf"
if ! grep -q "^unprivileged:" "$CONFIG_FILE"; then
echo "unprivileged: 1" >> "$CONFIG_FILE"
else
sed -i 's/^unprivileged:.*/unprivileged: 1/' "$CONFIG_FILE"
fi
msg_ok "$(translate 'Direct conversion completed for container') $CONTAINER_ID"
echo -e
msg_success "Press Enter to continue..."
read -r
}
cleanup_and_finalize() {
if whiptail --yesno "$(translate 'Do you want to start the converted unprivileged container') $CONTAINER_ID $(translate 'now?')" 10 60; then
msg_info2 "$(translate 'Starting unprivileged container...')"
pct start "$CONTAINER_ID"
msg_ok "$(translate 'Unprivileged container') $CONTAINER_ID $(translate 'started successfully.')"
fi
}
main() {
show_proxmenux_logo
msg_title "$(translate "LXC Privileged to Unprivileged conversion")"
msg_info "$(translate 'Starting LXC Privileged to Unprivileged conversion process...')"
select_privileged_container
validate_container_id
show_backup_warning
convert_direct_method
cleanup_and_finalize
msg_ok "$(translate 'Converted container ID:') $CONTAINER_ID"
msg_ok "$(translate 'LXC conversion from privileged to unprivileged completed successfully!')"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
exit 0
}
main

View File

@@ -0,0 +1,144 @@
#!/bin/bash
# ==========================================================
# ProxMenu - LXC Unprivileged to Privileged Converter
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 2.0
# Last Updated: 19/08/2025
# ==========================================================
# Description:
# This script converts an unprivileged LXC container to a privileged one
# by directly modifying the configuration file.
# WARNING: This reduces security. Use only when necessary.
# ==========================================================
# Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
select_unprivileged_container() {
CONTAINERS=$(pct list | awk 'NR>1 {print $1, $3}' | while read id name; do
if pct config "$id" | grep -q "^unprivileged: 1"; then
echo "$id" "$name"
fi
done | xargs -n2)
if [ -z "$CONTAINERS" ]; then
msg_error "$(translate 'No unprivileged containers available in Proxmox.')"
exit 1
fi
cleanup
CONTAINER_ID=$(whiptail --title "$(translate 'Select Unprivileged Container')" \
--menu "$(translate 'Select the unprivileged LXC container to convert:')" 20 70 10 $CONTAINERS 3>&1 1>&2 2>&3)
if [ -z "$CONTAINER_ID" ]; then
msg_error "$(translate 'No container selected. Exiting.')"
exit 1
fi
msg_ok "$(translate 'Unprivileged container selected:') $CONTAINER_ID"
}
show_backup_warning() {
if ! whiptail --title "$(translate 'Backup Recommendation')" \
--yes-button "$(translate 'Continue')" \
--no-button "$(translate 'Exit')" \
--yesno "$(translate 'It is recommended to create a backup before continuing.')" \
12 70; then
msg_info "$(translate 'Operation cancelled by user to create backup.')"
exit 0
fi
}
convert_to_privileged() {
CONF_FILE="/etc/pve/lxc/$CONTAINER_ID.conf"
CONTAINER_STATUS=$(pct status "$CONTAINER_ID" | awk '{print $2}')
if [ "$CONTAINER_STATUS" == "running" ]; then
msg_info "$(translate 'Stopping container') $CONTAINER_ID..."
pct shutdown "$CONTAINER_ID"
# Wait for container to stop
for i in {1..10}; do
sleep 1
if [ "$(pct status "$CONTAINER_ID" | awk '{print $2}')" != "running" ]; then
break
fi
done
# Verify container stopped
if [ "$(pct status "$CONTAINER_ID" | awk '{print $2}')" == "running" ]; then
msg_error "$(translate 'Failed to stop the container.')"
exit 1
fi
msg_ok "$(translate 'Container stopped.')"
else
msg_ok "$(translate 'Container is already stopped.')"
fi
msg_info "$(translate 'Creating backup of configuration file...')"
cp "$CONF_FILE" "$CONF_FILE.bak"
msg_ok "$(translate 'Configuration backup created:') $CONF_FILE.bak"
msg_info "$(translate 'Converting container to privileged...')"
sed -i '/^unprivileged: 1/d' "$CONF_FILE"
echo "unprivileged: 0" >> "$CONF_FILE"
msg_ok "$(translate 'Container successfully converted to privileged.')"
echo -e
msg_success "Press Enter to continue..."
read -r
}
finalize_conversion() {
if whiptail --yesno "$(translate 'Do you want to start the privileged container') $CONTAINER_ID $(translate 'now?')" 10 60; then
msg_info "$(translate 'Starting privileged container...')"
pct start "$CONTAINER_ID"
msg_ok "$(translate 'Privileged container') $CONTAINER_ID $(translate 'started successfully.')"
fi
}
main() {
show_proxmenux_logo
msg_title "$(translate "LXC Unprivileged to Privileged conversion")"
msg_info "$(translate 'Starting LXC Unprivileged to Privileged conversion process...')"
select_unprivileged_container
show_backup_warning
convert_to_privileged
finalize_conversion
msg_ok "$(translate 'LXC conversion from unprivileged to privileged completed successfully!')"
msg_ok "$(translate 'Converted container ID:') $CONTAINER_ID"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
exit 0
}
# Execute main function
main

104
scripts/menus/lxc_menu.sh Normal file
View File

@@ -0,0 +1,104 @@
#!/bin/bash
# ==========================================================
# ProxMenu - LXC Conversion Management Menu
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.0
# Last Updated: 19/08/2025
# ==========================================================
# Description:
# This script provides a menu interface for LXC container privilege conversions.
# Allows converting between privileged and unprivileged containers safely.
# ==========================================================
# Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
show_main_menu() {
CHOICE=$(dialog --backtitle "ProxMenux" --title "$(translate 'LXC Management')" \
--menu "$(translate 'Select conversion option:')" 20 70 10 \
"1" "$(translate 'Convert Privileged to Unprivileged')" \
"2" "$(translate 'Convert Unprivileged to Privileged')" \
"3" "$(translate 'Show Container Privilege Status')" \
"4" "$(translate "Help & Info (commands)")" \
"5" "$(translate 'Exit')" 3>&1 1>&2 2>&3)
case $CHOICE in
1)
bash <(curl -s "$REPO_URL/scripts/lxc/lxc-privileged-to-unprivileged.sh")
;;
2)
bash <(curl -s "$REPO_URL/scripts/lxc/lxc-unprivileged-to-privileged.sh")
;;
3)
show_container_status
;;
4)
bash <(curl -s "$REPO_URL/scripts/lxc/lxc-conversion-manual-guide.sh")
;;
5)
exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh")
;;
*)
exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh")
esac
}
show_container_status() {
msg_info "$(translate 'Gathering container privilege information...')"
TEMP_FILE=$(mktemp)
echo "$(translate 'LXC Container Privilege Status')" > "$TEMP_FILE"
echo "=================================" >> "$TEMP_FILE"
echo "" >> "$TEMP_FILE"
pct list | awk 'NR>1 {print $1, $3}' | while read id name; do
if pct config "$id" | grep -q "^unprivileged: 1"; then
status="$(translate 'Unprivileged')"
else
status="$(translate 'Privileged')"
fi
running_status=$(pct status "$id" | grep -q "running" && echo "$(translate 'Running')" || echo "$(translate 'Stopped')")
printf "ID: %-4s | %-20s | %-12s | %s\n" "$id" "$name" "$status" "$running_status" >> "$TEMP_FILE"
done
echo "" >> "$TEMP_FILE"
echo "$(translate 'Legend:')" >> "$TEMP_FILE"
echo "$(translate 'Privileged: Full host access (less secure)')" >> "$TEMP_FILE"
echo "$(translate 'Unprivileged: Limited access (more secure)')" >> "$TEMP_FILE"
cleanup
dialog --title "$(translate 'Container Status')" --textbox "$TEMP_FILE" 25 80
rm -f "$TEMP_FILE"
show_main_menu
}
show_main_menu

View File

@@ -16,27 +16,87 @@ BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
if ! command -v dialog &>/dev/null; then
apt update -qq >/dev/null 2>&1
apt install -y dialog >/dev/null 2>&1
fi
check_pve9_translation_compatibility() {
local pve_version
if command -v pveversion &>/dev/null; then
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
else
return 0
fi
if [[ -n "$pve_version" ]] && [[ "$pve_version" -ge 9 ]] && [[ -d "$VENV_PATH" ]]; then
local has_googletrans=false
local has_cache=false
if [[ -f "$VENV_PATH/bin/pip" ]]; then
if "$VENV_PATH/bin/pip" list 2>/dev/null | grep -q "googletrans"; then
has_googletrans=true
fi
fi
if [[ -f "$BASE_DIR/cache.json" ]]; then
has_cache=true
fi
if [[ "$has_googletrans" = true ]] || [[ "$has_cache" = true ]]; then
dialog --clear \
--backtitle "ProxMenux - Compatibility Required" \
--title "Translation Environment Incompatible with PVE $pve_version" \
--msgbox "NOTICE: You are running Proxmox VE $pve_version with translation components installed.\n\nTranslations are NOT supported in PVE 9+. This causes:\n• Menu loading errors\n• Translation failures\n• System instability\n\nREQUIRED ACTION:\nProxMenux will now automatically reinstall the Normal Version.\n\nThis process will:\n• Remove incompatible translation components\n• Install PVE 9+ compatible version\n• Preserve all your settings and preferences\n\nPress OK to continue with automatic reinstallation..." 20 75
bash <(curl -sSL "$REPO_URL/install_proxmenux.sh")
fi
exit
fi
}
check_pve9_translation_compatibility
# ==========================================================
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ "$PROXMENUX_PVE9_WARNING_SHOWN" = "1" ]]; then
if ! load_language 2>/dev/null; then
LANGUAGE="en"
fi
else
load_language
initialize_cache
fi
# ==========================================================
show_menu() {
local TEMP_FILE
TEMP_FILE=$(mktemp)
while true; do
local menu_title="Main ProxMenux"
if [[ -n "$PROXMENUX_PVE9_WARNING_SHOWN" ]]; then
menu_title="Main ProxMenux"
fi
dialog --clear \
--backtitle "ProxMenux" \
--title "$(translate "Main ProxMenux")" \
--title "$(translate "$menu_title")" \
--menu "$(translate "Select an option:")" 20 70 10 \
1 "$(translate "Settings post-install Proxmox")" \
2 "$(translate "Help and Info Commands")" \
@@ -52,7 +112,6 @@ show_menu() {
local EXIT_STATUS=$?
if [[ $EXIT_STATUS -ne 0 ]]; then
# ESC pressed or Cancel
clear
msg_ok "$(translate "Thank you for using ProxMenux. Goodbye!")"
rm -f "$TEMP_FILE"
@@ -77,6 +136,4 @@ show_menu() {
done
}
show_menu

View File

@@ -0,0 +1,82 @@
#!/bin/bash
# ==========================================================
# ProxMenu - A menu-driven script for Proxmox VE management
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 2.0
# Last Updated: 04/04/2025
# ==========================================================
# Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
if ! command -v dialog &>/dev/null; then
apt update -qq >/dev/null 2>&1
apt install -y dialog >/dev/null 2>&1
fi
show_menu() {
local TEMP_FILE
TEMP_FILE=$(mktemp)
while true; do
dialog --clear \
--backtitle "ProxMenux" \
--title "$(translate "Main ProxMenux")" \
--menu "$(translate "Select an option:")" 20 70 10 \
1 "$(translate "Settings post-install Proxmox")" \
2 "$(translate "Help and Info Commands")" \
3 "$(translate "Hardware: GPUs and Coral-TPU")" \
4 "$(translate "Create VM from template or script")" \
5 "$(translate "Disk and Storage Manager")" \
6 "$(translate "Proxmox VE Helper Scripts")" \
7 "$(translate "Network Management")" \
8 "$(translate "Utilities and Tools")" \
9 "$(translate "Settings")" \
0 "$(translate "Exit")" 2>"$TEMP_FILE"
local EXIT_STATUS=$?
if [[ $EXIT_STATUS -ne 0 ]]; then
# ESC pressed or Cancel
clear
msg_ok "$(translate "Thank you for using ProxMenux. Goodbye!")"
rm -f "$TEMP_FILE"
exit 0
fi
OPTION=$(<"$TEMP_FILE")
case $OPTION in
1) exec bash <(curl -s "$REPO_URL/scripts/menus/menu_post_install.sh") ;;
2) bash <(curl -s "$REPO_URL/scripts/help_info_menu.sh") ;;
3) exec bash <(curl -s "$REPO_URL/scripts/menus/hw_grafics_menu.sh") ;;
4) exec bash <(curl -s "$REPO_URL/scripts/menus/create_vm_menu.sh") ;;
5) exec bash <(curl -s "$REPO_URL/scripts/menus/storage_menu.sh") ;;
6) exec bash <(curl -s "$REPO_URL/scripts/menus/menu_Helper_Scripts.sh") ;;
7) exec bash <(curl -s "$REPO_URL/scripts/menus/network_menu.sh") ;;
8) exec bash <(curl -s "$REPO_URL/scripts/menus/utilities_menu.sh") ;;
9) exec bash <(curl -s "$REPO_URL/scripts/menus/config_menu.sh") ;;
0) clear; msg_ok "$(translate "Thank you for using ProxMenu. Goodbye!")"; rm -f "$TEMP_FILE"; exit 0 ;;
*) msg_warn "$(translate "Invalid option")"; sleep 2 ;;
esac
done
}
show_menu

View File

@@ -0,0 +1,88 @@
#!/bin/bash
# ==========================================================
# ProxMenux - Network Storage Manager Menu
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT
# Version : 1.2
# Last Updated: $(date +%d/%m/%Y)
# ==========================================================
# Configuration
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
while true; do
OPTION=$(dialog --colors --backtitle "ProxMenux" \
--title "$(translate "Mount and Share Manager")" \
--menu "\n$(translate "Select an option:")" 25 80 15 \
"" "\Z4──────────────────────── $(translate "HOST") ─────────────────────────\Zn" \
"1" "$(translate "Configure NFS shared on Host")" \
"2" "$(translate "Configure Samba shared on Host")" \
"3" "$(translate "Configure Local Shared on Host")" \
"" "\Z4──────────────────────── $(translate "LXC") ─────────────────────────\Zn" \
"4" "$(translate "Configure LXC Mount Points (Host ↔ Container)")" \
"" "" \
"5" "$(translate "Configure NFS Client in LXC (only privileged)")" \
"6" "$(translate "Configure Samba Client in LXC (only privileged)")" \
"7" "$(translate "Configure NFS Server in LXC (only privileged)")" \
"8" "$(translate "configure Samba Server in LXC (only privileged)")" \
"" "" \
"h" "$(translate "Help & Info (commands)")" \
"0" "$(translate "Return to Main Menu")" \
2>&1 >/dev/tty
) || { exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh"); }
case "$OPTION" in
lxctitle|hosttitle)
continue
;;
1)
bash <(curl -s "$REPO_URL/scripts/share/nfs_host.sh")
;;
2)
bash <(curl -s "$REPO_URL/scripts/share/samba_host.sh")
;;
3)
bash <(curl -s "$REPO_URL/scripts/share/local-shared-manager.sh")
;;
4)
bash <(curl -s "$REPO_URL/scripts/share/lxc-mount-manager_minimal.sh")
;;
5)
bash <(curl -s "$REPO_URL/scripts/share/nfs_client.sh")
;;
6)
bash <(curl -s "$REPO_URL/scripts/share/samba_client.sh")
;;
7)
bash <(curl -s "$REPO_URL/scripts/share/nfs_lxc_server.sh")
;;
8)
bash <(curl -s "$REPO_URL/scripts/share/samba_lxc_server.sh")
;;
h)
bash <(curl -s "$REPO_URL/scripts/share/commands_share.sh")
;;
0)
exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh")
;;
*)
exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh")
;;
esac
done

View File

@@ -36,15 +36,12 @@ while true; do
case $OPTION in
1)
clear
bash <(curl -s "$REPO_URL/scripts/storage/disk-passthrough.sh")
;;
2)
clear
bash <(curl -s "$REPO_URL/scripts/storage/disk-passthrough_ct.sh")
;;
3)
clear
bash <(curl -s "$REPO_URL/scripts/storage/import-disk-image.sh")
;;
4)

View File

@@ -26,11 +26,12 @@ initialize_cache
while true; do
OPTION=$(dialog --clear --backtitle "ProxMenux" --title "$(translate "Utilities Menu")" \
--menu "\n$(translate "Select an option:")" 20 70 8 \
--menu "$(translate "Select an option:")" 20 70 8 \
"1" "$(translate "UUp Dump ISO creator Custom")" \
"2" "$(translate "System Utilities Installer")" \
"3" "$(translate "Proxmox System Update")" \
"4" "$(translate "Return to Main Menu")" \
"4" "$(translate "Upgrade PVE 8 to PVE 9")" \
"5" "$(translate "Return to Main Menu")" \
2>&1 >/dev/tty)
case $OPTION in
@@ -69,7 +70,13 @@ initialize_cache
fi
fi
;;
4) exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh") ;;
4)
bash <(curl -s "$REPO_URL/scripts/utilities/upgrade_pve8_to_pve9.sh")
if [ $? -ne 0 ]; then
return
fi
;;
5) exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh") ;;
*) exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh") ;;
esac
done

View File

@@ -164,15 +164,17 @@ remove_subscription_banner() {
# ==========================================================
configure_time_sync() {
msg_info "$(translate "Configuring system time settings...")"
configure_time_sync_() {
msg_info2 "$(translate "Configuring system time settings...")"
# Get public IP address
this_ip=$(dig +short myip.opendns.com @resolver1.opendns.com)
if [ -z "$this_ip" ]; then
msg_warn "$(translate "Failed to obtain public IP address")"
timezone="UTC"
else
# Get timezone based on IP
timezone=$(curl -s "https://ipapi.co/${this_ip}/timezone")
if [ -z "$timezone" ]; then
msg_warn "$(translate "Failed to determine timezone from IP address")"
@@ -182,16 +184,64 @@ configure_time_sync() {
fi
fi
# Set the timezone
if timedatectl set-timezone "$timezone"; then
msg_ok "$(translate "Timezone set to $timezone")"
else
msg_error "$(translate "Failed to set timezone to $timezone")"
fi
# Configure time synchronization
msg_info "$(translate "Enabling automatic time synchronization...")"
if timedatectl set-ntp true; then
msg_ok "$(translate "Time settings configured - Timezone:") $timezone"
systemctl restart postfix 2>/dev/null || true
msg_ok "$(translate "Automatic time synchronization enabled")"
register_tool "time_sync" true
else
msg_error "$(translate "Failed to enable automatic time synchronization")"
fi
}
configure_time_sync() {
msg_info2 "$(translate "Configuring system time settings...")"
this_ip=$(dig +short myip.opendns.com @resolver1.opendns.com 2>/dev/null)
if [ -z "$this_ip" ]; then
msg_warn "$(translate "Failed to obtain public IP address - keeping current timezone settings")"
return 0
fi
timezone=$(curl -s --connect-timeout 10 "https://ipapi.co/${this_ip}/timezone" 2>/dev/null)
if [ -z "$timezone" ] || [ "$timezone" = "undefined" ]; then
msg_warn "$(translate "Failed to determine timezone from IP address - keeping current timezone settings")"
return 0
fi
msg_ok "$(translate "Found timezone $timezone for IP $this_ip")"
if timedatectl set-timezone "$timezone"; then
msg_ok "$(translate "Timezone set to $timezone")"
if timedatectl set-ntp true; then
msg_ok "$(translate "Time settings configured - Timezone:") $timezone"
register_tool "time_sync" true
systemctl restart postfix 2>/dev/null || true
else
msg_warn "$(translate "Failed to enable automatic time synchronization")"
fi
else
msg_warn "$(translate "Failed to set timezone - keeping current settings")"
fi
}
# ==========================================================
skip_apt_languages() {
msg_info "$(translate "Configuring APT to skip downloading additional languages...")"
@@ -228,11 +278,10 @@ optimize_journald() {
[Journal]
Storage=persistent
SplitMode=none
RateLimitInterval=0
RateLimitIntervalSec=0
RateLimitBurst=0
RateLimitIntervalSec=30s
RateLimitBurst=1000
ForwardToSyslog=no
ForwardToWall=yes
ForwardToWall=no
Seal=no
Compress=yes
SystemMaxUse=64M
@@ -418,68 +467,69 @@ force_apt_ipv4() {
}
# ==========================================================
apply_network_optimizations() {
msg_info "$(translate "Optimizing network settings...")"
NECESSARY_REBOOT=1
cat <<EOF > /etc/sysctl.d/99-network.conf
net.core.netdev_max_backlog=8192
net.core.optmem_max=8192
net.core.rmem_max=16777216
net.core.somaxconn=8151
net.core.wmem_max=16777216
cat <<'EOF' > /etc/sysctl.d/99-network.conf
# ==========================================================
# ProxMenux - Network tuning (PVE 9 compatible)
# ==========================================================
# Core buffers & queues
net.core.netdev_max_backlog = 8192
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.somaxconn = 8192
# IPv4
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.log_martians = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.default.log_martians = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.default.log_martians = 1
# rp_filter: loose multi-homed/bridges
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
# ICMP
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.ip_local_port_range=1024 65535
net.ipv4.tcp_base_mss = 1024
net.ipv4.tcp_challenge_ack_limit = 999999999
net.ipv4.tcp_fin_timeout=10
net.ipv4.tcp_keepalive_intvl=30
net.ipv4.tcp_keepalive_probes=3
net.ipv4.tcp_keepalive_time=240
net.ipv4.tcp_limit_output_bytes=65536
net.ipv4.tcp_max_syn_backlog=8192
net.ipv4.tcp_max_tw_buckets = 1440000
# TCP/IP
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_rfc1337=1
net.ipv4.tcp_rmem=8192 87380 16777216
net.ipv4.tcp_sack=1
net.ipv4.tcp_slow_start_after_idle=0
net.ipv4.tcp_syn_retries=3
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_tw_reuse = 0
net.ipv4.tcp_wmem=8192 65536 16777216
net.netfilter.nf_conntrack_generic_timeout = 60
net.netfilter.nf_conntrack_helper=0
net.netfilter.nf_conntrack_max = 524288
net.netfilter.nf_conntrack_tcp_timeout_established = 28800
net.ipv4.tcp_rfc1337 = 1
net.ipv4.tcp_sack = 1
net.ipv4.tcp_rmem = 8192 87380 16777216
net.ipv4.tcp_wmem = 8192 65536 16777216
# Unix sockets
net.unix.max_dgram_qlen = 4096
EOF
sysctl --system > /dev/null 2>&1
local interfaces_file="/etc/network/interfaces"
if ! grep -q 'source /etc/network/interfaces.d/*' "$interfaces_file"; then
echo "source /etc/network/interfaces.d/*" >> "$interfaces_file"
fi
msg_ok "$(translate "Network optimization completed")"
register_tool "network_optimization" true
}
# ==========================================================
disable_rpc() {
msg_info "$(translate "Disabling portmapper/rpcbind for security...")"
@@ -596,7 +646,8 @@ install_log2ram_auto() {
else
ROOT_PART=$(lsblk -no NAME,MOUNTPOINT | grep ' /$' | awk '{print $1}')
SYSTEM_DISK=$(lsblk -no PKNAME /dev/$ROOT_PART 2>/dev/null)
#SYSTEM_DISK=$(lsblk -no PKNAME /dev/$ROOT_PART 2>/dev/null)
SYSTEM_DISK=$(lsblk -no PKNAME /dev/$ROOT_PART 2>/dev/null | grep -E '^[a-z]+' | head -n1)
SYSTEM_DISK=${SYSTEM_DISK:-sda}
if [[ "$SYSTEM_DISK" == nvme* || "$(cat /sys/block/$SYSTEM_DISK/queue/rotational 2>/dev/null)" == "0" ]]; then
is_ssd=true
@@ -606,11 +657,16 @@ install_log2ram_auto() {
if [[ "$is_ssd" == true ]]; then
msg_ok "$(translate "System disk is SSD or M.2. Proceeding with Log2RAM setup.")"
else
msg_warn "$(translate "System disk is not SSD/M.2. Skipping Log2RAM installation.")"
return 0
if whiptail --yesno "$(translate "Do you want to install Log2RAM anyway to reduce log write load?")" \
10 70 --title "Log2RAM"; then
:
else
return 0
fi
fi
if [[ -f /etc/log2ram.conf ]] && command -v log2ram >/dev/null 2>&1 && systemctl list-units --all | grep -q log2ram; then
msg_ok "$(translate "Log2RAM is already installed and configured correctly.")"
register_tool "log2ram" true
@@ -641,7 +697,7 @@ install_log2ram_auto() {
if ! command -v git >/dev/null 2>&1; then
apt-get update -qq >/dev/null 2>&1
apt-get install -y git >/dev/null 2>&1
msg_ok "$(translate "Git installed successfully")"
#msg_ok "$(translate "Git installed successfully")"
fi
if ! git clone https://github.com/azlux/log2ram.git /tmp/log2ram >/dev/null 2>>/tmp/log2ram_install.log; then
@@ -686,11 +742,18 @@ install_log2ram_auto() {
sed -i "s/^SIZE=.*/SIZE=$LOG2RAM_SIZE/" /etc/log2ram.conf
LOG2RAM_BIN="$(command -v log2ram || echo /usr/local/bin/log2ram)"
rm -f /etc/cron.daily/log2ram /etc/cron.weekly/log2ram /etc/cron.monthly/log2ram 2>/dev/null || true
rm -f /etc/cron.hourly/log2ram
echo "0 */$CRON_HOURS * * * root /usr/sbin/log2ram write" > /etc/cron.d/log2ram
msg_ok "$(translate "log2ram write scheduled every") $CRON_HOURS $(translate "hour(s)")"
{
echo 'MAILTO=""'
echo "0 */$CRON_HOURS * * * root $LOG2RAM_BIN write >/dev/null 2>&1"
} > /etc/cron.d/log2ram
chmod 0644 /etc/cron.d/log2ram
chown root:root /etc/cron.d/log2ram
msg_ok "$(translate "Log2RAM write scheduled every") $CRON_HOURS $(translate "hour(s)")"
cat << 'EOF' > /usr/local/bin/log2ram-check.sh
@@ -706,7 +769,12 @@ fi
EOF
chmod +x /usr/local/bin/log2ram-check.sh
echo "*/5 * * * * root /usr/local/bin/log2ram-check.sh" > /etc/cron.d/log2ram-auto-sync
{
echo 'MAILTO=""'
echo "*/5 * * * * root /usr/local/bin/log2ram-check.sh >/dev/null 2>&1"
} > /etc/cron.d/log2ram-auto-sync
chmod 0644 /etc/cron.d/log2ram-auto-sync
chown root:root /etc/cron.d/log2ram-auto-sync
msg_ok "$(translate "Auto-sync enabled when /var/log exceeds 90% of") $LOG2RAM_SIZE"
register_tool "log2ram" true
@@ -775,8 +843,9 @@ run_complete_optimization() {
ensure_tools_json
apt_upgrade
cleanup
remove_subscription_banner
configure_time_sync
#configure_time_sync
skip_apt_languages
optimize_journald
optimize_logrotate
@@ -786,7 +855,7 @@ run_complete_optimization() {
configure_kernel_panic
force_apt_ipv4
apply_network_optimizations
disable_rpc
#disable_rpc
customize_bashrc
install_log2ram_auto
setup_persistent_network

File diff suppressed because it is too large Load Diff

View File

@@ -489,6 +489,93 @@ uninstall_persistent_network() {
uninstall_amd_fixes() {
msg_info2 "$(translate "Reverting AMD (Ryzen/EPYC) fixes...")"
NECESSARY_REBOOT=1
if grep -q "root=ZFS=" /proc/cmdline 2>/dev/null; then
cmdline_file="/etc/kernel/cmdline"
if [[ -f "$cmdline_file" ]] && grep -q "idle=nomwait" "$cmdline_file"; then
cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)" || {
msg_error "$(translate "Failed to backup $cmdline_file")"
return 1
}
sed -i 's/\bidle=nomwait\b//g; s/[[:space:]]\+/ /g; s/^ //; s/ $//' "$cmdline_file"
if command -v proxmox-boot-tool >/dev/null 2>&1; then
proxmox-boot-tool refresh >/dev/null 2>&1 || {
msg_error "$(translate "Failed to refresh boot configuration")"
return 1
}
fi
msg_ok "$(translate "Removed idle=nomwait from /etc/kernel/cmdline (ZFS)")"
fi
else
grub_file="/etc/default/grub"
if [[ -f "$grub_file" ]] && grep -q 'GRUB_CMDLINE_LINUX_DEFAULT=' "$grub_file"; then
if grep -q "idle=nomwait" "$grub_file"; then
cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)" || {
msg_error "$(translate "Failed to backup $grub_file")"
return 1
}
sed -i -E 's/(GRUB_CMDLINE_LINUX_DEFAULT=")/\1/; s/\bidle=nomwait\b//g' "$grub_file"
awk -F\" '
$1=="GRUB_CMDLINE_LINUX_DEFAULT=" {
gsub(/[[:space:]]+/," ",$2); sub(/^ /,"",$2); sub(/ $/,"",$2)
}1
' OFS="\"" "$grub_file" > "${grub_file}.tmp" && mv "${grub_file}.tmp" "$grub_file"
update-grub >/dev/null 2>&1 || {
msg_error "$(translate "Failed to update GRUB configuration")"
return 1
}
msg_ok "$(translate "Removed idle=nomwait from GRUB configuration")"
fi
fi
fi
kvm_conf="/etc/modprobe.d/kvm.conf"
if [[ -f "$kvm_conf" ]]; then
if grep -Eq '(ignore_msrs|report_ignored_msrs)' "$kvm_conf"; then
cp "$kvm_conf" "${kvm_conf}.bak.$(date +%Y%m%d_%H%M%S)" || {
msg_error "$(translate "Failed to backup $kvm_conf")"
return 1
}
sed -i -E '/ignore_msrs|report_ignored_msrs/d' "$kvm_conf"
if [[ ! -s "$kvm_conf" ]]; then
rm -f "$kvm_conf"
msg_ok "$(translate "Removed empty KVM configuration file")"
else
msg_ok "$(translate "Removed KVM MSR options from configuration")"
fi
update-initramfs -u -k all >/dev/null 2>&1 || true
else
msg_ok "$(translate "KVM MSR options not present, nothing to revert")"
fi
fi
msg_success "$(translate "AMD fixes have been successfully reverted")"
register_tool "amd_fixes" false
}
################################################################
migrate_installed_tools() {
@@ -604,6 +691,7 @@ show_uninstall_menu() {
figurine) desc="Figurine";;
fastfetch) desc="Fastfetch";;
log2ram) desc="Log2ram (SSD Protection)";;
amd_fixes) desc="AMD CPU (Ryzen/EPYC) fixes";;
persistent_network) desc="Setting persistent network interfaces";;
*) desc="$tool";;
esac

View File

@@ -0,0 +1,472 @@
#!/bin/bash
# ==========================================================
# ProxMenu - A menu-driven script for Proxmox VE management
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.5
# Last Updated: 04/08/2025
# ==========================================================
# Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
show_command() {
local step="$1"
local description="$2"
local command="$3"
local note="$4"
local command_extra="$5"
echo -e "${BGN}${step}.${CL} ${BL}${description}${CL}"
echo ""
echo -e "${TAB}${command}"
echo -e
[[ -n "$note" ]] && echo -e "${TAB}${DARK_GRAY}${note}${CL}"
[[ -n "$command_extra" ]] && echo -e "${TAB}${YW}${command_extra}${CL}"
echo ""
}
show_how_to_enter_lxc() {
clear
show_proxmenux_logo
msg_title "$(translate "How to Access an LXC Terminal from Proxmox Host")"
msg_info2 "$(translate "Use these commands on your Proxmox host to access an LXC container's terminal:")"
echo -e
show_command "1" \
"$(translate "Get a list of all your containers:")" \
"pct list" \
"" \
""
show_command "2" \
"$(translate "Enter the container's terminal")" \
"pct enter ${CUS}<container-id>${CL}" \
"$(translate "Replace <container-id> with the actual ID.")"\
"$(translate "For example: pct enter 101")"
show_command "3" \
"$(translate "To exit the container's terminal, press:")" \
"CTRL + D" \
"" \
""
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
show_host_mount_resources_help() {
clear
show_proxmenux_logo
msg_title "$(translate "Mount Remote Resources on Proxmox Host")"
msg_info2 "$(translate "How to mount NFS and Samba shares directly on the Proxmox host. Proxmox already has the necessary tools installed.")"
echo -e
echo -e "${BOLD}${BL}=== MOUNT NFS SHARE ===${CL}"
echo -e
show_command "1" \
"$(translate "Create mount point:")" \
"mkdir -p ${CUS}/mnt/nfs_share${CL}" \
"$(translate "Replace with your preferred path.")" \
""
show_command "2" \
"$(translate "Mount NFS share:")" \
"mount -t nfs ${CUS}192.168.1.100${CL}:${CUS}/path/to/share${CL} ${CUS}/mnt/nfs_share${CL}" \
"$(translate "Replace IP and paths with your values.")" \
""
show_command "3" \
"$(translate "Make permanent (optional):")" \
"echo '${CUS}192.168.1.100${CL}:${CUS}/path/to/share${CL} ${CUS}/mnt/nfs_share${CL} nfs4 rw,hard,intr,_netdev,rsize=1048576,wsize=1048576,timeo=600,retrans=2 0 0' >> /etc/fstab" \
"$(translate "_netdev waits for network before mounting.")" \
""
echo -e "${BOLD}${BL}=== MOUNT SAMBA SHARE ===${CL}"
echo -e
show_command "4" \
"$(translate "Create mount point:")" \
"mkdir -p ${CUS}/mnt/samba_share${CL}" \
"$(translate "Replace with your preferred path.")" \
""
show_command "5" \
"$(translate "Mount Samba share:")" \
"mount -t cifs //${CUS}192.168.1.100${CL}/${CUS}sharename${CL} ${CUS}/mnt/samba_share${CL} -o username=${CUS}user${CL}" \
"$(translate "You will be prompted for password. Replace IP, share and user.")" \
""
show_command "6" \
"$(translate "Make permanent (optional):")" \
"echo '//${CUS}192.168.1.100${CL}/${CUS}sharename${CL} ${CUS}/mnt/samba_share${CL} cifs username=${CUS}user${CL},password=${CUS}pass${CL},_netdev 0 0' >> /etc/fstab" \
"$(translate "Replace with your credentials.")" \
""
echo -e "${BOLD}${BL}=== CREATE LOCAL DIRECTORY ===${CL}"
echo -e
show_command "7" \
"$(translate "Create directory:")" \
"mkdir -p ${CUS}/mnt/local_share${CL}" \
"$(translate "Creates a local directory on Proxmox host.")" \
""
show_command "8" \
"$(translate "Set permissions:")" \
"chmod 755 ${CUS}/mnt/local_share${CL}" \
"$(translate "Sets basic read/write permissions.")" \
""
show_command "9" \
"$(translate "Verify mounts:")" \
"df -h" \
"$(translate "Shows all mounted filesystems.")" \
""
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
show_host_to_lxc_mount_help() {
clear
show_proxmenux_logo
msg_title "$(translate "Mount Host Directory to LXC Container")"
msg_info2 "$(translate "How to mount a Proxmox host directory into an LXC container. Execute these commands on the Proxmox host.")"
echo -e
show_command "1" \
"$(translate "Add mount point to container:")" \
"pct set ${CUS}<container-id>${CL} -mp0 ${CUS}/host/directory${CL},mp=${CUS}/container/path${CL},backup=0,shared=1" \
"$(translate "Replace container-id, host directory and container path.")" \
"$(translate "Example: pct set 101 -mp0 /mnt/shared,mp=/mnt/shared,,backup=0,shared=1")"
show_command "2" \
"$(translate "Restart container:")" \
"pct reboot ${CUS}<container-id>${CL}" \
"$(translate "Required to activate the mount point.")" \
""
show_command "3" \
"$(translate "Verify mount inside container:")" \
"pct enter ${CUS}<container-id>${CL}
df -h | grep ${CUS}/container/path${CL}" \
"$(translate "Check if the directory is mounted.")" \
""
show_command "4" \
"$(translate "Remove mount point (if needed):")" \
"pct set ${CUS}<container-id>${CL} --delete mp0" \
"$(translate "Removes the mount point. Use mp1, mp2, etc. for other mounts.")" \
""
echo -e "${BOR}"
echo -e "${BOLD}$(translate "Notes:")${CL}"
echo -e "${TAB}${BGN}$(translate "Mount indices:")${CL} ${BL}Use mp0, mp1, mp2, etc. for multiple mounts${CL}"
echo -e "${TAB}${BGN}$(translate "Permissions:")${CL} ${BL}May need adjustment depending on directory type${CL}"
echo -e "${TAB}${BGN}$(translate "Container types:")${CL} ${BL}Works with both privileged and unprivileged containers${CL}"
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
show_nfs_server_help() {
clear
show_proxmenux_logo
msg_title "$(translate "NFS Server Installation")"
msg_info2 "$(translate "How to install and configure an NFS server in an LXC container.")"
echo -e
show_command "1" \
"$(translate "Update and install packages:")" \
"apt-get update && apt-get install -y nfs-kernel-server" \
"" \
""
show_command "2" \
"$(translate "Create export directory:")" \
"mkdir -p ${CUS}/mnt/nfs_export${CL}" \
"$(translate "Replace with your preferred path.")" \
""
show_command "3" \
"$(translate "Set directory permissions:")" \
"chmod 755 ${CUS}/mnt/nfs_export${CL}" \
"" \
""
show_command "4.1" \
"$(translate "Configure exports (safe root_squash):")" \
"echo '${CUS}/mnt/nfs_export${CL} ${CUS}192.168.1.0/24${CL}(rw,sync,no_subtree_check,root_squash)' >> /etc/exports" \
"$(translate "Replace directory path and network range.")" \
""
show_command "4.2" \
"$(translate "Or Configure exports (map all users):")" \
"echo '${CUS}/mnt/nfs_export${CL} ${CUS}192.168.1.0/24${CL}(rw,sync,no_subtree_check,all_squash,anonuid=0,anongid=0)' >> /etc/exports" \
"$(translate "Replace directory path and network range.")" \
""
show_command "5" \
"$(translate "Apply configuration:")" \
"exportfs -ra" \
"" \
""
show_command "6" \
"$(translate "Start and enable service:")" \
"systemctl restart nfs-kernel-server
systemctl enable nfs-kernel-server" \
"" \
""
show_command "7" \
"$(translate "Verify exports:")" \
"showmount -e localhost" \
"$(translate "Shows available NFS exports.")" \
""
echo -e "${BOR}"
echo -e "${BOLD}$(translate "Export Options:")${CL}"
echo -e "${TAB}${BGN}$(translate "rw:")${CL} ${BL}Read-write access${CL}"
echo -e "${TAB}${BGN}$(translate "sync:")${CL} ${BL}Synchronous writes${CL}"
echo -e "${TAB}${BGN}$(translate "no_subtree_check:")${CL} ${BL}Improves performance${CL}"
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
show_samba_server_help() {
clear
show_proxmenux_logo
msg_title "$(translate "Samba Server Installation")"
msg_info2 "$(translate "How to install and configure a Samba server in an LXC container.")"
echo -e
show_command "1" \
"$(translate "Update and install packages:")" \
"apt-get update && apt-get install -y samba" \
"" \
""
show_command "2" \
"$(translate "Create share directory:")" \
"mkdir -p ${CUS}/mnt/samba_share${CL}" \
"$(translate "Replace with your preferred path.")" \
""
show_command "3" \
"$(translate "Set directory permissions:")" \
"chmod 755 ${CUS}/mnt/samba_share${CL}" \
"" \
""
show_command "4" \
"$(translate "Create Samba user:")" \
"adduser ${CUS}sambauser${CL}
smbpasswd -a ${CUS}sambauser${CL}" \
"$(translate "Replace with your username. You'll be prompted for password.")" \
""
show_command "5" \
"$(translate "Configure share:")" \
"cat >> /etc/samba/smb.conf << EOF
[shared]
comment = Shared folder
path = ${CUS}/mnt/samba_share${CL}
read only = no
browseable = yes
valid users = ${CUS}sambauser${CL}
EOF" \
"$(translate "Replace path and username.")" \
""
show_command "6" \
"$(translate "Restart and enable service:")" \
"systemctl restart smbd
systemctl enable smbd" \
"" \
""
show_command "7" \
"$(translate "Test configuration:")" \
"smbclient -L localhost -U ${CUS}sambauser${CL}" \
"$(translate "Lists available shares. You'll be prompted for password.")" \
""
echo -e "${BOR}"
echo -e "${BOLD}$(translate "Connection Examples:")${CL}"
echo -e "${TAB}${BGN}$(translate "Windows:")${CL} ${YW}\\\\<server-ip>\\shared${CL}"
echo -e "${TAB}${BGN}$(translate "Linux:")${CL} ${YW}smbclient //server-ip/shared -U sambauser${CL}"
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
show_nfs_client_help() {
clear
show_proxmenux_logo
msg_title "$(translate "NFS Client Configuration")"
msg_info2 "$(translate "How to configure an NFS client in an LXC container.")"
echo -e
show_command "1" \
"$(translate "Update and install packages:")" \
"apt-get update && apt-get install -y nfs-common" \
"" \
""
show_command "2" \
"$(translate "Create mount point:")" \
"mkdir -p ${CUS}/mnt/nfsmount${CL}" \
"$(translate "Replace with your preferred path.")" \
""
show_command "3" \
"$(translate "Mount NFS share:")" \
"mount -t nfs ${CUS}192.168.1.100${CL}:${CUS}/mnt/nfs_export${CL} ${CUS}/mnt/nfsmount${CL}" \
"$(translate "Replace server IP and paths.")" \
""
show_command "4" \
"$(translate "Test access:")" \
"ls -la ${CUS}/mnt/nfsmount${CL}" \
"$(translate "Verify you can access the mounted share.")" \
""
show_command "5" \
"$(translate "Make permanent (optional):")" \
"echo '${CUS}192.168.1.100${CL}:${CUS}/path/to/share${CL} ${CUS}/mnt/nfs_share${CL} nfs4 rw,hard,intr,_netdev,rsize=1048576,wsize=1048576,timeo=600,retrans=2 0 0' >> /etc/fstab" \
"$(translate "Replace with your server IP and paths.")" \
""
show_command "6" \
"$(translate "Verify mount:")" \
"df -h | grep nfs" \
"$(translate "Shows NFS mounts.")" \
""
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
show_samba_client_help() {
clear
show_proxmenux_logo
msg_title "$(translate "Samba Client Configuration")"
msg_info2 "$(translate "How to configure a Samba client in an LXC container.")"
echo -e
show_command "1" \
"$(translate "Update and install packages:")" \
"apt-get update && apt-get install -y cifs-utils" \
"" \
""
show_command "2" \
"$(translate "Create mount point:")" \
"mkdir -p ${CUS}/mnt/sambamount${CL}" \
"$(translate "Replace with your preferred path.")" \
""
show_command "3" \
"$(translate "Mount Samba share:")" \
"mount -t cifs //${CUS}192.168.1.100${CL}/${CUS}shared${CL} ${CUS}/mnt/sambamount${CL} -o username=${CUS}sambauser${CL}" \
"$(translate "Replace server IP, share name and username. You'll be prompted for password.")" \
""
show_command "4" \
"$(translate "Test access:")" \
"ls -la ${CUS}/mnt/sambamount${CL}" \
"$(translate "Verify you can access the mounted share.")" \
""
show_command "5" \
"$(translate "Create credentials file (optional):")" \
"cat > /etc/samba/credentials << EOF
username=${CUS}sambauser${CL}
password=${CUS}your_password${CL}
EOF
chmod 600 /etc/samba/credentials" \
"$(translate "Secure way to store credentials.")" \
""
show_command "6" \
"$(translate "Mount with credentials file:")" \
"mount -t cifs //${CUS}192.168.1.100${CL}/${CUS}shared${CL} ${CUS}/mnt/sambamount${CL} -o credentials=/etc/samba/credentials" \
"$(translate "No password prompt needed.")" \
""
show_command "7" \
"$(translate "Make permanent (optional):")" \
"echo '//${CUS}192.168.1.100${CL}/${CUS}shared${CL} ${CUS}/mnt/sambamount${CL} cifs credentials=/etc/samba/credentials,_netdev 0 0' >> /etc/fstab" \
"$(translate "Replace with your values.")" \
""
show_command "8" \
"$(translate "Verify mount:")" \
"df -h | grep cifs" \
"$(translate "Shows CIFS/Samba mounts.")" \
""
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
show_help_menu() {
while true; do
CHOICE=$(dialog --title "$(translate "Help & Information")" \
--menu "$(translate "Select help topic:")" 24 80 14 \
"0" "$(translate "How to Access an LXC Terminal")" \
"1" "$(translate "Mount Remote Resources on Proxmox Host")" \
"2" "$(translate "Mount Host Directory to LXC Container")" \
"3" "$(translate "NFS Server Installation")" \
"4" "$(translate "Samba Server Installation")" \
"5" "$(translate "NFS Client Configuration")" \
"6" "$(translate "Samba Client Configuration")" \
"7" "$(translate "Return to Main Menu")" \
3>&1 1>&2 2>&3)
case $CHOICE in
0) show_how_to_enter_lxc ;;
1) show_host_mount_resources_help ;;
2) show_host_to_lxc_mount_help ;;
3) show_nfs_server_help ;;
4) show_samba_server_help ;;
5) show_nfs_client_help ;;
6) show_samba_client_help ;;
7) return ;;
*) return ;;
esac
done
}
show_help_menu

View File

@@ -0,0 +1,271 @@
#!/usr/bin/env bash
# ==========================================================
# ProxMenux - Shared Groups Manager
# ==========================================================
# Author : MacRimi
# Description : Manage host groups for shared directories
# ==========================================================
# Configuration
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
pmx_list_groups() {
local groups
groups=$(getent group | awk -F: '$3 >= 1000 && $1 != "nogroup" && $1 !~ /^pve/ {print $1 ":" $3}')
if [[ -z "$groups" ]]; then
whiptail --title "$(translate "Groups")" --msgbox "$(translate "No user groups found.")" 8 60
return
fi
show_proxmenux_logo
msg_title "$(translate "Existing Groups")"
echo "$groups" | column -t -s: | while read -r name gid; do
members=$(getent group "$name" | awk -F: '{print $4}')
echo -e "${BL}$name${CL} (GID: $gid) -> ${YW}${members:-no members}${CL}"
done
echo ""
msg_success "$(translate "Press Enter to continue...")"
read -r
}
pmx_create_group() {
group_name=$(dialog --inputbox "$(translate "Enter new group name:")" 10 60 "sharedfiles-new" \
--title "$(translate "New Group")" 3>&1 1>&2 2>&3) || return
[[ -z "$group_name" ]] && return
if getent group "$group_name" >/dev/null; then
dialog --title "$(translate "Error")" --msgbox "$(translate "Group already exists.")" 8 50
return
fi
if groupadd "$group_name"; then
show_proxmenux_logo
msg_title "$(translate "Create Group")"
msg_ok "$(translate "Group created successfully:") $group_name"
else
show_proxmenux_logo
msg_title "$(translate "Create Group")"
msg_error "$(translate "Failed to create group.")"
fi
echo -e
msg_success "$(translate "Press Enter to continue...")"
read -r
}
pmx_edit_group() {
local groups group_name action
groups=$(getent group | awk -F: '$3 >= 1000 && $1 != "nogroup" && $1 !~ /^pve/ {print $1}')
if [[ -z "$groups" ]]; then
dialog --title "$(translate "Error")" --msgbox "$(translate "No groups available to edit.")" 8 50
return
fi
local menu_options=""
while read -r group; do
if [[ -n "$group" ]]; then
local gid=$(getent group "$group" | cut -d: -f3)
menu_options="$menu_options $group \"GID:$gid\""
fi
done <<< "$groups"
group_name=$(eval "dialog --title \"$(translate "Edit Group")\" --menu \
\"$(translate "Select a group:")\" 20 60 10 \
$menu_options 3>&1 1>&2 2>&3")
if [[ -z "$group_name" ]]; then
return
fi
action=$(dialog --title "$(translate "Edit Group")" --menu \
"$(translate "What do you want to edit in group:") $group_name" 15 60 3 \
"rename" "$(translate "Rename group")" \
"gid" "$(translate "Change GID")" \
"users" "$(translate "Add/Remove users")" 3>&1 1>&2 2>&3)
if [[ -z "$action" ]]; then
return
fi
case "$action" in
rename)
new_name=$(dialog --inputbox "$(translate "Enter new group name:")" 10 60 \
"$group_name" --title "$(translate "Rename Group")" 3>&1 1>&2 2>&3)
if [[ -n "$new_name" && "$new_name" != "$group_name" ]]; then
if groupmod -n "$new_name" "$group_name" 2>/dev/null; then
show_proxmenux_logo
msg_title "$(translate "Rename Group")"
msg_ok "$(translate "Group renamed to:") $new_name"
else
show_proxmenux_logo
msg_title "$(translate "Rename Group")"
msg_error "$(translate "Failed to rename group")"
fi
fi
;;
gid)
current_gid=$(getent group "$group_name" | cut -d: -f3)
new_gid=$(dialog --inputbox "$(translate "Enter new GID:")" 10 60 \
"$current_gid" --title "$(translate "Change GID")" 3>&1 1>&2 2>&3)
if [[ -n "$new_gid" && "$new_gid" != "$current_gid" ]]; then
if groupmod -g "$new_gid" "$group_name" 2>/dev/null; then
show_proxmenux_logo
msg_title "$(translate "Change GID")"
msg_ok "$(translate "GID changed to:") $new_gid"
else
show_proxmenux_logo
msg_title "$(translate "Change GID")"
msg_error "$(translate "Failed to change GID")"
fi
fi
;;
users)
user_action=$(dialog --title "$(translate "User Management")" --menu \
"$(translate "Choose an action for group:") $group_name" 15 60 2 \
"add" "$(translate "Add user to group")" \
"remove" "$(translate "Remove user from group")" 3>&1 1>&2 2>&3)
case "$user_action" in
add)
username=$(dialog --inputbox "$(translate "Enter username to add:")" 10 60 \
--title "$(translate "Add User")" 3>&1 1>&2 2>&3)
if [[ -n "$username" ]]; then
if id "$username" >/dev/null 2>&1; then
if usermod -aG "$group_name" "$username" 2>/dev/null; then
show_proxmenux_logo
msg_title "$(translate "Add User")"
msg_ok "$(translate "User added:") $username"
else
show_proxmenux_logo
msg_title "$(translate "Add User")"
msg_error "$(translate "Failed to add user")"
fi
else
show_proxmenux_logo
msg_title "$(translate "Add User")"
msg_error "$(translate "User does not exist:") $username"
fi
fi
;;
remove)
members=$(getent group "$group_name" | awk -F: '{print $4}' | tr ',' ' ')
if [[ -z "$members" ]]; then
dialog --title "$(translate "Info")" --msgbox "$(translate "No users in this group.")" 8 50
return
fi
local user_options=""
for user in $members; do
user_options="$user_options $user \"\""
done
username=$(eval "dialog --title \"$(translate "Remove User")\" --menu \
\"$(translate "Select user to remove:")\" 15 60 5 \
$user_options 3>&1 1>&2 2>&3")
if [[ -n "$username" ]]; then
if gpasswd -d "$username" "$group_name" 2>/dev/null; then
show_proxmenux_logo
msg_title "$(translate "Remove User")"
msg_ok "$(translate "User removed:") $username"
else
show_proxmenux_logo
msg_title "$(translate "Remove User")"
msg_error "$(translate "Failed to remove user")"
fi
fi
;;
esac
;;
esac
echo -e
msg_success "$(translate "Press Enter to continue...")"
read -r
}
pmx_delete_group() {
local groups group_name menu_options
groups=$(getent group | awk -F: '$3 >= 1000 && $1 != "nogroup" && $1 !~ /^pve/ {print $1}')
if [[ -z "$groups" ]]; then
dialog --title "$(translate "Error")" --msgbox "$(translate "No groups available to delete.")" 8 50
return
fi
menu_options=""
while read -r group; do
if [[ -n "$group" ]]; then
menu_options="$menu_options $group \"\""
fi
done <<< "$groups"
group_name=$(eval "dialog --title \"$(translate "Delete Group")\" --menu \
\"$(translate "Select a group to delete:")\" 20 60 10 \
$menu_options 3>&1 1>&2 2>&3") || return
if dialog --yesno "$(translate "Are you sure you want to delete group:") $group_name ?" 10 60; then
if groupdel "$group_name" 2>/dev/null; then
show_proxmenux_logo
msg_title "$(translate "Deleting Groups")"
msg_ok "$(translate "Group deleted:") $group_name"
else
show_proxmenux_logo
msg_title "$(translate "Deleting Groups")"
msg_ok "$(translate "Group deleted:") $group_name"
msg_error "$(translate "Failed to delete group")"
fi
fi
echo -e
msg_success "$(translate "Press Enter to continue...")"
read -r
}
pmx_manage_groups() {
while true; do
CHOICE=$(dialog --title "$(translate "Shared Groups Manager")" \
--menu "$(translate "Select an option:")" 20 70 10 \
"list" "$(translate "View existing groups")" \
"create" "$(translate "Create new group")" \
"edit" "$(translate "Edit existing group")" \
"delete" "$(translate "Delete a group")" \
"exit" "$(translate "Exit")" \
3>&1 1>&2 2>&3) || return 0
case "$CHOICE" in
list) pmx_list_groups ;;
create) pmx_create_group ;;
edit) pmx_edit_group ;;
delete) pmx_delete_group ;;
exit) return 0 ;;
esac
done
}
pmx_manage_groups

1292
scripts/share/guia.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
#!/bin/bash
# ==========================================================
# ProxMenux - Local Shared Directory Manager
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT
# Version : 1.0
# Last Updated: $(date +%d/%m/%Y)
# ==========================================================
# Configuration
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
SHARE_COMMON_LOADED=false
else
SHARE_COMMON_LOADED=true
fi
load_language
initialize_cache
# ==========================================================
create_shared_directory() {
SHARED_DIR=$(pmx_select_host_mount_point "$(translate "Select Shared Directory Location")" "/mnt/shared")
[[ -z "$SHARED_DIR" ]] && return
if [[ -d "$SHARED_DIR" ]]; then
if ! whiptail --yesno "$(translate "Directory already exists. Continue with permission setup?")" 10 70 --title "$(translate "Directory Exists")"; then
return
fi
fi
SHARE_GROUP=$(pmx_choose_or_create_group "sharedfiles") || return 1
SHARE_GID=$(pmx_ensure_host_group "$SHARE_GROUP" 101000) || return 1
if command -v setfacl >/dev/null 2>&1; then
setfacl -k /mnt 2>/dev/null || true
setfacl -b /mnt 2>/dev/null || true
fi
chmod 755 /mnt 2>/dev/null || true
pmx_prepare_host_shared_dir "$SHARED_DIR" "$SHARE_GROUP" || return 1
if command -v setfacl >/dev/null 2>&1; then
setfacl -b -R "$SHARED_DIR" 2>/dev/null || true
fi
chown root:"$SHARE_GROUP" "$SHARED_DIR"
chmod 2775 "$SHARED_DIR"
pmx_share_map_set "$SHARED_DIR" "$SHARE_GROUP"
show_proxmenux_logo
msg_title "$(translate "Create Shared Directory")"
echo -e ""
echo -e "${TAB}${BOLD}$(translate "Shared Directory Created:")${CL}"
echo -e "${TAB}${BGN}$(translate "Directory:")${CL} ${BL}$SHARED_DIR${CL}"
echo -e "${TAB}${BGN}$(translate "Group:")${CL} ${BL}$SHARE_GROUP (GID: $SHARE_GID)${CL}"
echo -e "${TAB}${BGN}$(translate "Permissions:")${CL} ${BL}2775 (rwxrwsr-x)${CL}"
echo -e "${TAB}${BGN}$(translate "Owner:")${CL} ${BL}root:$SHARE_GROUP${CL}"
echo -e "${TAB}${BGN}$(translate "ACL Status:")${CL} ${BL}$(translate "Cleaned and set for POSIX inheritance")${CL}"
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
create_shared_directory

View File

@@ -0,0 +1,480 @@
#!/bin/bash
# ==========================================================
# ProxMenux - LXC Mount Manager
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT
# Version : 3.1-enhanced
# Last Updated: $(date +%d/%m/%Y)
# ==========================================================
BASE_DIR="/usr/local/share/proxmenux"
source "$BASE_DIR/utils.sh"
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
SHARE_COMMON_LOADED=false
else
SHARE_COMMON_LOADED=true
fi
load_language
initialize_cache
# ==========================================================
get_container_uid_shift() {
local ctid="$1"
local conf="/etc/pve/lxc/${ctid}.conf"
local uid_shift
if [[ ! -f "$conf" ]]; then
echo "100000"
return 0
fi
local unpriv
unpriv=$(grep "^unprivileged:" "$conf" | awk '{print $2}')
if [[ "$unpriv" == "1" ]]; then
uid_shift=$(grep "^lxc.idmap" "$conf" | grep 'u 0' | awk '{print $5}' | head -1)
echo "${uid_shift:-100000}"
return 0
fi
echo "0"
return 0
}
setup_container_access() {
local ctid="$1" group_name="$2" host_gid="$3" host_dir="$4"
local uid_shift mapped_gid
if [[ ! "$ctid" =~ ^[0-9]+$ ]]; then
msg_error "$(translate 'Invalid container ID format:') $ctid"
return 1
fi
uid_shift=$(get_container_uid_shift "$ctid")
# ===================================================================
# CONTAINER TYPE DETECTION AND STRATEGY
# ===================================================================
if [[ "$uid_shift" -eq 0 ]]; then
msg_ok "$(translate "PRIVILEGED container detected - using direct UID/GID mapping")"
mapped_gid="$host_gid"
container_type="privileged"
else
msg_ok "$(translate "UNPRIVILEGED container detected - using mapped UID/GID")"
mapped_gid=$((uid_shift + host_gid))
container_type="unprivileged"
msg_ok "UID shift: $uid_shift, Host GID: $host_gid → Container GID: $mapped_gid"
fi
# ===================================================================
# STEP 1: ACL TOOLS (only for unprivileged containers)
# ===================================================================
if [[ "$container_type" == "unprivileged" ]]; then
if ! command -v setfacl >/dev/null 2>&1; then
msg_info "$(translate "Installing ACL tools (REQUIRED for unprivileged containers)...")"
apt-get update >/dev/null 2>&1
apt-get install -y acl >/dev/null 2>&1
if command -v setfacl >/dev/null 2>&1; then
msg_ok "$(translate "ACL tools installed successfully")"
else
msg_error "$(translate "Failed to install ACL tools - permissions may not work correctly")"
fi
else
msg_ok "$(translate "ACL tools already available")"
fi
else
msg_ok "$(translate "Privileged container - ACL tools not required (using POSIX permissions)")"
fi
# ===================================================================
# STEP 2: CONTAINER GROUP CONFIGURATION
# ===================================================================
msg_info "$(translate "Configuring container group with") $container_type $(translate "strategy...")"
pct exec "$ctid" -- sh -c "
# Remove existing group if GID is wrong
if getent group $group_name >/dev/null 2>&1; then
current_gid=\$(getent group $group_name | cut -d: -f3)
if [ \"\$current_gid\" != \"$mapped_gid\" ]; then
groupdel $group_name 2>/dev/null || true
fi
fi
# Create group with correct GID
groupadd -g $mapped_gid $group_name 2>/dev/null || true
" 2>/dev/null
msg_ok "$(translate "Container group configured:") $group_name (GID: $mapped_gid)"
# ===================================================================
# STEP 3: USER PROCESSING (different strategies)
# ===================================================================
local container_users
container_users=$(pct exec "$ctid" -- getent passwd | awk -F: '{print $1 ":" $3}' 2>/dev/null)
local users_added=0
local acls_applied=0
if [[ "$container_type" == "privileged" ]]; then
msg_ok "$(translate "Privileged container:") $users_added $(translate "users added to group (no ACLs needed)")"
else
msg_info "$(translate "Using UNPRIVILEGED strategy: mapped UIDs + ACL permissions")"
while IFS=: read -r username ct_uid; do
if [[ -n "$username" && "$ct_uid" =~ ^[0-9]+$ ]]; then
local host_uid=$((uid_shift + ct_uid))
if pct exec "$ctid" -- usermod -aG "$group_name" "$username" 2>/dev/null; then
users_added=$((users_added + 1))
if command -v setfacl >/dev/null 2>&1; then
setfacl -m u:$host_uid:rwx "$host_dir" 2>/dev/null
setfacl -m d:u:$host_uid:rwx "$host_dir" 2>/dev/null
acls_applied=$((acls_applied + 1))
fi
case "$username" in
root|www-data|ncp|nobody|ubuntu|debian)
msg_ok "$(translate "Configured user:") $username (CT_UID:$ct_uid → HOST_UID:$host_uid)"
;;
esac
fi
fi
done <<< "$container_users"
msg_ok "$(translate "Unprivileged container:") $users_added $(translate "users added,") $acls_applied $(translate "ACL entries applied")"
fi
# ===================================================================
# STEP 4: DIRECTORY PERMISSIONS
# ===================================================================
msg_info "$(translate "Setting optimal directory permissions...")"
chmod 2775 "$host_dir" 2>/dev/null || true
chgrp "$group_name" "$host_dir" 2>/dev/null || true
msg_ok "$(translate "Host directory permissions:") 2775 root:$group_name"
# ===================================================================
# STEP 5: VERIFICATION
# ===================================================================
msg_info "$(translate "Verifying configuration...")"
if [[ "$container_type" == "unprivileged" ]] && command -v getfacl >/dev/null 2>&1; then
local acl_count=$(getfacl "$host_dir" 2>/dev/null | grep "^user:" | grep -v "^user::" | wc -l)
msg_ok "$(translate "ACL entries configured:") $acl_count"
# Show sample ACL entries
if [[ $acl_count -gt 0 ]]; then
echo -e "${TAB}${BGN}$(translate " ACL entries:")${CL}"
getfacl "$host_dir" 2>/dev/null | grep "^user:" | grep -v "^user::" | head -3 | while read acl_line; do
echo -e "${TAB} ${BL}$acl_line${CL}"
done
fi
fi
local test_users=("www-data" "root" "ncp" "nobody")
local successful_tests=0
for test_user in "${test_users[@]}"; do
if pct exec "$ctid" -- id "$test_user" >/dev/null 2>&1; then
if pct exec "$ctid" -- su -s /bin/bash "$test_user" -c "ls '$4' >/dev/null 2>&1" 2>/dev/null; then
successful_tests=$((successful_tests + 1))
fi
fi
done
if [[ $successful_tests -gt 0 ]]; then
msg_ok "$(translate "Access verification:") $successful_tests $(translate "users can access mount point")"
fi
if [[ "$container_type" == "privileged" ]]; then
msg_ok "$(translate "PRIVILEGED container configuration completed - using direct POSIX permissions")"
else
msg_ok "$(translate "UNPRIVILEGED container configuration completed - using ACL permissions")"
fi
return 0
}
get_next_mp_index() {
local ctid="$1"
local conf="/etc/pve/lxc/${ctid}.conf"
if [[ ! "$ctid" =~ ^[0-9]+$ ]] || [[ ! -f "$conf" ]]; then
echo "0"
return 0
fi
local used idx next=0
used=$(awk -F: '/^mp[0-9]+:/ {print $1}' "$conf" | sed 's/mp//' | sort -n)
for idx in $used; do
[[ "$idx" -ge "$next" ]] && next=$((idx+1))
done
echo "$next"
}
add_bind_mount() {
local ctid="$1" host_path="$2" ct_path="$3"
local mpidx result
if [[ ! "$ctid" =~ ^[0-9]+$ ]]; then
msg_error "$(translate 'Invalid container ID format:') $ctid"
return 1
fi
if [[ -z "$ctid" || -z "$host_path" || -z "$ct_path" ]]; then
msg_error "$(translate "Missing arguments")"
return 1
fi
if pct config "$ctid" | grep -q "$host_path"; then
echo -e
msg_warn "$(translate "Directory already mounted in container configuration.")"
echo -e ""
msg_success "$(translate 'Press Enter to return to menu...')"
read -r
return 1
fi
mpidx=$(get_next_mp_index "$ctid")
result=$(pct set "$ctid" -mp${mpidx} "$host_path,mp=$ct_path,shared=1,backup=0,acl=1" 2>&1)
if [[ $? -eq 0 ]]; then
msg_ok "$(translate "Successfully mounted:") $host_path$ct_path"
return 0
else
msg_error "$(translate "Error mounting folder:") $result"
return 1
fi
}
mount_host_directory_to_lxc() {
# Step 1: Select container
local container_id
container_id=$(select_lxc_container)
if [[ $? -ne 0 || -z "$container_id" ]]; then
return 1
fi
show_proxmenux_logo
msg_title "$(translate 'Mount Host Directory to LXC Container')"
# Step 1.1: Ensure running
ct_status=$(pct status "$container_id" | awk '{print $2}')
if [[ "$ct_status" != "running" ]]; then
msg_info "$(translate "Starting container") $container_id..."
if pct start "$container_id"; then
sleep 3
msg_ok "$(translate "Container started")"
else
msg_error "$(translate "Failed to start container")"
echo -e ""
msg_success "$(translate 'Press Enter to continue...')"
read -r
return 1
fi
fi
msg_ok "$(translate 'Container selected and running')"
sleep 2
# Step 2: Select host directory
local host_dir
host_dir=$(select_host_directory)
if [[ -z "$host_dir" ]]; then
return 1
fi
msg_ok "$(translate 'Host directory selected')"
# Step 3: Setup group
local group_name="sharedfiles"
local group_gid
group_gid=$(pmx_ensure_host_group "$group_name")
if [[ -z "$group_gid" ]]; then
return 1
fi
# Set basic permissions
chown -R root:"$group_name" "$host_dir" 2>/dev/null || true
chmod -R 2775 "$host_dir" 2>/dev/null || true
msg_ok "$(translate 'Host group configured')"
# Step 4: Select container mount point
local ct_mount_point
ct_mount_point=$(select_container_mount_point "$container_id" "$host_dir")
if [[ -z "$ct_mount_point" ]]; then
return 1
fi
# Step 5: Confirmation
local uid_shift container_type
uid_shift=$(get_container_uid_shift "$container_id")
if [[ "$uid_shift" -eq 0 ]]; then
container_type="$(translate 'Privileged')"
else
container_type="$(translate 'Unprivileged')"
fi
local confirm_msg="$(translate "Mount Configuration:")
$(translate "Container ID:"): $container_id ($container_type)
$(translate "Host Directory:"): $host_dir
$(translate "Container Mount Point:"): $ct_mount_point
$(translate "Shared Group:"): $group_name (GID: $group_gid)
$(translate "Proceed?")"
if ! whiptail --title "$(translate "Confirm Mount")" --yesno "$confirm_msg" 16 70; then
return 1
fi
# Step 6: Add mount
if ! add_bind_mount "$container_id" "$host_dir" "$ct_mount_point"; then
return 1
fi
# Step 7: Setup access (handles both privileged and unprivileged)
setup_container_access "$container_id" "$group_name" "$group_gid" "$host_dir"
# Step 8: Final setup
pct exec "$container_id" -- chgrp "$group_name" "$ct_mount_point" 2>/dev/null || true
pct exec "$container_id" -- chmod 2775 "$ct_mount_point" 2>/dev/null || true
# Step 9: Summary
echo -e ""
echo -e "${TAB}${BOLD}$(translate 'Mount Added Successfully:')${CL}"
echo -e "${TAB}${BGN}$(translate 'Container:')${CL} ${BL}$container_id ($container_type)${CL}"
echo -e "${TAB}${BGN}$(translate 'Host Directory:')${CL} ${BL}$host_dir${CL}"
echo -e "${TAB}${BGN}$(translate 'Mount Point:')${CL} ${BL}$ct_mount_point${CL}"
echo -e "${TAB}${BGN}$(translate 'Group:')${CL} ${BL}$group_name (GID: $group_gid)${CL}"
if [[ "$uid_shift" -eq 0 ]]; then
echo -e "${TAB}${BGN}$(translate 'Permission Strategy:')${CL} ${BL}POSIX (direct mapping)${CL}"
else
echo -e "${TAB}${BGN}$(translate 'Permission Strategy:')${CL} ${BL}ACL (mapped UIDs)${CL}"
fi
echo -e ""
if whiptail --yesno "$(translate "Restart container to activate mount?")" 8 60; then
msg_info "$(translate 'Restarting container...')"
if pct reboot "$container_id"; then
sleep 5
msg_ok "$(translate 'Container restarted successfully')"
echo -e
echo -e "${TAB}${BOLD}$(translate 'Testing access and read/write:')${CL}"
test_user=$(pct exec "$container_id" -- sh -c "id -u ncp >/dev/null 2>&1 && echo ncp || echo www-data")
if pct exec "$container_id" -- su -s /bin/bash $test_user -c "touch $ct_mount_point/test_access.txt" 2>/dev/null; then
msg_ok "$(translate "Mount access and read/write successful (tested as $test_user)")"
rm -f "$host_dir/test_access.txt" 2>/dev/null || true
else
msg_warn "$(translate "⚠ Access test failed - check permissions (user: $test_user)")"
fi
else
msg_warn "$(translate 'Failed to restart - restart manually')"
fi
fi
echo -e ""
msg_success "$(translate 'Press Enter to continue...')"
read -r
}
# Main menu
main_menu() {
while true; do
choice=$(dialog --title "$(translate 'LXC Mount Manager')" \
--menu "\n$(translate 'Choose an option:')" 25 80 15 \
"1" "$(translate 'Mount Host Directory to LXC')" \
"2" "$(translate 'View Mount Points')" \
"3" "$(translate 'Remove Mount Point')" \
"4" "$(translate 'Exit')" 3>&1 1>&2 2>&3)
case $choice in
1)
mount_host_directory_to_lxc
;;
2)
msg_info2 "$(translate 'Feature coming soon...')"
read -p "$(translate 'Press Enter to continue...')"
;;
3)
msg_info2 "$(translate 'Feature coming soon...')"
read -p "$(translate 'Press Enter to continue...')"
;;
4|"")
exit 0
;;
esac
done
}
#main_menu
mount_host_directory_to_lxc

File diff suppressed because it is too large Load Diff

687
scripts/share/nfs_client.sh Normal file
View File

@@ -0,0 +1,687 @@
#!/bin/bash
# ==========================================================
# ProxMenu CT - NFS Client Manager for Proxmox LXC
# ==========================================================
# Based on ProxMenux by MacRimi
# ==========================================================
# Description:
# This script allows you to manage NFS client mounts inside Proxmox CTs:
# - Mount NFS shares (temporary and permanent)
# - View current mounts
# - Unmount and remove NFS shares
# - Auto-discover NFS servers
# ==========================================================
# Configuration
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
# Load shared functions
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
msg_error "$(translate "Could not load shared functions. Script cannot continue.")"
exit 1
fi
load_language
initialize_cache
select_privileged_lxc
install_nfs_client() {
if pct exec "$CTID" -- dpkg -s nfs-common &>/dev/null; then
return 0
fi
show_proxmenux_logo
msg_title "$(translate "Installing NFS Client in LXC")"
msg_info "$(translate "Installing NFS client packages...")"
if ! pct exec "$CTID" -- apt-get update >/dev/null 2>&1; then
msg_error "$(translate "Failed to update package list.")"
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
if ! pct exec "$CTID" -- apt-get install -y nfs-common >/dev/null 2>&1; then
msg_error "$(translate "Failed to install NFS client packages.")"
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
if ! pct exec "$CTID" -- which showmount >/dev/null 2>&1; then
msg_error "$(translate "showmount command not found after installation.")"
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
if ! pct exec "$CTID" -- which mount.nfs >/dev/null 2>&1; then
msg_error "$(translate "mount.nfs command not found after installation.")"
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
msg_ok "$(translate "NFS client installed successfully.")"
return 0
}
discover_nfs_servers() {
show_proxmenux_logo
msg_title "$(translate "Mount NFS Client in LXC")"
msg_info "$(translate "Scanning network for NFS servers...")"
HOST_IP=$(hostname -I | awk '{print $1}')
NETWORK=$(echo "$HOST_IP" | cut -d. -f1-3).0/24
if ! which nmap >/dev/null 2>&1; then
apt-get install -y nmap &>/dev/null
fi
SERVERS=$(nmap -p 2049 --open "$NETWORK" 2>/dev/null | grep -B 4 "2049/tcp open" | grep "Nmap scan report" | awk '{print $5}' | sort -u || true)
if [[ -z "$SERVERS" ]]; then
cleanup
whiptail --title "$(translate "No Servers Found")" --msgbox "$(translate "No NFS servers found on the network.")\n\n$(translate "You can add servers manually.")" 10 60
return 1
fi
OPTIONS=()
while IFS= read -r server; do
if [[ -n "$server" ]]; then
EXPORTS_COUNT=$(showmount -e "$server" 2>/dev/null | tail -n +2 | wc -l || echo "0")
SERVER_INFO="NFS Server ($EXPORTS_COUNT exports)"
OPTIONS+=("$server" "$SERVER_INFO")
fi
done <<< "$SERVERS"
if [[ ${#OPTIONS[@]} -eq 0 ]]; then
cleanup
whiptail --title "$(translate "No Valid Servers")" --msgbox "$(translate "No accessible NFS servers found.")" 8 50
return 1
fi
msg_ok "$(translate "NFS servers detected")"
NFS_SERVER=$(whiptail --title "$(translate "Select NFS Server")" --menu "$(translate "Choose an NFS server:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
[[ -n "$NFS_SERVER" ]] && return 0 || return 1
}
select_nfs_server() {
METHOD=$(whiptail --backtitle "ProxMenux" --title "$(translate "NFS Server Selection")" --menu "$(translate "How do you want to select the NFS server?")" 15 70 3 \
"auto" "$(translate "Auto-discover servers on network")" \
"manual" "$(translate "Enter server IP/hostname manually")" 3>&1 1>&2 2>&3)
case "$METHOD" in
auto)
discover_nfs_servers || return 1
;;
manual)
NFS_SERVER=$(whiptail --inputbox "$(translate "Enter NFS server IP or hostname:")" 10 60 --title "$(translate "NFS Server")" 3>&1 1>&2 2>&3)
[[ -z "$NFS_SERVER" ]] && return 1
;;
*)
return 1
;;
esac
return 0
}
select_nfs_export() {
if ! pct exec "$CTID" -- which showmount >/dev/null 2>&1; then
whiptail --title "$(translate "NFS Client Error")" \
--msgbox "$(translate "showmount command is not working properly.")\n\n$(translate "Please check the installation.")" \
10 60
return 1
fi
if ! pct exec "$CTID" -- ping -c 1 -W 3 "$NFS_SERVER" >/dev/null 2>&1; then
whiptail --title "$(translate "Connection Error")" \
--msgbox "$(translate "Cannot reach server") $NFS_SERVER\n\n$(translate "Please check:")\n• $(translate "Server IP/hostname is correct")\n• $(translate "Network connectivity")\n• $(translate "Server is online")" \
12 70
return 1
fi
if ! pct exec "$CTID" -- nc -z -w 3 "$NFS_SERVER" 2049 2>/dev/null; then
whiptail --title "$(translate "NFS Port Error")" \
--msgbox "$(translate "NFS port (2049) is not accessible on") $NFS_SERVER\n\n$(translate "Please check:")\n• $(translate "NFS server is running")\n• $(translate "Firewall settings")\n• $(translate "NFS service is enabled")" \
12 70
return 1
fi
EXPORTS_OUTPUT=$(pct exec "$CTID" -- showmount -e "$NFS_SERVER" 2>&1)
EXPORTS_RESULT=$?
if [[ $EXPORTS_RESULT -ne 0 ]]; then
ERROR_MSG=$(echo "$EXPORTS_OUTPUT" | grep -i "error\|failed\|denied" | head -1)
if echo "$EXPORTS_OUTPUT" | grep -qi "connection refused\|network unreachable"; then
whiptail --title "$(translate "Network Error")" \
--msgbox "$(translate "Network connection failed to") $NFS_SERVER\n\n$(translate "Error:"): $ERROR_MSG\n\n$(translate "Please check:")\n• $(translate "Server is running")\n• $(translate "Network connectivity")\n• $(translate "Firewall settings")" \
14 80
else
whiptail --title "$(translate "NFS Error")" \
--msgbox "$(translate "Failed to connect to") $NFS_SERVER\n\n$(translate "Error:"): $ERROR_MSG" \
12 80
fi
return 1
fi
EXPORTS=$(echo "$EXPORTS_OUTPUT" | tail -n +2 | awk '{print $1}' | grep -v "^$")
if [[ -z "$EXPORTS" ]]; then
whiptail --title "$(translate "No Exports Found")" \
--msgbox "$(translate "No exports found on server") $NFS_SERVER\n\n$(translate "Server response:")\n$(echo "$EXPORTS_OUTPUT" | head -10)\n\n$(translate "You can enter the export path manually.")" \
16 80
NFS_EXPORT=$(whiptail --inputbox "$(translate "Enter NFS export path (e.g., /mnt/shared):")" 10 60 --title "$(translate "Export Path")" 3>&1 1>&2 2>&3)
[[ -z "$NFS_EXPORT" ]] && return 1
return 0
fi
# Build options for whiptail
OPTIONS=()
while IFS= read -r export_line; do
if [[ -n "$export_line" ]]; then
EXPORT_PATH=$(echo "$export_line" | awk '{print $1}')
# Get allowed clients if available
CLIENTS=$(echo "$EXPORTS_OUTPUT" | grep "^$EXPORT_PATH" | awk '{for(i=2;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/[[:space:]]*$//')
if [[ -n "$CLIENTS" ]]; then
OPTIONS+=("$EXPORT_PATH" "$CLIENTS")
else
OPTIONS+=("$EXPORT_PATH" "$(translate "NFS export")")
fi
fi
done <<< "$EXPORTS"
if [[ ${#OPTIONS[@]} -eq 0 ]]; then
whiptail --title "$(translate "No Available Exports")" \
--msgbox "$(translate "No accessible exports found.")\n\n$(translate "You can enter the export path manually.")" \
10 70
NFS_EXPORT=$(whiptail --inputbox "$(translate "Enter NFS export path (e.g., /mnt/shared):")" 10 60 --title "$(translate "Export Path")" 3>&1 1>&2 2>&3)
[[ -n "$NFS_EXPORT" ]] && return 0 || return 1
fi
NFS_EXPORT=$(whiptail --title "$(translate "Select NFS Export")" --menu "$(translate "Choose an export to mount:")" 20 70 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
[[ -n "$NFS_EXPORT" ]] && return 0 || return 1
}
select_mount_point() {
while true; do
METHOD=$(whiptail --title "$(translate "Select Mount Point")" --menu "$(translate "Where do you want to mount the NFS export?")" 15 70 3 \
"1" "$(translate "Create new folder in /mnt")" \
"2" "$(translate "Select from existing folders in /mnt")" \
"3" "$(translate "Enter custom path")" 3>&1 1>&2 2>&3)
case "$METHOD" in
1)
# Create default name from server and export
EXPORT_NAME=$(basename "$NFS_EXPORT")
DEFAULT_NAME="nfs_${NFS_SERVER}_${EXPORT_NAME}"
FOLDER_NAME=$(whiptail --inputbox "$(translate "Enter new folder name:")" 10 60 "$DEFAULT_NAME" --title "$(translate "New Folder in /mnt")" 3>&1 1>&2 2>&3)
if [[ -n "$FOLDER_NAME" ]]; then
MOUNT_POINT="/mnt/$FOLDER_NAME"
return 0
fi
;;
2)
DIRS=$(pct exec "$CTID" -- find /mnt -maxdepth 1 -mindepth 1 -type d 2>/dev/null)
if [[ -z "$DIRS" ]]; then
whiptail --title "$(translate "No Folders")" --msgbox "$(translate "No folders found in /mnt. Please create a new folder.")" 8 60
continue
fi
OPTIONS=()
while IFS= read -r dir; do
if [[ -n "$dir" ]]; then
name=$(basename "$dir")
if pct exec "$CTID" -- [ "$(ls -A "$dir" 2>/dev/null | wc -l)" -eq 0 ]; then
status="$(translate "Empty")"
else
status="$(translate "Contains files")"
fi
OPTIONS+=("$dir" "$name ($status)")
fi
done <<< "$DIRS"
MOUNT_POINT=$(whiptail --title "$(translate "Select Existing Folder")" --menu "$(translate "Choose a folder to mount the export:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
if [[ -n "$MOUNT_POINT" ]]; then
if pct exec "$CTID" -- [ "$(ls -A "$MOUNT_POINT" 2>/dev/null | wc -l)" -gt 0 ]; then
FILE_COUNT=$(pct exec "$CTID" -- ls -A "$MOUNT_POINT" 2>/dev/null | wc -l)
if ! whiptail --yesno "$(translate "WARNING: The selected directory is not empty!")\n\n$(translate "Directory:"): $MOUNT_POINT\n$(translate "Contains:"): $FILE_COUNT $(translate "files/folders")\n\n$(translate "Mounting here will hide existing files until unmounted.")\n\n$(translate "Do you want to continue?")" 14 70 --title "$(translate "Directory Not Empty")"; then
continue
fi
fi
return 0
fi
;;
3)
MOUNT_POINT=$(whiptail --inputbox "$(translate "Enter full path for mount point:")" 10 70 "/mnt/nfs_share" --title "$(translate "Custom Path")" 3>&1 1>&2 2>&3)
if [[ -n "$MOUNT_POINT" ]]; then
return 0
fi
;;
*)
return 1
;;
esac
done
}
configure_mount_options() {
MOUNT_TYPE=$(whiptail --title "$(translate "Mount Options")" --menu "$(translate "Select mount configuration:")" 15 70 4 \
"1" "$(translate "Default options read/write")" \
"2" "$(translate "Read-only mount")" \
"3" "$(translate "Custom options")" 3>&1 1>&2 2>&3)
case "$MOUNT_TYPE" in
1)
MOUNT_OPTIONS="rw,hard,rsize=1048576,wsize=1048576,timeo=600,retrans=2"
;;
2)
MOUNT_OPTIONS="ro,hard,rsize=1048576,wsize=1048576,timeo=600,retrans=2"
;;
3)
MOUNT_OPTIONS=$(whiptail --inputbox "$(translate "Enter custom mount options:")" \
10 70 "rw,hard,rsize=1048576,wsize=1048576,timeo=600,retrans=2" \
--title "$(translate "Custom Options")" 3>&1 1>&2 2>&3)
[[ $? -ne 0 ]] && return 1
[[ -z "$MOUNT_OPTIONS" ]] && MOUNT_OPTIONS="rw,hard"
;;
*)
MOUNT_OPTIONS="rw,hard,rsize=65536,wsize=65536,timeo=600,retrans=2"
;;
esac
if whiptail --yesno "$(translate "Do you want to make this mount permanent?")\n\n$(translate "This will add the mount to /etc/fstab so it persists after reboot.")" 10 70 --title "$(translate "Permanent Mount")"; then
PERMANENT_MOUNT=true
else
PERMANENT_MOUNT=false
fi
}
validate_export_exists() {
local server="$1"
local export="$2"
VALIDATION_OUTPUT=$(pct exec "$CTID" -- showmount -e "$server" 2>/dev/null | grep "^$export[[:space:]]")
if [[ -n "$VALIDATION_OUTPUT" ]]; then
return 0
else
show_proxmenux_logo
echo -e
msg_error "$(translate "Export not found on server:") $export"
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return 1
fi
}
mount_nfs_share() {
# Step 0: Install NFS client first
install_nfs_client || return
# Step 1: Select server
select_nfs_server || return
show_proxmenux_logo
msg_title "$(translate "Mount NFS Share on Host")"
msg_ok "$(translate "NFS server Selected")"
# Step 2: Select export
select_nfs_export || return
msg_ok "$(translate "NFS export Selected")"
# Step 2.5: Validate export exists
if ! validate_export_exists "$NFS_SERVER" "$NFS_EXPORT"; then
echo -e ""
msg_error "$(translate "Cannot proceed with invalid export path.")"
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return
fi
# Step 3: Select mount point
select_mount_point || return
# Step 4: Configure mount options
configure_mount_options || return
if ! pct exec "$CTID" -- test -d "$MOUNT_POINT"; then
if pct exec "$CTID" -- mkdir -p "$MOUNT_POINT"; then
msg_ok "$(translate "Mount point created.")"
else
msg_error "$(translate "Failed to create mount point.")"
return 1
fi
fi
if pct exec "$CTID" -- mount | grep -q "$MOUNT_POINT"; then
msg_warn "$(translate "Something is already mounted at") $MOUNT_POINT"
if ! whiptail --yesno "$(translate "Do you want to unmount it first?")" 8 60 --title "$(translate "Already Mounted")"; then
return
fi
pct exec "$CTID" -- umount "$MOUNT_POINT" 2>/dev/null || true
fi
# Build mount command
NFS_PATH="$NFS_SERVER:$NFS_EXPORT"
msg_info "$(translate "Testing NFS connection...")"
if pct exec "$CTID" -- mount -t nfs -o "$MOUNT_OPTIONS" "$NFS_PATH" "$MOUNT_POINT"; then
msg_ok "$(translate "NFS share mounted successfully!")"
# Test write access
if pct exec "$CTID" -- touch "$MOUNT_POINT/.test_write" 2>/dev/null; then
pct exec "$CTID" -- rm "$MOUNT_POINT/.test_write" 2>/dev/null
msg_ok "$(translate "Write access confirmed.")"
else
msg_warn "$(translate "Read-only access (or no write permissions).")"
fi
# Add to fstab if permanent
if [[ "$PERMANENT_MOUNT" == "true" ]]; then
pct exec "$CTID" -- sed -i "\|$MOUNT_POINT|d" /etc/fstab
FSTAB_ENTRY="$NFS_PATH $MOUNT_POINT nfs $MOUNT_OPTIONS 0 0"
pct exec "$CTID" -- bash -c "echo '$FSTAB_ENTRY' >> /etc/fstab"
msg_ok "$(translate "Added to /etc/fstab for permanent mounting.")"
fi
# Show mount information
echo -e ""
echo -e "${TAB}${BOLD}$(translate "Mount Information:")${CL}"
echo -e "${TAB}${BGN}$(translate "Server:")${CL} ${BL}$NFS_SERVER${CL}"
echo -e "${TAB}${BGN}$(translate "Export:")${CL} ${BL}$NFS_EXPORT${CL}"
echo -e "${TAB}${BGN}$(translate "Mount Point:")${CL} ${BL}$MOUNT_POINT${CL}"
echo -e "${TAB}${BGN}$(translate "Options:")${CL} ${BL}$MOUNT_OPTIONS${CL}"
echo -e "${TAB}${BGN}$(translate "Permanent:")${CL} ${BL}$PERMANENT_MOUNT${CL}"
else
msg_error "$(translate "Failed to mount NFS share.")"
echo -e "${TAB}$(translate "Please check:")"
echo -e "${TAB}$(translate "Server is accessible:"): $NFS_SERVER"
echo -e "${TAB}$(translate "Export exists:"): $NFS_EXPORT"
echo -e "${TAB}$(translate "Network connectivity")"
echo -e "${TAB}$(translate "NFS server is running")"
echo -e "${TAB}$(translate "Export permissions allow access")"
fi
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
view_nfs_mounts() {
show_proxmenux_logo
msg_title "$(translate "Current NFS Mounts")"
echo -e "$(translate "NFS mounts in CT") $CTID:"
echo "=================================="
# Show currently mounted NFS shares - VERSIÓN CORREGIDA
CURRENT_MOUNTS=$(pct exec "$CTID" -- mount | grep -E "type nfs|:.*on.*nfs" 2>/dev/null || true)
if [[ -n "$CURRENT_MOUNTS" ]]; then
echo -e "${BOLD}$(translate "Currently Mounted:")${CL}"
echo "$CURRENT_MOUNTS"
echo ""
else
# Verificar si hay montajes NFS en fstab que estén activos
ACTIVE_NFS_MOUNTS=$(pct exec "$CTID" -- grep "nfs" /etc/fstab 2>/dev/null | grep -v "^#" | while read -r line; do
MOUNT_POINT=$(echo "$line" | awk '{print $2}')
if pct exec "$CTID" -- mount | grep -q "$MOUNT_POINT"; then
echo "$MOUNT_POINT"
fi
done)
if [[ -n "$ACTIVE_NFS_MOUNTS" ]]; then
echo -e "${BOLD}$(translate "Currently Mounted:")${CL}"
while IFS= read -r mount_point; do
if [[ -n "$mount_point" ]]; then
MOUNT_INFO=$(pct exec "$CTID" -- mount | grep "$mount_point")
echo "$MOUNT_INFO"
fi
done <<< "$ACTIVE_NFS_MOUNTS"
echo ""
else
echo "$(translate "No NFS shares currently mounted.")"
echo ""
fi
fi
# Show fstab entries
FSTAB_NFS=$(pct exec "$CTID" -- grep "nfs" /etc/fstab 2>/dev/null || true)
if [[ -n "$FSTAB_NFS" ]]; then
echo -e "${BOLD}$(translate "Permanent Mounts (fstab):")${CL}"
echo "$FSTAB_NFS"
echo ""
echo -e "${TAB}${BOLD}$(translate "Mount Details:")${CL}"
while IFS= read -r fstab_line; do
if [[ -n "$fstab_line" && ! "$fstab_line" =~ ^# ]]; then
NFS_PATH=$(echo "$fstab_line" | awk '{print $1}')
MOUNT_POINT=$(echo "$fstab_line" | awk '{print $2}')
OPTIONS=$(echo "$fstab_line" | awk '{print $4}')
# Extract server and export from NFS path
SERVER=$(echo "$NFS_PATH" | cut -d: -f1)
EXPORT=$(echo "$NFS_PATH" | cut -d: -f2)
echo -e "${TAB}${BGN}$(translate "Server:")${CL} ${BL}$SERVER${CL}"
echo -e "${TAB}${BGN}$(translate "Export:")${CL} ${BL}$EXPORT${CL}"
echo -e "${TAB}${BGN}$(translate "Mount Point:")${CL} ${BL}$MOUNT_POINT${CL}"
echo -e "${TAB}${BGN}$(translate "Options:")${CL} ${BL}$OPTIONS${CL}"
# Check if currently mounted
if pct exec "$CTID" -- mount | grep -q "$MOUNT_POINT"; then
echo -e "${TAB}${BGN}$(translate "Status:")${CL} ${GN}$(translate "Mounted")${CL}"
else
echo -e "${TAB}${BGN}$(translate "Status:")${CL} ${RD}$(translate "Not Mounted")${CL}"
fi
echo ""
fi
done <<< "$FSTAB_NFS"
else
echo "$(translate "No permanent NFS mounts configured.")"
fi
echo ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
unmount_nfs_share() {
# Get current NFS mounts
MOUNTS=$(pct exec "$CTID" -- mount | grep -E "type nfs|:.*on.*nfs" | awk '{print $3}' | sort -u || true)
FSTAB_MOUNTS=$(pct exec "$CTID" -- grep -E "nfs" /etc/fstab 2>/dev/null | grep -v "^#" | awk '{print $2}' | sort -u || true)
# Combine and deduplicate
ALL_MOUNTS=$(echo -e "$MOUNTS\n$FSTAB_MOUNTS" | sort -u | grep -v "^$" || true)
if [[ -z "$ALL_MOUNTS" ]]; then
dialog --backtitle "ProxMenux" --title "$(translate "No Mounts")" --msgbox "\n$(translate "No NFS mounts found.")" 8 50
return
fi
OPTIONS=()
while IFS= read -r mount_point; do
[[ -n "$mount_point" ]] && OPTIONS+=("$mount_point" "")
done <<< "$ALL_MOUNTS"
SELECTED_MOUNT=$(dialog --backtitle "ProxMenux" --title "$(translate "Unmount NFS Share")" --menu "$(translate "Select mount point to unmount:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
[[ -z "$SELECTED_MOUNT" ]] && return
if whiptail --yesno "$(translate "Are you sure you want to unmount this NFS share?")\n\n$(translate "Mount Point:"): $SELECTED_MOUNT\n\n$(translate "This will remove the mount from /etc/fstab.")" 12 80 --title "$(translate "Confirm Unmount")"; then
show_proxmenux_logo
msg_title "$(translate "Unmount NFS Share")"
# Remove from fstab
pct exec "$CTID" -- sed -i "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
msg_ok "$(translate "Removed from /etc/fstab.")"
echo -e ""
msg_ok "$(translate "NFS share unmount successfully. Reboot LXC required to take effect.")"
fi
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
test_nfs_connectivity() {
show_proxmenux_logo
msg_title "$(translate "Test NFS Connectivity")"
echo -e "$(translate "NFS Client Status in CT") $CTID:"
echo "=================================="
# Check if NFS client is installed
if pct exec "$CTID" -- dpkg -s nfs-common &>/dev/null; then
echo "$(translate "NFS Client: INSTALLED")"
# Check showmount
if pct exec "$CTID" -- which showmount >/dev/null 2>&1; then
echo "$(translate "NFS Client Tools: AVAILABLE")"
else
echo "$(translate "NFS Client Tools: NOT AVAILABLE")"
fi
# Check rpcbind service
if pct exec "$CTID" -- systemctl is-active --quiet rpcbind 2>/dev/null; then
echo "$(translate "RPC Bind Service: RUNNING")"
else
echo "$(translate "RPC Bind Service: STOPPED")"
msg_warn "$(translate "Starting rpcbind service...")"
pct exec "$CTID" -- systemctl start rpcbind 2>/dev/null || true
fi
echo ""
echo "$(translate "Current NFS mounts:")"
CURRENT_MOUNTS=$(pct exec "$CTID" -- mount | grep -E "type nfs|:.*on.*nfs" 2>/dev/null || true)
if [[ -n "$CURRENT_MOUNTS" ]]; then
echo "$CURRENT_MOUNTS"
else
# Check for active NFS mounts from fstab
ACTIVE_NFS_MOUNTS=$(pct exec "$CTID" -- grep "nfs" /etc/fstab 2>/dev/null | grep -v "^#" | while read -r line; do
MOUNT_POINT=$(echo "$line" | awk '{print $2}')
if pct exec "$CTID" -- mount | grep -q "$MOUNT_POINT"; then
pct exec "$CTID" -- mount | grep "$MOUNT_POINT"
fi
done)
if [[ -n "$ACTIVE_NFS_MOUNTS" ]]; then
echo "$ACTIVE_NFS_MOUNTS"
else
echo "$(translate "No NFS mounts active.")"
fi
fi
echo ""
echo "$(translate "Testing network connectivity...")"
# Test connectivity to known NFS servers from fstab
FSTAB_SERVERS=$(pct exec "$CTID" -- grep "nfs" /etc/fstab 2>/dev/null | awk '{print $1}' | cut -d: -f1 | sort -u || true)
if [[ -n "$FSTAB_SERVERS" ]]; then
while IFS= read -r server; do
if [[ -n "$server" ]]; then
echo -n "$(translate "Testing") $server: "
if pct exec "$CTID" -- ping -c 1 -W 2 "$server" >/dev/null 2>&1; then
echo -e "\033[1;92m$(translate "Reachable")\033[0m"
# Test NFS port
echo -n " $(translate "NFS port 2049"): "
if pct exec "$CTID" -- nc -z -w 2 "$server" 2049 2>/dev/null; then
echo -e "\033[1;92m$(translate "Open")\033[0m"
else
echo -e "\033[1;91m$(translate "Closed")\033[0m"
fi
# Try to list exports
echo -n " $(translate "Export list test"): "
if pct exec "$CTID" -- showmount -e "$server" >/dev/null 2>&1; then
echo -e "\033[1;92m$(translate "Available")\033[0m"
else
echo -e "\033[1;91m$(translate "Failed")\033[0m"
fi
else
echo -e "\033[1;91m$(translate "Unreachable")\033[0m"
fi
fi
done <<< "$FSTAB_SERVERS"
else
echo "$(translate "No NFS servers configured to test.")"
fi
else
echo "$(translate "NFS Client: NOT INSTALLED")"
echo ""
echo "$(translate "Run 'Mount NFS Share' to install NFS client automatically.")"
fi
echo ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
# === Main Menu ===
while true; do
CHOICE=$(dialog --backtitle "ProxMenux" --title "$(translate "NFS Client Manager - CT") $CTID" \
--menu "$(translate "Choose an option:")" 20 70 12 \
"1" "$(translate "Mount NFS Share")" \
"2" "$(translate "View Current Mounts")" \
"3" "$(translate "Unmount NFS Share")" \
"4" "$(translate "Test NFS Connectivity")" \
"5" "$(translate "Exit")" \
3>&1 1>&2 2>&3)
RETVAL=$?
if [[ $RETVAL -ne 0 ]]; then
exit 0
fi
case $CHOICE in
1) mount_nfs_share ;;
2) view_nfs_mounts ;;
3) unmount_nfs_share ;;
4) test_nfs_connectivity ;;
5) exit 0 ;;
*) exit 0 ;;
esac
done

852
scripts/share/nfs_host.sh Normal file
View File

@@ -0,0 +1,852 @@
#!/bin/bash
# ==========================================================
# ProxMenu Host - NFS Host Manager for Proxmox Host
# ==========================================================
# Based on ProxMenux by MacRimi
# ==========================================================
# Description:
# This script allows you to manage NFS client mounts on Proxmox Host:
# - Mount external NFS shares on the host
# - Configure permanent mounts
# - Auto-discover NFS servers
# - Integrate with Proxmox storage system
# ==========================================================
# Configuration
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# Load common share functions
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
msg_warn "$(translate "Could not load shared functions. Using fallback methods.")"
SHARE_COMMON_LOADED=false
else
SHARE_COMMON_LOADED=true
fi
discover_nfs_servers() {
show_proxmenux_logo
msg_title "$(translate "Mount NFS Share on Host")"
msg_info "$(translate "Scanning network for NFS servers...")"
HOST_IP=$(hostname -I | awk '{print $1}')
NETWORK=$(echo "$HOST_IP" | cut -d. -f1-3).0/24
if ! which nmap >/dev/null 2>&1; then
apt-get install -y nmap &>/dev/null
fi
SERVERS=$(nmap -p 2049 --open "$NETWORK" 2>/dev/null | grep -B 4 "2049/tcp open" | grep "Nmap scan report" | awk '{print $5}' | sort -u || true)
if [[ -z "$SERVERS" ]]; then
cleanup
dialog --clear --title "$(translate "No Servers Found")" --msgbox "$(translate "No NFS servers found on the network.")\n\n$(translate "You can add servers manually.")" 10 60
return 1
fi
OPTIONS=()
while IFS= read -r server; do
if [[ -n "$server" ]]; then
EXPORTS_COUNT=$(showmount -e "$server" 2>/dev/null | tail -n +2 | wc -l || echo "0")
SERVER_INFO="NFS Server ($EXPORTS_COUNT exports)"
OPTIONS+=("$server" "$SERVER_INFO")
fi
done <<< "$SERVERS"
if [[ ${#OPTIONS[@]} -eq 0 ]]; then
cleanup
dialog --clear --title "$(translate "No Valid Servers")" --msgbox "$(translate "No accessible NFS servers found.")" 8 50
return 1
fi
cleanup
NFS_SERVER=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select NFS Server")" --menu "$(translate "Choose an NFS server:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
[[ -n "$NFS_SERVER" ]] && return 0 || return 1
}
select_nfs_server() {
METHOD=$(dialog --backtitle "ProxMenux" --title "$(translate "NFS Server Selection")" --menu "$(translate "How do you want to select the NFS server?")" 15 70 3 \
"auto" "$(translate "Auto-discover servers on network")" \
"manual" "$(translate "Enter server IP/hostname manually")" \
"recent" "$(translate "Select from recent servers")" 3>&1 1>&2 2>&3)
case "$METHOD" in
auto)
discover_nfs_servers || return 1
;;
manual)
clear
NFS_SERVER=$(whiptail --inputbox "$(translate "Enter NFS server IP or hostname:")" 10 60 --title "$(translate "NFS Server")" 3>&1 1>&2 2>&3)
[[ -z "$NFS_SERVER" ]] && return 1
;;
recent)
clear
RECENT=$(grep "nfs" /etc/fstab 2>/dev/null | awk '{print $1}' | cut -d: -f1 | sort -u || true)
if [[ -z "$RECENT" ]]; then
dialog --title "$(translate "No Recent Servers")" --msgbox "\n$(translate "No recent NFS servers found.")" 8 50
return 1
fi
OPTIONS=()
while IFS= read -r server; do
[[ -n "$server" ]] && OPTIONS+=("$server" "$(translate "Recent NFS server")")
done <<< "$RECENT"
NFS_SERVER=$(whiptail --title "$(translate "Recent NFS Servers")" --menu "$(translate "Choose a recent server:")" 20 70 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
[[ -n "$NFS_SERVER" ]] && return 0 || return 1
;;
*)
return 1
;;
esac
return 0
}
select_nfs_export() {
if ! which showmount >/dev/null 2>&1; then
whiptail --title "$(translate "NFS Client Error")" \
--msgbox "$(translate "showmount command is not working properly.")\n\n$(translate "Please check the installation.")" \
10 60
return 1
fi
if ! ping -c 1 -W 3 "$NFS_SERVER" >/dev/null 2>&1; then
whiptail --title "$(translate "Connection Error")" \
--msgbox "$(translate "Cannot reach server") $NFS_SERVER\n\n$(translate "Please check:")\n• $(translate "Server IP/hostname is correct")\n• $(translate "Network connectivity")\n• $(translate "Server is online")" \
12 70
return 1
fi
if ! nc -z -w 3 "$NFS_SERVER" 2049 2>/dev/null; then
whiptail --title "$(translate "NFS Port Error")" \
--msgbox "$(translate "NFS port (2049) is not accessible on") $NFS_SERVER\n\n$(translate "Please check:")\n• $(translate "NFS server is running")\n• $(translate "Firewall settings")\n• $(translate "NFS service is enabled")" \
12 70
return 1
fi
EXPORTS_OUTPUT=$(showmount -e "$NFS_SERVER" 2>&1)
EXPORTS_RESULT=$?
if [[ $EXPORTS_RESULT -ne 0 ]]; then
ERROR_MSG=$(echo "$EXPORTS_OUTPUT" | grep -i "error\|failed\|denied" | head -1)
if echo "$EXPORTS_OUTPUT" | grep -qi "connection refused\|network unreachable"; then
whiptail --title "$(translate "Network Error")" \
--msgbox "$(translate "Network connection failed to") $NFS_SERVER\n\n$(translate "Error:"): $ERROR_MSG\n\n$(translate "Please check:")\n• $(translate "Server is running")\n• $(translate "Network connectivity")\n• $(translate "Firewall settings")" \
14 80
else
whiptail --title "$(translate "NFS Error")" \
--msgbox "$(translate "Failed to connect to") $NFS_SERVER\n\n$(translate "Error:"): $ERROR_MSG" \
12 80
fi
return 1
fi
EXPORTS=$(echo "$EXPORTS_OUTPUT" | tail -n +2 | awk '{print $1}' | grep -v "^$")
if [[ -z "$EXPORTS" ]]; then
whiptail --title "$(translate "No Exports Found")" \
--msgbox "$(translate "No exports found on server") $NFS_SERVER\n\n$(translate "Server response:")\n$(echo "$EXPORTS_OUTPUT" | head -10)\n\n$(translate "You can enter the export path manually.")" \
16 80
NFS_EXPORT=$(whiptail --inputbox "$(translate "Enter NFS export path (e.g., /mnt/shared):")" 10 60 --title "$(translate "Export Path")" 3>&1 1>&2 2>&3)
[[ -z "$NFS_EXPORT" ]] && return 1
return 0
fi
OPTIONS=()
while IFS= read -r export_line; do
if [[ -n "$export_line" ]]; then
EXPORT_PATH=$(echo "$export_line" | awk '{print $1}')
CLIENTS=$(echo "$EXPORTS_OUTPUT" | grep "^$EXPORT_PATH" | awk '{for(i=2;i<=NF;i++) printf "%s ", $i; print ""}' | sed 's/[[:space:]]*$//')
if [[ -n "$CLIENTS" ]]; then
OPTIONS+=("$EXPORT_PATH" "$CLIENTS")
else
OPTIONS+=("$EXPORT_PATH" "$(translate "NFS export")")
fi
fi
done <<< "$EXPORTS"
if [[ ${#OPTIONS[@]} -eq 0 ]]; then
whiptail --title "$(translate "No Available Exports")" \
--msgbox "$(translate "No accessible exports found.")\n\n$(translate "You can enter the export path manually.")" \
10 70
NFS_EXPORT=$(whiptail --inputbox "$(translate "Enter NFS export path (e.g., /mnt/shared):")" 10 60 --title "$(translate "Export Path")" 3>&1 1>&2 2>&3)
[[ -n "$NFS_EXPORT" ]] && return 0 || return 1
fi
NFS_EXPORT=$(whiptail --title "$(translate "Select NFS Export")" --menu "$(translate "Choose an export to mount:")" 20 70 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
[[ -n "$NFS_EXPORT" ]] && return 0 || return 1
}
select_host_mount_point() {
local export_name=$(basename "$NFS_EXPORT")
local default_path="/mnt/shared_nfs_${export_name}"
MOUNT_POINT=$(pmx_select_host_mount_point "$(translate "NFS Mount Point")" "$default_path")
[[ -n "$MOUNT_POINT" ]] && return 0 || return 1
}
configure_host_mount_options() {
MOUNT_TYPE=$(whiptail --title "$(translate "Mount Options")" --menu "$(translate "Select mount configuration:")" 15 70 4 \
"1" "$(translate "Default options read/write")" \
"2" "$(translate "Read-only mount")" \
"3" "$(translate "Enter custom options")" 3>&1 1>&2 2>&3)
[[ $? -ne 0 ]] && return 1
case "$MOUNT_TYPE" in
1)
MOUNT_OPTIONS="rw,hard,nofail,rsize=131072,wsize=131072,timeo=600,retrans=2"
;;
2)
MOUNT_OPTIONS="ro,hard,nofail,rsize=131072,wsize=131072,timeo=600,retrans=2"
;;
3)
MOUNT_OPTIONS=$(whiptail --inputbox "$(translate "Enter custom mount options:")" \
10 70 "rw,hard,nofail,rsize=131072,wsize=131072,timeo=600,retrans=2" \
--title "$(translate "Custom Options")" 3>&1 1>&2 2>&3)
[[ $? -ne 0 ]] && return 1
[[ -z "$MOUNT_OPTIONS" ]] && MOUNT_OPTIONS="rw,hard,nofail"
;;
*)
MOUNT_OPTIONS="rw,hard,nofail,rsize=131072,wsize=131072,timeo=600,retrans=2"
;;
esac
if whiptail --yesno "$(translate "Do you want to make this mount permanent?")\n\n$(translate "This will add the mount to /etc/fstab so it persists after reboot.")" 10 70 --title "$(translate "Permanent Mount")"; then
PERMANENT_MOUNT=true
else
if [[ $? -eq 1 ]]; then
PERMANENT_MOUNT=false
else
return 1
fi
fi
TEMP_MOUNT="/tmp/nfs_test_$$"
mkdir -p "$TEMP_MOUNT" 2>/dev/null
NFS_PATH="$NFS_SERVER:$NFS_EXPORT"
if timeout 10 mount -t nfs -o ro,soft,timeo=5 "$NFS_PATH" "$TEMP_MOUNT" 2>/dev/null; then
umount "$TEMP_MOUNT" 2>/dev/null || true
rmdir "$TEMP_MOUNT" 2>/dev/null || true
msg_ok "$(translate "NFS export is accessible")"
if whiptail --yesno "$(translate "Do you want to add this as Proxmox storage?")\n\n$(translate "This will make the NFS share available as storage in Proxmox web interface.")" 10 70 --title "$(translate "Proxmox Storage")"; then
PROXMOX_STORAGE=true
STORAGE_ID=$(whiptail --inputbox "$(translate "Enter storage ID for Proxmox:")" 10 60 "nfs-$(echo $NFS_SERVER | tr '.' '-')" --title "$(translate "Storage ID")" 3>&1 1>&2 2>&3)
STORAGE_ID_RESULT=$?
if [[ $STORAGE_ID_RESULT -ne 0 ]]; then
if whiptail --yesno "$(translate "Storage ID input was cancelled.")\n\n$(translate "Do you want to continue without Proxmox storage integration?")" 10 70 --title "$(translate "Continue Without Storage")"; then
PROXMOX_STORAGE=false
else
return 1
fi
else
[[ -z "$STORAGE_ID" ]] && STORAGE_ID="nfs-$(echo $NFS_SERVER | tr '.' '-')"
fi
else
DIALOG_RESULT=$?
if [[ $DIALOG_RESULT -eq 1 ]]; then
PROXMOX_STORAGE=false
else
return 1
fi
fi
else
rmdir "$TEMP_MOUNT" 2>/dev/null || true
msg_warn "$(translate "NFS export accessibility test failed")"
if whiptail --yesno "$(translate "The NFS export could not be validated for accessibility.")\n\n$(translate "This might be due to:")\n• $(translate "Network connectivity issues")\n• $(translate "Export permission restrictions")\n• $(translate "Firewall blocking access")\n\n$(translate "Do you want to continue mounting anyway?")\n$(translate "(Proxmox storage integration will be skipped)")" 16 80 --title "$(translate "Export Validation Failed")"; then
PROXMOX_STORAGE=false
msg_info2 "$(translate "Continuing without Proxmox storage integration due to accessibility issues.")"
sleep 2
else
return 1
fi
fi
return 0
}
validate_host_export_exists() {
local server="$1"
local export="$2"
VALIDATION_OUTPUT=$(showmount -e "$server" 2>/dev/null | grep "^$export[[:space:]]")
if [[ -n "$VALIDATION_OUTPUT" ]]; then
return 0
else
show_proxmenux_logo
echo -e
msg_error "$(translate "Export not found on server:") $export"
return 1
fi
}
add_proxmox_nfs_storage() {
local storage_id="$1"
local server="$2"
local export="$3"
local content="${4:-backup,iso,vztmpl}"
msg_info "$(translate "Starting Proxmox storage integration...")"
if ! command -v pvesm >/dev/null 2>&1; then
show_proxmenux_logo
msg_error "$(translate "pvesm command not found. This should not happen on Proxmox.")"
echo "Press Enter to continue..."
read -r
return 1
fi
msg_ok "$(translate "pvesm command found")"
# Check if storage ID already exists
if pvesm status "$storage_id" >/dev/null 2>&1; then
msg_warn "$(translate "Storage ID already exists:") $storage_id"
if ! whiptail --yesno "$(translate "Storage ID already exists. Do you want to remove and recreate it?")" 8 60 --title "$(translate "Storage Exists")"; then
return 0
fi
pvesm remove "$storage_id" 2>/dev/null || true
fi
msg_ok "$(translate "Storage ID is available")"
# Let Proxmox handle NFS version negotiation automatically
if pvesm_output=$(pvesm add nfs "$storage_id" \
--server "$server" \
--export "$export" \
--content "$content" 2>&1); then
msg_ok "$(translate "NFS storage added successfully!")"
# Get the actual NFS version that Proxmox negotiated
local nfs_version="Auto-negotiated"
if pvesm config "$storage_id" 2>/dev/null | grep -q "options.*vers="; then
nfs_version="v$(pvesm config "$storage_id" | grep "options" | grep -o "vers=[0-9.]*" | cut -d= -f2)"
fi
echo -e ""
echo -e "${TAB}${BGN}$(translate "Storage ID:")${CL} ${BL}$storage_id${CL}"
echo -e "${TAB}${BGN}$(translate "Server:")${CL} ${BL}$server${CL}"
echo -e "${TAB}${BGN}$(translate "Export:")${CL} ${BL}$export${CL}"
echo -e "${TAB}${BGN}$(translate "Content Types:")${CL} ${BL}$content${CL}"
echo -e "${TAB}${BGN}$(translate "NFS Version:")${CL} ${BL}$nfs_version${CL}"
echo -e ""
msg_ok "$(translate "Storage is now available in Proxmox web interface under Datacenter > Storage")"
return 0
else
msg_error "$(translate "Failed to add NFS storage to Proxmox.")"
echo "$(translate "Error details:"): $pvesm_output"
msg_warn "$(translate "The NFS share is still mounted, but not added as Proxmox storage.")"
msg_info2 "$(translate "You can add it manually through:")"
echo -e "${TAB}$(translate "Proxmox web interface: Datacenter > Storage > Add > NFS")"
echo -e "${TAB}$(translate "Command line:"): pvesm add nfs $storage_id --server $server --export $export --content backup,iso,vztmpl"
return 1
fi
}
prepare_host_directory() {
local mount_point="$1"
if [[ "$SHARE_COMMON_LOADED" == "true" ]]; then
# Use common functions for advanced directory preparation
local group_name
group_name=$(pmx_choose_or_create_group "sharedfiles")
if [[ -n "$group_name" ]]; then
local host_gid
host_gid=$(pmx_ensure_host_group "$group_name")
if [[ -n "$host_gid" ]]; then
pmx_prepare_host_shared_dir "$mount_point" "$group_name"
pmx_share_map_set "$mount_point" "$group_name"
msg_ok "$(translate "Directory prepared with shared group:") $group_name (GID: $host_gid)"
return 0
fi
fi
msg_warn "$(translate "Failed to use shared functions, using basic directory creation.")"
fi
# Fallback: basic directory creation
if ! test -d "$mount_point"; then
if mkdir -p "$mount_point"; then
msg_ok "$(translate "Mount point created on host.")"
return 0
else
msg_error "$(translate "Failed to create mount point on host.")"
return 1
fi
fi
return 0
}
mount_host_nfs_share() {
if ! which showmount >/dev/null 2>&1; then
msg_error "$(translate "NFS client tools not found. Please check Proxmox installation.")"
return 1
fi
# Step 1:
select_nfs_server || return
# Step 2:
select_nfs_export || return
# Step 2.5:
if ! validate_host_export_exists "$NFS_SERVER" "$NFS_EXPORT"; then
echo -e ""
msg_error "$(translate "Cannot proceed with invalid export path.")"
msg_success "$(translate "Press Enter to return to menu...")"
read -r
return
fi
# Step 3:
select_host_mount_point || return
# Step 4:
configure_host_mount_options || return
show_proxmenux_logo
msg_title "$(translate "Mount NFS Share on Host")"
msg_ok "$(translate "NFS server selected")"
prepare_host_directory "$MOUNT_POINT" || return 1
if mount | grep -q "$MOUNT_POINT"; then
msg_warn "$(translate "Something is already mounted at") $MOUNT_POINT"
if ! whiptail --yesno "$(translate "Do you want to unmount it first?")" 8 60 --title "$(translate "Already Mounted")"; then
return
fi
umount "$MOUNT_POINT" 2>/dev/null || true
fi
NFS_PATH="$NFS_SERVER:$NFS_EXPORT"
if mount -t nfs -o "$MOUNT_OPTIONS" "$NFS_PATH" "$MOUNT_POINT" > /dev/null 2>&1; then
msg_ok "$(translate "NFS share mounted successfully on host!")"
if touch "$MOUNT_POINT/.test_write" 2>/dev/null; then
rm "$MOUNT_POINT/.test_write" 2>/dev/null
msg_ok "$(translate "Write access confirmed.")"
else
msg_warn "$(translate "Read-only access (or no write permissions).")"
fi
if [[ "$PERMANENT_MOUNT" == "true" ]]; then
sed -i "\|$MOUNT_POINT|d" /etc/fstab
FSTAB_ENTRY="$NFS_PATH $MOUNT_POINT nfs $MOUNT_OPTIONS 0 0"
echo "$FSTAB_ENTRY" >> /etc/fstab
msg_ok "$(translate "Added to /etc/fstab for permanent mounting.")"
msg_info "$(translate "Reloading systemd configuration...")"
systemctl daemon-reload 2>/dev/null || true
msg_ok "$(translate "Systemd configuration reloaded.")"
fi
if [[ "$PROXMOX_STORAGE" == "true" ]]; then
add_proxmox_nfs_storage "$STORAGE_ID" "$NFS_SERVER" "$NFS_EXPORT" "$MOUNT_CONTENT"
fi
echo -e ""
echo -e "${TAB}${BOLD}$(translate "Host Mount Information:")${CL}"
echo -e "${TAB}${BGN}$(translate "Server:")${CL} ${BL}$NFS_SERVER${CL}"
echo -e "${TAB}${BGN}$(translate "Export:")${CL} ${BL}$NFS_EXPORT${CL}"
echo -e "${TAB}${BGN}$(translate "Host Mount Point:")${CL} ${BL}$MOUNT_POINT${CL}"
echo -e "${TAB}${BGN}$(translate "Options:")${CL} ${BL}$MOUNT_OPTIONS${CL}"
echo -e "${TAB}${BGN}$(translate "Permanent:")${CL} ${BL}$PERMANENT_MOUNT${CL}"
if [[ "$PROXMOX_STORAGE" == "true" ]]; then
echo -e "${TAB}${BGN}$(translate "Proxmox Storage ID:")${CL} ${BL}$STORAGE_ID${CL}"
fi
else
msg_error "$(translate "Failed to mount NFS share on host.")"
echo -e "${TAB}$(translate "Please check:")"
echo -e "${TAB}$(translate "Server is accessible:"): $NFS_SERVER"
echo -e "${TAB}$(translate "Export exists:"): $NFS_EXPORT"
echo -e "${TAB}$(translate "Network connectivity")"
echo -e "${TAB}$(translate "NFS server is running")"
echo -e "${TAB}$(translate "Export permissions allow access")"
fi
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
view_host_nfs_mounts() {
show_proxmenux_logo
msg_title "$(translate "Current NFS Mounts on Host")"
echo -e "$(translate "NFS mounts on Proxmox host:"):"
echo "=================================="
CURRENT_MOUNTS=$(mount | grep -E "type nfs|:.*on.*nfs" 2>/dev/null || true)
if [[ -n "$CURRENT_MOUNTS" ]]; then
echo -e "${BOLD}$(translate "Currently Mounted:")${CL}"
echo "$CURRENT_MOUNTS"
echo ""
else
echo "$(translate "No NFS shares currently mounted on host.")"
echo ""
fi
FSTAB_NFS=$(grep "nfs" /etc/fstab 2>/dev/null || true)
if [[ -n "$FSTAB_NFS" ]]; then
echo -e "${BOLD}$(translate "Permanent Mounts (fstab):")${CL}"
echo "$FSTAB_NFS"
echo ""
echo -e "${TAB}${BOLD}$(translate "Mount Details:")${CL}"
while IFS= read -r fstab_line; do
if [[ -n "$fstab_line" && ! "$fstab_line" =~ ^# ]]; then
NFS_PATH=$(echo "$fstab_line" | awk '{print $1}')
MOUNT_POINT=$(echo "$fstab_line" | awk '{print $2}')
OPTIONS=$(echo "$fstab_line" | awk '{print $4}')
SERVER=$(echo "$NFS_PATH" | cut -d: -f1)
EXPORT=$(echo "$NFS_PATH" | cut -d: -f2)
echo -e "${TAB}${BGN}$(translate "Server:")${CL} ${BL}$SERVER${CL}"
echo -e "${TAB}${BGN}$(translate "Export:")${CL} ${BL}$EXPORT${CL}"
echo -e "${TAB}${BGN}$(translate "Host Mount Point:")${CL} ${BL}$MOUNT_POINT${CL}"
echo -e "${TAB}${BGN}$(translate "Options:")${CL} ${BL}$OPTIONS${CL}"
if mount | grep -q "$MOUNT_POINT"; then
echo -e "${TAB}${BGN}$(translate "Status:")${CL} ${GN}$(translate "Mounted")${CL}"
else
echo -e "${TAB}${BGN}$(translate "Status:")${CL} ${RD}$(translate "Not Mounted")${CL}"
fi
echo ""
fi
done <<< "$FSTAB_NFS"
else
echo "$(translate "No NFS mounts found in fstab.")"
fi
echo ""
echo "$(translate "Proxmox NFS Storage Status:")"
if which pvesm >/dev/null 2>&1; then
NFS_STORAGES=$(pvesm status 2>/dev/null | grep "nfs" || true)
if [[ -n "$NFS_STORAGES" ]]; then
echo "$NFS_STORAGES"
else
echo "$(translate "No NFS storage configured in Proxmox.")"
fi
else
echo "$(translate "pvesm command not available.")"
fi
echo ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
unmount_host_nfs_share() {
MOUNTS=$(mount | grep -E "type nfs|:.*on.*nfs" | awk '{print $3}' | sort -u || true)
FSTAB_MOUNTS=$(grep -E "nfs" /etc/fstab 2>/dev/null | grep -v "^#" | awk '{print $2}' | sort -u || true)
ALL_MOUNTS=$(echo -e "$MOUNTS\n$FSTAB_MOUNTS" | sort -u | grep -v "^$" || true)
if [[ -z "$ALL_MOUNTS" ]]; then
dialog --backtitle "ProxMenux" --title "$(translate "No Mounts")" --msgbox "\n$(translate "No NFS mounts found on host.")" 8 50
return
fi
OPTIONS=()
while IFS= read -r mount_point; do
if [[ -n "$mount_point" ]]; then
NFS_PATH=$(mount | grep "$mount_point" | awk '{print $1}' || grep "$mount_point" /etc/fstab | awk '{print $1}' || echo "Unknown")
SERVER=$(echo "$NFS_PATH" | cut -d: -f1)
EXPORT=$(echo "$NFS_PATH" | cut -d: -f2)
OPTIONS+=("$mount_point" "$SERVER:$EXPORT")
fi
done <<< "$ALL_MOUNTS"
SELECTED_MOUNT=$(dialog --backtitle "ProxMenux" --title "$(translate "Unmount NFS Share")" --menu "$(translate "Select mount point to unmount:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
[[ -z "$SELECTED_MOUNT" ]] && return
NFS_PATH=$(mount | grep "$SELECTED_MOUNT" | awk '{print $1}' || grep "$SELECTED_MOUNT" /etc/fstab | awk '{print $1}' || echo "Unknown")
SERVER=$(echo "$NFS_PATH" | cut -d: -f1)
EXPORT=$(echo "$NFS_PATH" | cut -d: -f2)
PROXMOX_STORAGE=""
if which pvesm >/dev/null 2>&1; then
NFS_STORAGES=$(pvesm status 2>/dev/null | grep "nfs" | awk '{print $1}' || true)
while IFS= read -r storage_id; do
if [[ -n "$storage_id" ]]; then
STORAGE_INFO=$(pvesm config "$storage_id" 2>/dev/null || true)
STORAGE_SERVER=$(echo "$STORAGE_INFO" | grep "server" | awk '{print $2}')
STORAGE_EXPORT=$(echo "$STORAGE_INFO" | grep "export" | awk '{print $2}')
if [[ "$STORAGE_SERVER" == "$SERVER" && "$STORAGE_EXPORT" == "$EXPORT" ]]; then
PROXMOX_STORAGE="$storage_id"
break
fi
fi
done <<< "$NFS_STORAGES"
fi
CONFIRMATION_MSG="$(translate "Are you sure you want to unmount this NFS share?")\n\n$(translate "Mount Point:"): $SELECTED_MOUNT\n$(translate "Server:"): $SERVER\n$(translate "Export:"): $EXPORT\n\n$(translate "This will:")\n• $(translate "Unmount the NFS share")\n• $(translate "Remove from /etc/fstab")"
if [[ -n "$PROXMOX_STORAGE" ]]; then
CONFIRMATION_MSG="$CONFIRMATION_MSG\n• $(translate "Remove Proxmox storage:"): $PROXMOX_STORAGE"
fi
CONFIRMATION_MSG="$CONFIRMATION_MSG\n• $(translate "Remove mount point directory")"
if whiptail --yesno "$CONFIRMATION_MSG" 16 80 --title "$(translate "Confirm Unmount")"; then
show_proxmenux_logo
msg_title "$(translate "Unmount NFS Share from Host")"
if [[ -n "$PROXMOX_STORAGE" ]]; then
if pvesm remove "$PROXMOX_STORAGE" 2>/dev/null; then
msg_ok "$(translate "Proxmox storage removed successfully.")"
else
msg_warn "$(translate "Failed to remove Proxmox storage, continuing with unmount...")"
fi
fi
if mount | grep -q "$SELECTED_MOUNT"; then
if umount "$SELECTED_MOUNT"; then
msg_ok "$(translate "Successfully unmounted.")"
else
msg_warn "$(translate "Failed to unmount. Trying force unmount...")"
if umount -f "$SELECTED_MOUNT" 2>/dev/null; then
msg_ok "$(translate "Force unmount successful.")"
else
msg_error "$(translate "Failed to unmount. Mount point may be busy.")"
echo -e "${TAB}$(translate "Try closing any applications using the mount point.")"
fi
fi
fi
msg_info "$(translate "Removing from /etc/fstab...")"
sed -i "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
msg_ok "$(translate "Removed from /etc/fstab.")"
echo -e ""
msg_ok "$(translate "NFS share unmounted successfully from host!")"
if [[ -n "$PROXMOX_STORAGE" ]]; then
echo -e "${TAB}${BGN}$(translate "Proxmox storage removed:")${CL} ${BL}$PROXMOX_STORAGE${CL}"
fi
echo -e "${TAB}${BGN}$(translate "Mount point unmounted:")${CL} ${BL}$SELECTED_MOUNT${CL}"
echo -e "${TAB}${BGN}$(translate "Removed from fstab:")${CL} ${BL}Yes${CL}"
fi
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
manage_proxmox_storage() {
if ! command -v pvesm >/dev/null 2>&1; then
dialog --backtitle "ProxMenux" --title "$(translate "Error")" --msgbox "\n$(translate "pvesm command not found. This should not happen on Proxmox.")" 8 60
return
fi
NFS_STORAGES=$(pvesm status 2>/dev/null | awk '$2 == "nfs" {print $1}')
if [[ -z "$NFS_STORAGES" ]]; then
dialog --backtitle "ProxMenux" --title "$(translate "No NFS Storage")" --msgbox "\n$(translate "No NFS storage found in Proxmox.")" 8 60
return
fi
OPTIONS=()
while IFS= read -r storage_id; do
if [[ -n "$storage_id" ]]; then
STORAGE_INFO=$(pvesm config "$storage_id" 2>/dev/null || true)
SERVER=$(echo "$STORAGE_INFO" | grep "server" | awk '{print $2}')
EXPORT=$(echo "$STORAGE_INFO" | grep "export" | awk '{print $2}')
if [[ -n "$SERVER" && -n "$EXPORT" ]]; then
OPTIONS+=("$storage_id" "$SERVER:$EXPORT")
else
OPTIONS+=("$storage_id" "$(translate "NFS Storage")")
fi
fi
done <<< "$NFS_STORAGES"
SELECTED_STORAGE=$(dialog --backtitle "ProxMenux" --title "$(translate "Manage Proxmox NFS Storage")" --menu "$(translate "Select storage to manage:")" 20 80 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
[[ -z "$SELECTED_STORAGE" ]] && return
STORAGE_INFO=$(pvesm config "$SELECTED_STORAGE" 2>/dev/null || true)
SERVER=$(echo "$STORAGE_INFO" | grep "server" | awk '{print $2}')
EXPORT=$(echo "$STORAGE_INFO" | grep "export" | awk '{print $2}')
CONTENT=$(echo "$STORAGE_INFO" | grep "content" | awk '{print $2}')
FSTAB_NFS=$(grep "nfs" /etc/fstab 2>/dev/null || true)
if [[ -n "$FSTAB_NFS" ]]; then
while IFS= read -r fstab_line; do
if [[ -n "$fstab_line" && ! "$fstab_line" =~ ^# ]]; then
NFS_PATH=$(echo "$fstab_line" | awk '{print $1}')
MOUNT_POINT=$(echo "$fstab_line" | awk '{print $2}')
OPTIONS=$(echo "$fstab_line" | awk '{print $4}')
SERVER=$(echo "$NFS_PATH" | cut -d: -f1)
EXPORT=$(echo "$NFS_PATH" | cut -d: -f2)
fi
done <<< "$FSTAB_NFS"
fi
if whiptail --yesno "$(translate "Are you sure you want to REMOVE storage") $SELECTED_STORAGE?\n\n$(translate "Server:"): $SERVER\n$(translate "Export:"): $EXPORT\n\n$(translate "WARNING: This will permanently remove the storage from Proxmox configuration.")\n$(translate "The NFS mount on the host will NOT be affected.")" 14 80 --title "$(translate "Remove Storage")"; then
show_proxmenux_logo
msg_title "$(translate "Remove Storage")"
if pvesm remove "$SELECTED_STORAGE" 2>/dev/null; then
msg_ok "$(translate "Storage removed successfully from Proxmox.")"
echo -e ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
else
msg_error "$(translate "Failed to remove storage.")"
fi
fi
}
test_host_nfs_connectivity() {
show_proxmenux_logo
msg_title "$(translate "Test NFS Connectivity on Host")"
echo -e "$(translate "NFS Client Status on Proxmox Host:"):"
echo "=================================="
if which showmount >/dev/null 2>&1; then
echo "$(translate "NFS Client Tools: AVAILABLE")"
if systemctl is-active --quiet rpcbind 2>/dev/null; then
echo "$(translate "RPC Bind Service: RUNNING")"
else
echo "$(translate "RPC Bind Service: STOPPED")"
msg_warn "$(translate "Starting rpcbind service...")"
systemctl start rpcbind 2>/dev/null || true
fi
echo ""
echo "$(translate "Current NFS mounts on host:")"
CURRENT_MOUNTS=$(mount | grep -E "type nfs|:.*on.*nfs" 2>/dev/null || true)
if [[ -n "$CURRENT_MOUNTS" ]]; then
echo "$CURRENT_MOUNTS"
else
echo "$(translate "No NFS mounts active on host.")"
fi
echo ""
echo "$(translate "Testing network connectivity...")"
FSTAB_SERVERS=$(grep "nfs" /etc/fstab 2>/dev/null | awk '{print $1}' | cut -d: -f1 | sort -u || true)
if [[ -n "$FSTAB_SERVERS" ]]; then
while IFS= read -r server; do
if [[ -n "$server" ]]; then
echo -n "$(translate "Testing") $server: "
if ping -c 1 -W 2 "$server" >/dev/null 2>&1; then
echo -e "${GN}$(translate "Reachable")${CL}"
echo -n " $(translate "NFS port 2049"): "
if nc -z -w 2 "$server" 2049 2>/dev/null; then
echo -e "${GN}$(translate "Open")${CL}"
else
echo -e "${RD}$(translate "Closed")${CL}"
fi
echo -n " $(translate "Export list test"): "
if showmount -e "$server" >/dev/null 2>&1; then
echo -e "${GN}$(translate "Available")${CL}"
else
echo -e "${RD}$(translate "Failed")${CL}"
fi
else
echo -e "${RD}$(translate "Unreachable")${CL}"
fi
fi
done <<< "$FSTAB_SERVERS"
else
echo "$(translate "No NFS servers configured to test.")"
fi
echo ""
echo "$(translate "Proxmox NFS Storage Status:")"
if which pvesm >/dev/null 2>&1; then
NFS_STORAGES=$(pvesm status 2>/dev/null | grep "nfs" || true)
if [[ -n "$NFS_STORAGES" ]]; then
echo "$NFS_STORAGES"
else
echo "$(translate "No NFS storage configured in Proxmox.")"
fi
else
echo "$(translate "pvesm command not available.")"
fi
else
echo "$(translate "NFS Client Tools: NOT AVAILABLE")"
echo ""
echo "$(translate "This is unusual for Proxmox. NFS client tools should be installed.")"
fi
echo ""
echo "$(translate "ProxMenux Extensions:")"
if [[ "$SHARE_COMMON_LOADED" == "true" ]]; then
echo "$(translate "Shared Functions: LOADED")"
if [[ -f "$PROXMENUX_SHARE_MAP_DB" ]]; then
MAPPED_DIRS=$(wc -l < "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null || echo "0")
echo "$(translate "Mapped directories:"): $MAPPED_DIRS"
fi
else
echo "$(translate "Shared Functions: NOT LOADED (using fallback methods)")"
fi
echo ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
# === Main Menu ===
while true; do
CHOICE=$(dialog --backtitle "ProxMenux" --title "$(translate "NFS Host Manager - Proxmox Host")" \
--menu "$(translate "Choose an option:")" 22 80 14 \
"1" "$(translate "Mount NFS Share on Host")" \
"2" "$(translate "View Current Host NFS Mounts")" \
"3" "$(translate "Unmount NFS Share from Host")" \
"4" "$(translate "Remove Proxmox NFS Storage")" \
"5" "$(translate "Test NFS Connectivity")" \
"6" "$(translate "Exit")" \
3>&1 1>&2 2>&3)
RETVAL=$?
if [[ $RETVAL -ne 0 ]]; then
exit 0
fi
case $CHOICE in
1) mount_host_nfs_share ;;
2) view_host_nfs_mounts ;;
3) unmount_host_nfs_share ;;
4) manage_proxmox_storage ;;
5) test_host_nfs_connectivity ;;
6) exit 0 ;;
*) exit 0 ;;
esac
done

View File

@@ -0,0 +1,572 @@
#!/bin/bash
# ==========================================================
# ProxMenu CT - NFS Manager for Proxmox LXC (Simple + Universal)
# ==========================================================
# Based on ProxMenux by MacRimi
# ==========================================================
# Description:
# This script allows you to manage NFS shares inside Proxmox CTs:
# - Create NFS exports with universal sharedfiles group
# - View configured exports
# - Delete existing exports
# - Check NFS service status
# ==========================================================
# Configuration
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
# Load shared functions
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
msg_error "$(translate "Could not load shared functions. Script cannot continue.")"
exit 1
fi
load_language
initialize_cache
select_privileged_lxc
setup_universal_sharedfiles_group() {
local ctid="$1"
msg_info "$(translate "Setting sharedfiles group with UID remapping...")"
if ! pct exec "$ctid" -- getent group sharedfiles >/dev/null 2>&1; then
pct exec "$ctid" -- groupadd -g 101000 sharedfiles
msg_ok "$(translate "Created sharedfiles group (GID: 101000)")"
else
local current_gid=$(pct exec "$ctid" -- getent group sharedfiles | cut -d: -f3)
if [[ "$current_gid" != "101000" ]]; then
pct exec "$ctid" -- groupmod -g 101000 sharedfiles
msg_ok "$(translate "Updated sharedfiles group to GID: 101000")"
else
msg_ok "$(translate "Sharedfiles group already exists (GID: 101000)")"
fi
fi
local lxc_users=$(pct exec "$ctid" -- awk -F: '$3 >= 1000 && $3 < 65534 {print $1 ":" $3}' /etc/passwd)
if [[ -n "$lxc_users" ]]; then
msg_info "$(translate "Adding existing users to sharedfiles group...")"
while IFS=: read -r username uid; do
if [[ -n "$username" ]]; then
pct exec "$ctid" -- usermod -a -G sharedfiles "$username" 2>/dev/null || true
msg_ok "$(translate "Added user") $username (UID: $uid) $(translate "to sharedfiles group")"
fi
done <<< "$lxc_users"
fi
msg_info "$(translate "Creating UID remapping for unprivileged container compatibility...")"
local remapped_count=0
if [[ -n "$lxc_users" ]]; then
while IFS=: read -r username uid; do
if [[ -n "$uid" ]]; then
local remapped_uid=$((uid + 100000))
local remapped_username="remap_${uid}"
if ! pct exec "$ctid" -- id "$remapped_username" >/dev/null 2>&1; then
pct exec "$ctid" -- useradd -u "$remapped_uid" -g sharedfiles -s /bin/false -M "$remapped_username" 2>/dev/null || true
msg_ok "$(translate "Created remapped user") $remapped_username (UID: $remapped_uid)"
((remapped_count++))
else
pct exec "$ctid" -- usermod -g sharedfiles "$remapped_username" 2>/dev/null || true
fi
fi
done <<< "$lxc_users"
fi
local common_uids=(33 1000 1001 1002)
for base_uid in "${common_uids[@]}"; do
local remapped_uid=$((base_uid + 100000))
local remapped_username="remap_${base_uid}"
if ! pct exec "$ctid" -- id "$remapped_username" >/dev/null 2>&1; then
pct exec "$ctid" -- useradd -u "$remapped_uid" -g sharedfiles -s /bin/false -M "$remapped_username" 2>/dev/null || true
msg_ok "$(translate "Created common remapped user") $remapped_username (UID: $remapped_uid)"
((remapped_count++))
fi
done
msg_ok "$(translate "Universal sharedfiles group configured with") $remapped_count $(translate "remapped users")"
}
select_mount_point() {
while true; do
METHOD=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Folder")" \
--menu "$(translate "How do you want to select the folder to export?")" 15 60 5 \
"auto" "$(translate "Select from folders inside /mnt")" \
"manual" "$(translate "Enter path manually")" \
3>&1 1>&2 2>&3)
if [[ $? -ne 0 ]]; then
return 1
fi
case "$METHOD" in
auto)
DIRS=$(pct exec "$CTID" -- find /mnt -maxdepth 1 -mindepth 1 -type d 2>/dev/null)
if [[ -z "$DIRS" ]]; then
whiptail --title "$(translate "No Folders")" --msgbox "$(translate "No folders found inside /mnt in the CT.")" 8 60
continue
fi
OPTIONS=()
while IFS= read -r dir; do
name=$(basename "$dir")
OPTIONS+=("$dir" "$name")
done <<< "$DIRS"
MOUNT_POINT=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Folder")" \
--menu "$(translate "Choose a folder to export:")" 20 60 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
if [[ $? -ne 0 ]]; then
return 1
fi
[[ -n "$MOUNT_POINT" ]] && return 0
;;
manual)
CT_NAME=$(pct config "$CTID" | awk -F: '/hostname/ {print $2}' | xargs)
DEFAULT_MOUNT_POINT="/mnt/${CT_NAME}_nfs"
MOUNT_POINT=$(whiptail --title "$(translate "Mount Point")" \
--inputbox "$(translate "Enter the mount point for the NFS export (e.g., /mnt/mynfs):")" \
10 70 "$DEFAULT_MOUNT_POINT" 3>&1 1>&2 2>&3)
if [[ $? -ne 0 ]]; then
return 1
fi
if [[ -z "$MOUNT_POINT" ]]; then
whiptail --title "$(translate "Error")" \
--msgbox "$(translate "No mount point was specified.")" 8 50
continue
fi
pct exec "$CTID" -- mkdir -p "$MOUNT_POINT" 2>/dev/null
return 0
;;
esac
done
}
get_network_config() {
NETWORK=$(whiptail --backtitle "ProxMenux" --title "$(translate "Network Configuration")" --menu "\n$(translate "Select network access level:")" 15 70 4 \
"1" "$(translate "Local network only (192.168.0.0/16)")" \
"2" "$(translate "Specific subnet (enter manually)")" \
"3" "$(translate "Specific host (enter IP)")" 3>&1 1>&2 2>&3)
case "$NETWORK" in
1)
NETWORK_RANGE="192.168.0.0/16"
;;
2)
clear
NETWORK_RANGE=$(whiptail --inputbox "$(translate "Enter subnet (e.g., 192.168.0.0/24):")" 10 60 "192.168.0.0/24" --title "$(translate "Subnet")" 3>&1 1>&2 2>&3)
[[ -z "$NETWORK_RANGE" ]] && return 1
;;
3)
dialog
NETWORK_RANGE=$(whiptail --inputbox "$(translate "Enter host IP (e.g., 192.168.0.100):")" 10 60 --title "$(translate "Host IP")" 3>&1 1>&2 2>&3)
[[ -z "$NETWORK_RANGE" ]] && return 1
;;
*)
return 1
;;
esac
return 0
}
select_export_options() {
EXPORT_OPTIONS=$(whiptail --title "$(translate "Export Options")" --menu \
"\n$(translate "Select export permissions:")" 15 70 3 \
"1" "$(translate "Read-Write (recommended)")" \
"2" "$(translate "Read-Only")" \
"3" "$(translate "Custom options")" 3>&1 1>&2 2>&3)
case "$EXPORT_OPTIONS" in
1)
OPTIONS="rw,sync,no_subtree_check,all_squash,anonuid=0,anongid=101000"
;;
2)
OPTIONS="ro,sync,no_subtree_check,all_squash,anonuid=0,anongid=101000"
;;
3)
OPTIONS=$(whiptail --inputbox "$(translate "Enter custom NFS options:")" \
10 70 "rw,sync,no_subtree_check,all_squash,anonuid=0,anongid=101000" \
--title "$(translate "Custom Options")" 3>&1 1>&2 2>&3)
[[ -z "$OPTIONS" ]] && OPTIONS="rw,sync,no_subtree_check,all_squash,anonuid=0,anongid=101000"
;;
*)
OPTIONS="rw,sync,no_subtree_check,all_squash,anonuid=0,anongid=101000"
;;
esac
}
create_nfs_export() {
show_proxmenux_logo
msg_title "$(translate "Create LXC server NFS")"
sleep 2
select_mount_point || return
get_network_config || return
select_export_options || return
msg_ok "$(translate "Directory successfully.")"
if ! pct exec "$CTID" -- dpkg -s nfs-kernel-server &>/dev/null; then
msg_info "$(translate "Installing NFS server packages inside the CT...")"
pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y nfs-kernel-server nfs-common rpcbind"
pct exec "$CTID" -- systemctl enable --now rpcbind nfs-kernel-server
msg_ok "$(translate "NFS server installed successfully.")"
else
msg_ok "$(translate "NFS server is already installed.")"
fi
setup_universal_sharedfiles_group "$CTID"
msg_info "$(translate "Setting directory ownership and permissions...")"
pct exec "$CTID" -- chown root:sharedfiles "$MOUNT_POINT"
pct exec "$CTID" -- chmod 2775 "$MOUNT_POINT"
msg_ok "$(translate "Directory configured with sharedfiles group ownership")"
EXPORT_LINE="$MOUNT_POINT $NETWORK_RANGE($OPTIONS)"
if pct exec "$CTID" -- grep -q "^$MOUNT_POINT " /etc/exports; then
if dialog --yesno "$(translate "Do you want to update the existing export?")" \
10 60 --title "$(translate "Update Export")"; then
pct exec "$CTID" -- sed -i "\|^$MOUNT_POINT |d" /etc/exports
pct exec "$CTID" -- bash -c "echo '$EXPORT_LINE' >> /etc/exports"
show_proxmenux_logo
msg_title "$(translate "Create LXC server NFS")"
msg_ok "$(translate "Directory successfully.")"
msg_ok "$(translate "Export updated successfully.")"
msg_ok "$(translate "NFS server is already installed.")"
msg_ok "$(translate "Directory configured with sharedfiles group ownership")"
fi
else
pct exec "$CTID" -- bash -c "echo '$EXPORT_LINE' >> /etc/exports"
msg_ok "$(translate "Export added successfully.")"
fi
pct exec "$CTID" -- systemctl restart rpcbind nfs-kernel-server
pct exec "$CTID" -- exportfs -ra
CT_IP=$(pct exec "$CTID" -- hostname -I | awk '{print $1}')
echo -e ""
msg_ok "$(translate "NFS export created successfully!")"
echo -e ""
echo -e "${TAB}${BOLD}$(translate "Connection details:")${CL}"
echo -e "${TAB}${BGN}$(translate "Server IP:")${CL} ${BL}$CT_IP${CL}"
echo -e "${TAB}${BGN}$(translate "Export path:")${CL} ${BL}$MOUNT_POINT${CL}"
echo -e "${TAB}${BGN}$(translate "Mount options:")${CL} ${BL}$OPTIONS${CL}"
echo -e "${TAB}${BGN}$(translate "Network access:")${CL} ${BL}$NETWORK_RANGE${CL}"
echo -e "${TAB}${BGN}$(translate "NFS Version:")${CL} ${BL}Auto-negotiation (NFSv3/NFSv4)${CL}"
echo -e ""
echo -e "${TAB}${BOLD}$(translate "Mount Examples:")${CL}"
echo -e "${TAB}${BGN}$(translate "Auto-negotiate:")${CL} ${BL}mount -t nfs $CT_IP:$MOUNT_POINT /mnt/nfs${CL}"
echo -e "${TAB}${BGN}$(translate "Force NFSv4:")${CL} ${BL}mount -t nfs4 $CT_IP:$MOUNT_POINT /mnt/nfs${CL}"
echo -e "${TAB}${BGN}$(translate "Force NFSv3:")${CL} ${BL}mount -t nfs -o vers=3 $CT_IP:$MOUNT_POINT /mnt/nfs${CL}"
echo ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
view_exports() {
show_proxmenux_logo
msg_title "$(translate "View Current Exports")"
echo -e "$(translate "Current NFS exports in CT") $CTID:"
echo "=================================="
if pct exec "$CTID" -- test -f /etc/exports; then
EXPORTS=$(pct exec "$CTID" -- cat /etc/exports | grep -v '^#' | grep -v '^$')
if [[ -n "$EXPORTS" ]]; then
echo "$EXPORTS"
echo ""
echo "$(translate "Active exports:")"
pct exec "$CTID" -- showmount -e localhost 2>/dev/null || echo "$(translate "No active exports or showmount not available")"
echo ""
echo "$(translate "Universal Group Configuration:")"
echo "=================================="
if pct exec "$CTID" -- getent group sharedfiles >/dev/null 2>&1; then
local group_members=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f4)
local sharedfiles_gid=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f3)
echo "$(translate "Shared group: sharedfiles (GID:") $sharedfiles_gid)"
local member_count=$(echo "$group_members" | tr ',' '\n' | wc -l)
echo "$(translate "Total members:") $member_count $(translate "users")"
local remapped_users=$(pct exec "$CTID" -- getent passwd | grep "^remap_" | wc -l)
if [[ "$remapped_users" -gt 0 ]]; then
echo "$(translate "Remapped users:") $remapped_users $(translate "users (for unprivileged compatibility)")"
fi
echo "$(translate "Universal compatibility: ENABLED")"
echo "$(translate "NFS Version: Auto-negotiation (NFSv3/NFSv4)")"
else
echo "$(translate "Universal group: NOT CONFIGURED")"
fi
CT_IP=$(pct exec "$CTID" -- hostname -I | awk '{print $1}')
echo ""
echo "=================================="
echo -e "${TAB}${BOLD}$(translate "Connection Details:")${CL}"
echo -e "${TAB}${BGN}$(translate "Server IP:")${CL} ${BL}$CT_IP${CL}"
while IFS= read -r export_line; do
if [[ -n "$export_line" ]]; then
EXPORT_PATH=$(echo "$export_line" | awk '{print $1}')
echo -e "${TAB}${BGN}$(translate "Export path:")${CL} ${BL}$EXPORT_PATH${CL}"
echo ""
fi
done <<< "$EXPORTS"
else
echo "$(translate "No exports configured.")"
fi
else
echo "$(translate "/etc/exports file does not exist.")"
fi
echo ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
delete_export() {
if ! pct exec "$CTID" -- test -f /etc/exports; then
dialog --title "$(translate "Error")" --msgbox "\n$(translate "No exports file found.")" 8 50
return
fi
EXPORTS=$(pct exec "$CTID" -- awk '!/^#|^$/ {print NR, $0}' /etc/exports)
if [[ -z "$EXPORTS" ]]; then
dialog --title "$(translate "No Exports")" --msgbox "$(translate "No exports found in /etc/exports.")" 8 60
return
fi
OPTIONS=()
while read -r line; do
[[ -z "$line" ]] && continue
NUM=$(echo "$line" | awk '{print $1}')
EXPORT_LINE=$(echo "$line" | cut -d' ' -f2-)
EXPORT_PATH=$(echo "$EXPORT_LINE" | awk '{print $1}')
EXPORT_CLIENT=$(echo "$EXPORT_LINE" | awk '{print $2}' | cut -d'(' -f1)
[[ -z "$EXPORT_PATH" || -z "$EXPORT_CLIENT" ]] && continue
OPTIONS+=("$NUM" "$EXPORT_PATH $EXPORT_CLIENT")
done <<< "$EXPORTS"
SELECTED_NUM=$(dialog --title "$(translate "Delete Export")" --menu "$(translate "Select an export to delete:")" 20 70 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
[ -z "$SELECTED_NUM" ] && return
EXPORT_LINE=$(echo "$EXPORTS" | awk -v num="$SELECTED_NUM" '$1 == num {$1=""; print substr($0,2)}')
if whiptail --yesno "$(translate "Are you sure you want to delete this export?")\n\n$EXPORT_LINE" 10 70 --title "$(translate "Confirm Deletion")"; then
show_proxmenux_logo
msg_title "$(translate "Delete Export")"
pct exec "$CTID" -- sed -i "${SELECTED_NUM}d" /etc/exports
pct exec "$CTID" -- exportfs -ra
pct exec "$CTID" -- systemctl restart nfs-kernel-server
msg_ok "$(translate "Export deleted and NFS service restarted.")"
fi
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
check_nfs_status() {
show_proxmenux_logo
msg_title "$(translate "Check NFS Status")"
echo -e "$(translate "NFS Service Status in CT") $CTID:"
echo "=================================="
if pct exec "$CTID" -- dpkg -s nfs-kernel-server &>/dev/null; then
echo "$(translate "NFS Server: INSTALLED")"
if pct exec "$CTID" -- systemctl is-active --quiet nfs-kernel-server; then
echo "$(translate "NFS Service: RUNNING")"
else
echo "$(translate "NFS Service: STOPPED")"
fi
if pct exec "$CTID" -- systemctl is-active --quiet rpcbind; then
echo "$(translate "RPC Bind Service: RUNNING")"
else
echo "$(translate "RPC Bind Service: STOPPED")"
fi
echo ""
echo "$(translate "NFS Version Configuration:")"
echo "$(translate "Version: Auto-negotiation (NFSv3/NFSv4)")"
echo "$(translate "Client determines best version to use")"
echo ""
echo "$(translate "Universal Group Configuration:")"
if pct exec "$CTID" -- getent group sharedfiles >/dev/null 2>&1; then
echo "$(translate "Shared group: CONFIGURED")"
local group_members=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f4)
local sharedfiles_gid=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f3)
echo "$(translate "Group GID:") $sharedfiles_gid"
local member_count=$(echo "$group_members" | tr ',' '\n' | wc -l)
echo "$(translate "Total members:") $member_count $(translate "users")"
local remapped_users=$(pct exec "$CTID" -- getent passwd | grep "^remap_" | wc -l)
echo "$(translate "Remapped users:") $remapped_users $(translate "users")"
echo "$(translate "Universal compatibility: ENABLED")"
else
echo "$(translate "Universal group: NOT CONFIGURED")"
fi
echo ""
echo "$(translate "Listening ports:")"
pct exec "$CTID" -- ss -tlnp | grep -E ':(111|2049|20048)' || echo "$(translate "No NFS ports found")"
else
echo "$(translate "NFS Server: NOT INSTALLED")"
fi
echo ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
uninstall_nfs() {
if ! pct exec "$CTID" -- dpkg -s nfs-kernel-server &>/dev/null; then
dialog --title "$(translate "NFS Not Installed")" --msgbox "\n$(translate "NFS server is not installed in this CT.")" 8 60
return
fi
if ! whiptail --title "$(translate "Uninstall NFS Server")" \
--yesno "$(translate "WARNING: This will completely remove NFS server from the CT.")\n\n$(translate "This action will:")\n$(translate "• Stop all NFS services")\n$(translate "• Remove all exports")\n$(translate "• Uninstall NFS packages")\n$(translate "• Remove universal sharedfiles group")\n$(translate "• Clean up remapped users")\n\n$(translate "Are you sure you want to continue?")" \
18 70; then
return
fi
show_proxmenux_logo
msg_title "$(translate "Uninstall NFS Server")"
msg_info "$(translate "Stopping NFS services...")"
pct exec "$CTID" -- systemctl stop nfs-kernel-server 2>/dev/null || true
pct exec "$CTID" -- systemctl stop rpcbind 2>/dev/null || true
pct exec "$CTID" -- systemctl disable nfs-kernel-server 2>/dev/null || true
pct exec "$CTID" -- systemctl disable rpcbind 2>/dev/null || true
msg_ok "$(translate "NFS services stopped and disabled.")"
if pct exec "$CTID" -- test -f /etc/exports; then
pct exec "$CTID" -- truncate -s 0 /etc/exports
msg_ok "$(translate "Exports cleared.")"
fi
msg_info "$(translate "Removing remapped users...")"
local remapped_users=$(pct exec "$CTID" -- getent passwd | grep "^remap_" | cut -d: -f1)
if [[ -n "$remapped_users" ]]; then
while IFS= read -r username; do
if [[ -n "$username" ]]; then
pct exec "$CTID" -- userdel "$username" 2>/dev/null || true
msg_ok "$(translate "Removed remapped user:") $username"
fi
done <<< "$remapped_users"
fi
if pct exec "$CTID" -- getent group sharedfiles >/dev/null 2>&1; then
local regular_members=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f4 | tr ',' '\n' | grep -v "^remap_" | wc -l)
if [[ "$regular_members" -eq 0 ]]; then
pct exec "$CTID" -- groupdel sharedfiles 2>/dev/null || true
msg_ok "$(translate "Removed sharedfiles group.")"
else
msg_warn "$(translate "Kept sharedfiles group (has regular users assigned).")"
fi
fi
pct exec "$CTID" -- apt-get remove --purge -y nfs-kernel-server nfs-common 2>/dev/null || true
pct exec "$CTID" -- apt-get autoremove -y 2>/dev/null || true
msg_ok "$(translate "NFS packages removed.")"
msg_info "$(translate "Cleaning up remaining processes...")"
pct exec "$CTID" -- pkill -f nfs 2>/dev/null || true
pct exec "$CTID" -- pkill -f rpc 2>/dev/null || true
sleep 2
msg_ok "$(translate "Universal NFS server has been completely uninstalled!")"
echo -e ""
echo -e "${TAB}${BOLD}$(translate "Uninstallation Summary:")${CL}"
echo -e "${TAB}${BGN}$(translate "Services:")${CL} ${BL}$(translate "Stopped and disabled")${CL}"
echo -e "${TAB}${BGN}$(translate "Packages:")${CL} ${BL}$(translate "Removed")${CL}"
echo -e "${TAB}${BGN}$(translate "Exports:")${CL} ${BL}$(translate "Cleared")${CL}"
echo -e "${TAB}${BGN}$(translate "Universal Group:")${CL} ${BL}$(translate "Cleaned up")${CL}"
echo -e "${TAB}${BGN}$(translate "Remapped Users:")${CL} ${BL}$(translate "Removed")${CL}"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
# === Main Menu ===
while true; do
CHOICE=$(dialog --title "$(translate "NFS LXC Manager - CT") $CTID" --menu "$(translate "Choose an option:")" 20 70 12 \
"1" "$(translate "Create Universal NFS Export")" \
"2" "$(translate "View Current Exports")" \
"3" "$(translate "Delete Export")" \
"4" "$(translate "Check NFS Status")" \
"5" "$(translate "Uninstall NFS Server")" \
"6" "$(translate "Exit")" 3>&1 1>&2 2>&3)
case $CHOICE in
1) create_nfs_export ;;
2) view_exports ;;
3) delete_export ;;
4) check_nfs_status ;;
5) uninstall_nfs ;;
6) exit 0 ;;
*) exit 0 ;;
esac
done

File diff suppressed because it is too large Load Diff

1266
scripts/share/samba_host.sh Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,576 @@
#!/bin/bash
# ==========================================================
# ProxMenu CT - Samba Manager for Proxmox LXC
# ==========================================================
# Based on ProxMenux by MacRimi
# ==========================================================
# Description:
# This script allows you to manage Samba shares inside Proxmox CTs:
# - Create shared folders
# - View configured shares
# - Delete existing shares
# - Check Samba service status
# ==========================================================
# Configuration
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
CREDENTIALS_DIR="/etc/samba/credentials"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
SHARE_COMMON_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/global/share-common.func"
if ! source <(curl -s "$SHARE_COMMON_URL" 2>/dev/null); then
msg_error "$(translate "Could not load shared functions. Script cannot continue.")"
exit 1
fi
load_language
initialize_cache
select_privileged_lxc
select_mount_point() {
while true; do
METHOD=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Folder")" \
--menu "$(translate "How do you want to select the folder to share?")" 15 60 5 \
"auto" "$(translate "Select from folders inside /mnt")" \
"manual" "$(translate "Enter path manually")" \
3>&1 1>&2 2>&3)
if [[ $? -ne 0 ]]; then
return 1
fi
case "$METHOD" in
auto)
DIRS=$(pct exec "$CTID" -- find /mnt -maxdepth 1 -mindepth 1 -type d 2>/dev/null)
if [[ -z "$DIRS" ]]; then
whiptail --title "$(translate "No Folders")" --msgbox "$(translate "No folders found inside /mnt in the CT.")" 8 60
continue
fi
OPTIONS=()
while IFS= read -r dir; do
name=$(basename "$dir")
OPTIONS+=("$dir" "$name")
done <<< "$DIRS"
MOUNT_POINT=$(whiptail --title "$(translate "Select Folder")" \
--menu "$(translate "Choose a folder to share:")" 20 60 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
if [[ $? -ne 0 ]]; then
return 1
fi
[[ -n "$MOUNT_POINT" ]] && return 0
;;
manual)
CT_NAME=$(pct config "$CTID" | awk -F: '/hostname/ {print $2}' | xargs)
DEFAULT_MOUNT_POINT="/mnt/${CT_NAME}_share"
MOUNT_POINT=$(whiptail --title "$(translate "Mount Point")" \
--inputbox "$(translate "Enter the mount point for the shared folder (e.g., /mnt/myshare):")" \
10 70 "$DEFAULT_MOUNT_POINT" 3>&1 1>&2 2>&3)
if [[ $? -ne 0 ]]; then
return 1
fi
clear
if [[ -z "$MOUNT_POINT" ]]; then
whiptail --title "$(translate "Error")" --msgbox "\n$(translate "No mount point was specified.")" 8 50
continue
else
return 0
fi
;;
esac
done
}
create_share() {
show_proxmenux_logo
msg_title "$(translate "Create Samba server service")"
sleep 2
select_mount_point || return
if ! pct exec "$CTID" -- test -d "$MOUNT_POINT"; then
if whiptail --yesno "$(translate "The directory does not exist in the CT.")\n\n$MOUNT_POINT\n\n$(translate "Do you want to create it?")" 12 70 --title "$(translate "Create Directory")"; then
pct exec "$CTID" -- mkdir -p "$MOUNT_POINT"
msg_ok "$(translate "Directory created successfully.")"
else
msg_error "$(translate "Directory does not exist and was not created.")"
return
fi
fi
if pct exec "$CTID" -- dpkg -s samba &>/dev/null; then
SAMBA_INSTALLED=true
else
SAMBA_INSTALLED=false
fi
if [ "$SAMBA_INSTALLED" = false ]; then
echo -e "${TAB}$(translate "Installing Samba server packages inside the CT...")"
pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y samba samba-common-bin acl"
USERNAME=$(whiptail --inputbox "$(translate "Enter the Samba username:")" 10 60 "proxmenux" --title "$(translate "Samba User")" 3>&1 1>&2 2>&3)
[[ -z "$USERNAME" ]] && msg_error "$(translate "No username provided.")" && return
while true; do
PASSWORD1=$(whiptail --passwordbox "$(translate "Enter the password for Samba user:")" 10 60 --title "$(translate "Samba Password")" 3>&1 1>&2 2>&3)
[[ -z "$PASSWORD1" ]] && msg_error "$(translate "No password provided.")" && return
PASSWORD2=$(whiptail --passwordbox "$(translate "Confirm the password:")" 10 60 --title "$(translate "Confirm Password")" 3>&1 1>&2 2>&3)
[[ -z "$PASSWORD2" ]] && msg_error "$(translate "Password confirmation is required.")" && return
if [[ "$PASSWORD1" != "$PASSWORD2" ]]; then
whiptail --title "$(translate "Password Mismatch")" --msgbox "$(translate "The passwords do not match. Please try again.")" 10 60
else
PASSWORD="$PASSWORD1"
break
fi
done
if ! pct exec "$CTID" -- id "$USERNAME" &>/dev/null; then
pct exec "$CTID" -- adduser --disabled-password --gecos "" "$USERNAME"
fi
pct exec "$CTID" -- bash -c "echo -e '$PASSWORD\n$PASSWORD' | smbpasswd -a '$USERNAME'"
msg_ok "$(translate "Samba server installed successfully.")"
else
USERNAME=$(pct exec "$CTID" -- pdbedit -L | awk -F: '{print $1}' | head -n1)
msg_ok "$(translate "Samba server is already installed.")"
echo -e "$(translate "Detected existing Samba user:") $USERNAME"
fi
IS_MOUNTED=$(pct exec "$CTID" -- mount | grep "$MOUNT_POINT" || true)
if [[ -n "$IS_MOUNTED" ]]; then
msg_info "$(translate "Detected a mounted directory from host. Setting up shared group...")"
SHARE_GID=999
GROUP_EXISTS=$(pct exec "$CTID" -- getent group sharedfiles || true)
GID_IN_USE=$(pct exec "$CTID" -- getent group "$SHARE_GID" | cut -d: -f1 || true)
if [[ -z "$GROUP_EXISTS" ]]; then
if [[ -z "$GID_IN_USE" ]]; then
pct exec "$CTID" -- groupadd -g "$SHARE_GID" sharedfiles
msg_ok "$(translate "Group 'sharedfiles' created with GID $SHARE_GID")"
else
pct exec "$CTID" -- groupadd sharedfiles
msg_warn "$(translate "GID $SHARE_GID already in use. Group 'sharedfiles' created with dynamic GID.")"
fi
else
msg_ok "$(translate "Group 'sharedfiles' already exists inside the CT")"
fi
if pct exec "$CTID" -- getent group sharedfiles >/dev/null; then
pct exec "$CTID" -- usermod -aG sharedfiles "$USERNAME"
pct exec "$CTID" -- chown root:sharedfiles "$MOUNT_POINT"
pct exec "$CTID" -- chmod 2775 "$MOUNT_POINT"
else
msg_error "$(translate "Group 'sharedfiles' was not created successfully. Skipping chown/usermod.")"
fi
HAS_ACCESS=$(pct exec "$CTID" -- su -s /bin/bash -c "test -w '$MOUNT_POINT' && echo yes || echo no" "$USERNAME" 2>/dev/null)
if [ "$HAS_ACCESS" = "no" ]; then
pct exec "$CTID" -- setfacl -R -m "u:$USERNAME:rwx" "$MOUNT_POINT"
msg_warn "$(translate "ACL permissions applied to allow write access for user:") $USERNAME"
else
msg_ok "$(translate "Write access confirmed for user:") $USERNAME"
fi
else
msg_ok "$(translate "No shared mount detected. Applying standard local access.")"
pct exec "$CTID" -- chown -R "$USERNAME:$USERNAME" "$MOUNT_POINT"
pct exec "$CTID" -- chmod -R 755 "$MOUNT_POINT"
HAS_ACCESS=$(pct exec "$CTID" -- su -s /bin/bash -c "test -w '$MOUNT_POINT' && echo yes || echo no" "$USERNAME" 2>/dev/null)
if [ "$HAS_ACCESS" = "no" ]; then
pct exec "$CTID" -- setfacl -R -m "u:$USERNAME:rwx" "$MOUNT_POINT"
msg_warn "$(translate "ACL permissions applied for local access for user:") $USERNAME"
else
msg_ok "$(translate "Write access confirmed for user:") $USERNAME"
fi
fi
SHARE_OPTIONS=$(whiptail --title "$(translate "Share Options")" --menu "$(translate "Select share permissions:")" 15 70 3 \
"rw" "$(translate "Read-Write access")" \
"ro" "$(translate "Read-Only access")" \
"custom" "$(translate "Custom options")" 3>&1 1>&2 2>&3)
SHARE_NAME=$(basename "$MOUNT_POINT")
case "$SHARE_OPTIONS" in
rw)
CONFIG=$(cat <<EOF
[$SHARE_NAME]
comment = Shared folder for $USERNAME
path = $MOUNT_POINT
read only = no
writable = yes
browseable = yes
guest ok = no
valid users = $USERNAME
force group = sharedfiles
create mask = 0664
directory mask = 2775
force create mode = 0664
force directory mode = 2775
veto files = /lost+found/
EOF
)
;;
ro)
CONFIG=$(cat <<EOF
[$SHARE_NAME]
comment = Read-only shared folder for $USERNAME
path = $MOUNT_POINT
read only = yes
writable = no
browseable = yes
guest ok = no
valid users = $USERNAME
force group = sharedfiles
veto files = /lost+found/
EOF
)
;;
custom)
CUSTOM_CONFIG=$(whiptail --inputbox "$(translate "Enter custom Samba configuration for this share:")" 15 80 "read only = no\nwritable = yes\nbrowseable = yes\nguest ok = no" --title "$(translate "Custom Configuration")" 3>&1 1>&2 2>&3)
CONFIG=$(cat <<EOF
[$SHARE_NAME]
comment = Custom shared folder for $USERNAME
path = $MOUNT_POINT
valid users = $USERNAME
force group = sharedfiles
$CUSTOM_CONFIG
veto files = /lost+found/
EOF
)
;;
*)
CONFIG=$(cat <<EOF
[$SHARE_NAME]
comment = Shared folder for $USERNAME
path = $MOUNT_POINT
read only = no
writable = yes
browseable = yes
guest ok = no
valid users = $USERNAME
force group = sharedfiles
create mask = 0664
directory mask = 2775
force create mode = 0664
force directory mode = 2775
veto files = /lost+found/
EOF
)
;;
esac
if pct exec "$CTID" -- grep -q "\[$SHARE_NAME\]" /etc/samba/smb.conf; then
msg_warn "$(translate "The share already exists in smb.conf:") [$SHARE_NAME]"
if whiptail --yesno "$(translate "Do you want to update the existing share?")" 10 60 --title "$(translate "Update Share")"; then
pct exec "$CTID" -- sed -i "/^\[$SHARE_NAME\]/,/^$/d" /etc/samba/smb.conf
pct exec "$CTID" -- bash -c "echo '$CONFIG' >> /etc/samba/smb.conf"
msg_ok "$(translate "Share updated successfully.")"
else
return
fi
else
msg_ok "$(translate "Adding new share to smb.conf...")"
pct exec "$CTID" -- bash -c "echo '$CONFIG' >> /etc/samba/smb.conf"
msg_ok "$(translate "Share added successfully.")"
fi
pct exec "$CTID" -- systemctl restart smbd.service
CT_IP=$(pct exec "$CTID" -- hostname -I | awk '{print $1}')
echo -e ""
msg_ok "$(translate "Samba share created successfully!")"
echo -e ""
echo -e "${TAB}${BOLD}$(translate "Connection details:")${CL}"
echo -e "${TAB}${BGN}$(translate "Server IP:")${CL} ${BL}$CT_IP${CL}"
echo -e "${TAB}${BGN}$(translate "Share name:")${CL} ${BL}$SHARE_NAME${CL}"
echo -e "${TAB}${BGN}$(translate "Share path:")${CL} ${BL}$MOUNT_POINT${CL}"
echo -e "${TAB}${BGN}$(translate "Username:")${CL} ${BL}$USERNAME${CL}"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
view_shares() {
show_proxmenux_logo
msg_title "$(translate "View Current Shares")"
echo -e "$(translate "Current Samba shares in CT") $CTID:"
echo "=================================="
if pct exec "$CTID" -- test -f /etc/samba/smb.conf; then
SHARES=$(pct exec "$CTID" -- awk '/^\[.*\]/ && !/^\[global\]/ && !/^\[homes\]/ && !/^\[printers\]/ {print $0}' /etc/samba/smb.conf)
if [[ -n "$SHARES" ]]; then
while IFS= read -r share_line; do
if [[ -n "$share_line" ]]; then
SHARE_NAME=$(echo "$share_line" | sed 's/\[//g' | sed 's/\]//g')
SHARE_PATH=$(pct exec "$CTID" -- awk "/^\[$SHARE_NAME\]/,/^$/ {if(/path =/) print \$3}" /etc/samba/smb.conf)
echo "$share_line -> $SHARE_PATH"
fi
done <<< "$SHARES"
CT_IP=$(pct exec "$CTID" -- hostname -I | awk '{print $1}')
USERNAME=$(pct exec "$CTID" -- pdbedit -L | awk -F: '{print $1}' | head -n1)
echo ""
echo "=================================="
echo -e "${TAB}${BOLD}$(translate "Connection Details:")${CL}"
echo -e "${TAB}${BGN}$(translate "Server IP:")${CL} ${BL}$CT_IP${CL}"
echo -e "${TAB}${BGN}$(translate "Username:")${CL} ${BL}$USERNAME${CL}"
echo ""
echo -e "${TAB}${BOLD}$(translate "Available Shares:")${CL}"
while IFS= read -r share_line; do
if [[ -n "$share_line" ]]; then
SHARE_NAME=$(echo "$share_line" | sed 's/\[//g' | sed 's/\]//g')
SHARE_PATH=$(pct exec "$CTID" -- awk "/^\[$SHARE_NAME\]/,/^$/ {if(/path =/) print \$3}" /etc/samba/smb.conf)
echo -e "${TAB}${BGN}$(translate "Share name:")${CL} ${BL}$SHARE_NAME${CL}"
echo -e "${TAB}${BGN}$(translate "Share path:")${CL} ${BL}$SHARE_PATH${CL}"
echo -e "${TAB}${BGN}$(translate "Windows path:")${CL} ${YW}\\\\$CT_IP\\$SHARE_NAME${CL}"
echo -e "${TAB}${BGN}$(translate "Linux/Mac path:")${CL} ${YW}smb://$CT_IP/$SHARE_NAME${CL}"
echo ""
fi
done <<< "$SHARES"
else
echo "$(translate "No shares configured.")"
fi
else
echo "$(translate "/etc/samba/smb.conf file does not exist.")"
fi
echo ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
delete_share() {
if ! pct exec "$CTID" -- test -f /etc/samba/smb.conf; then
dialog --backtitle "ProxMenux" --title "$(translate "Error")" --msgbox "\n$(translate "No smb.conf file found.")" 8 50
return
fi
SHARES=$(pct exec "$CTID" -- awk '/^\[.*\]/ && !/^\[global\]/ && !/^\[homes\]/ && !/^\[printers\]/ {gsub(/\[|\]/, ""); print NR, $0}' /etc/samba/smb.conf)
if [[ -z "$SHARES" ]]; then
dialog --backtitle "ProxMenux" --title "$(translate "No Shares")" --msgbox "\n$(translate "No shares found in smb.conf.")" 8 60
return
fi
OPTIONS=()
while read -r line; do
[[ -z "$line" ]] && continue
NUM=$(echo "$line" | awk '{print $1}')
SHARE_NAME=$(echo "$line" | awk '{print $2}')
SHARE_PATH=$(pct exec "$CTID" -- awk "/^\[$SHARE_NAME\]/,/^$/ {if(/path =/) print \$3}" /etc/samba/smb.conf)
[[ -z "$SHARE_NAME" ]] && continue
OPTIONS+=("$SHARE_NAME" "$SHARE_NAME -> $SHARE_PATH")
done <<< "$SHARES"
SELECTED_SHARE=$(dialog --backtitle "ProxMenux" --title "$(translate "Delete Share")" --menu "$(translate "Select a share to delete:")" 20 70 10 "${OPTIONS[@]}" 3>&1 1>&2 2>&3)
[ -z "$SELECTED_SHARE" ] && return
SHARE_PATH=$(pct exec "$CTID" -- awk "/^\[$SELECTED_SHARE\]/,/^$/ {if(/path =/) print \$3}" /etc/samba/smb.conf)
if whiptail --yesno "$(translate "Are you sure you want to delete this share?")\n\n$(translate "Share name:"): $SELECTED_SHARE\n$(translate "Share path:"): $SHARE_PATH" 12 70 --title "$(translate "Confirm Deletion")"; then
show_proxmenux_logo
msg_title "$(translate "Delete Share")"
pct exec "$CTID" -- sed -i "/^\[$SELECTED_SHARE\]/,/^$/d" /etc/samba/smb.conf
pct exec "$CTID" -- systemctl restart smbd.service
msg_ok "$(translate "Share deleted and Samba service restarted.")"
fi
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
check_samba_status() {
show_proxmenux_logo
msg_title "$(translate "Check Samba Status")"
echo -e "$(translate "Samba Service Status in CT") $CTID:"
echo "=================================="
if pct exec "$CTID" -- dpkg -s samba &>/dev/null; then
echo "$(translate "Samba Server: INSTALLED")"
if pct exec "$CTID" -- systemctl is-active --quiet smbd; then
echo "$(translate "Samba Service: RUNNING")"
else
echo "$(translate "Samba Service: STOPPED")"
fi
if pct exec "$CTID" -- systemctl is-active --quiet nmbd; then
echo "$(translate "NetBIOS Service: RUNNING")"
else
echo "$(translate "NetBIOS Service: STOPPED")"
fi
echo ""
echo "$(translate "Listening ports:")"
pct exec "$CTID" -- ss -tlnp | grep -E ':(139|445)' || echo "$(translate "No Samba ports found")"
echo ""
echo "$(translate "Samba users:")"
pct exec "$CTID" -- pdbedit -L 2>/dev/null || echo "$(translate "No Samba users found")"
else
echo "$(translate "Samba Server: NOT INSTALLED")"
fi
echo ""
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
uninstall_samba() {
if ! pct exec "$CTID" -- dpkg -s samba &>/dev/null; then
dialog --backtitle "ProxMenux" --title "$(translate "Samba Not Installed")" --msgbox "\n$(translate "Samba server is not installed in this CT.")" 8 60
return
fi
if ! whiptail --title "$(translate "Uninstall Samba Server")" \
--yesno "$(translate "WARNING: This will completely remove Samba server from the CT.")\n\n$(translate "This action will:")\n$(translate "• Stop all Samba services")\n$(translate "• Remove all shares")\n$(translate "• Remove all Samba users")\n$(translate "• Uninstall Samba packages")\n$(translate "• Remove Samba groups")\n\n$(translate "Are you sure you want to continue?")" \
18 70; then
return
fi
show_proxmenux_logo
msg_title "$(translate "Uninstall Samba Server")"
msg_info "$(translate "Stopping Samba services...")"
pct exec "$CTID" -- systemctl stop smbd 2>/dev/null || true
pct exec "$CTID" -- systemctl stop nmbd 2>/dev/null || true
pct exec "$CTID" -- systemctl disable smbd 2>/dev/null || true
pct exec "$CTID" -- systemctl disable nmbd 2>/dev/null || true
msg_ok "$(translate "Samba services stopped and disabled.")"
if pct exec "$CTID" -- test -f /etc/samba/smb.conf; then
pct exec "$CTID" -- cp /etc/samba/smb.conf /etc/samba/smb.conf.backup.$(date +%Y%m%d_%H%M%S)
msg_ok "$(translate "Samba configuration backed up.")"
fi
SAMBA_USERS=$(pct exec "$CTID" -- pdbedit -L 2>/dev/null | awk -F: '{print $1}' || true)
if [[ -n "$SAMBA_USERS" ]]; then
while IFS= read -r user; do
if [[ -n "$user" ]]; then
pct exec "$CTID" -- smbpasswd -x "$user" 2>/dev/null || true
fi
done <<< "$SAMBA_USERS"
msg_ok "$(translate "Samba users removed.")"
fi
pct exec "$CTID" -- apt-get remove --purge -y samba samba-common-bin samba-common 2>/dev/null || true
pct exec "$CTID" -- apt-get autoremove -y 2>/dev/null || true
msg_ok "$(translate "Samba packages removed.")"
if pct exec "$CTID" -- getent group sharedfiles >/dev/null 2>&1; then
GROUP_USERS=$(pct exec "$CTID" -- getent group sharedfiles | cut -d: -f4)
if [[ -z "$GROUP_USERS" ]]; then
pct exec "$CTID" -- groupdel sharedfiles 2>/dev/null || true
msg_ok "$(translate "Samba group removed.")"
else
msg_warn "$(translate "Samba group kept (has users assigned).")"
fi
fi
msg_info "$(translate "Cleaning up Samba directories...")"
pct exec "$CTID" -- pkill -f smbd 2>/dev/null || true
pct exec "$CTID" -- pkill -f nmbd 2>/dev/null || true
pct exec "$CTID" -- rm -rf /var/lib/samba 2>/dev/null || true
pct exec "$CTID" -- rm -rf /var/cache/samba 2>/dev/null || true
sleep 2
msg_ok "$(translate "Samba server has been completely uninstalled!")"
echo -e ""
echo -e "${TAB}${BOLD}$(translate "Uninstallation Summary:")${CL}"
echo -e "${TAB}${BGN}$(translate "Services:")${CL} ${BL}$(translate "Stopped and disabled")${CL}"
echo -e "${TAB}${BGN}$(translate "Packages:")${CL} ${BL}$(translate "Removed")${CL}"
echo -e "${TAB}${BGN}$(translate "Users:")${CL} ${BL}$(translate "Removed")${CL}"
echo -e "${TAB}${BGN}$(translate "Configuration:")${CL} ${BL}$(translate "Backed up and cleared")${CL}"
echo -e "${TAB}${BGN}$(translate "Groups:")${CL} ${BL}$(translate "Cleaned up")${CL}"
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
read -r
}
# === Main Menu ===
while true; do
CHOICE=$(dialog --backtitle "ProxMenux" --title "$(translate "Samba Manager - CT") $CTID" --menu "$(translate "Choose an option:")" 20 70 12 \
"1" "$(translate "Create Samba server service")" \
"2" "$(translate "View Current Shares")" \
"3" "$(translate "Delete Share")" \
"4" "$(translate "Check Samba Status")" \
"5" "$(translate "Uninstall Samba Server")" \
"6" "$(translate "Exit")" 3>&1 1>&2 2>&3)
case $CHOICE in
1) create_share ;;
2) view_shares ;;
3) delete_share ;;
4) check_samba_status ;;
5) uninstall_samba ;;
6) exit 0 ;;
*) exit 0 ;;
esac
done

View File

@@ -223,7 +223,12 @@ is_disk_in_use() {
}
FREE_DISKS=()
LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -n1 readlink -f | sort -u)
LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -r -n1 readlink -f | sort -u)
if [[ -n "$LVM_DEVICES" ]] && echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then
IS_MOUNTED=true
fi
RAID_ACTIVE=$(grep -Po 'md\d+\s*:\s*active\s+raid[0-9]+' /proc/mdstat | awk '{print $1}' | sort -u)
while read -r DISK; do

View File

@@ -0,0 +1,275 @@
#!/bin/bash
# ==========================================================
# ProxMenux - Manual Proxmox VE 8 to 9 Upgrade Guide
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.0
# Last Updated: 13/08/2025
# ==========================================================
# Configuration ============================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
show_command() {
local step="$1"
local description="$2"
local command="$3"
local note="$4"
local command_extra="$5"
echo -e "${BGN}${step}.${CL} ${BL}${description}${CL}"
echo ""
echo -e "${TAB}${command}"
echo -e
[[ -n "$note" ]] && echo -e "${TAB}${DARK_GRAY}${note}${CL}"
[[ -n "$command_extra" ]] && echo -e "${TAB}${YW}${command_extra}${CL}"
echo ""
}
show_proxmox_upgrade_manual_guide() {
clear
show_proxmenux_logo
msg_title "$(translate "Proxmox VE 8 to 9 Manual Upgrade Guide")"
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "Source:")${CL} ${BL}https://pve.proxmox.com/wiki/Upgrade_from_8_to_9${CL}"
echo -e
echo -e
echo -e "${TAB}${BOLD}$(translate "IMPORTANT PREREQUISITES:")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "System must be updated to latest PVE 8.4+ before starting")${CL}"
echo -e "${TAB}${BGN}$(translate "Use SSH or terminal access (SSH recommended)")${CL}"
echo -e "${TAB}${BGN}$(translate "Use tmux or screen to avoid interruptions")${CL}"
echo -e "${TAB}${BGN}$(translate "Have valid backups of all VMs and containers")${CL}"
echo -e "${TAB}${BGN}$(translate "At least 5GB free space on root filesystem")${CL}"
echo -e "${TAB}${BGN}$(translate "Do not run the upgrade from the Web UI virtual console (it will disconnect)")${CL}"
echo -e
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
show_command "1" \
"$(translate "Update system to latest PVE 8.4+ (if not done already):")\n\n" \
"apt update && apt dist-upgrade -y" \
"$(translate "Or use ProxMenux update function")" \
"\n\n"
show_command "2" \
"$(translate "Verify PVE version (must be 8.4.1 or newer):")\n\n" \
"pveversion" \
"" \
"\n"
show_command "2.1" \
"${YW}$(translate "If this node runs hyper-converged Ceph: ensure Ceph is 19.x (Squid) BEFORE upgrading PVE.")${CL}\n\n" \
"ceph --version" \
"$(translate "If not 19.x, upgrade Ceph (Reef→Squid) first per the official guide:") ${BL}https://pve.proxmox.com/wiki/Ceph_Squid${CL}" \
"\n"
show_command "3" \
"$(translate "Run upgrade checklist script:")\n\n" \
"pve8to9 --full" \
"${YW}$(translate "If it warns about 'systemd-boot' meta-package, remove it:")${CL} apt remove systemd-boot" \
"\n"
show_command "4" \
"$(translate "Start terminal multiplexer (recommended):")\n\n" \
"tmux new-session -s upgrade ${DARK_GRAY}$(translate "# Recommended: avoids disconnection during upgrade")${CL}\n\n screen -S upgrade ${DARK_GRAY}$(translate "# Alternative if you prefer screen")${CL}" \
"" \
"\n"
show_command "5" \
"$(translate "Update Debian repositories to Trixie:")\n\n" \
"sed -i 's/bookworm/trixie/g' /etc/apt/sources.list" \
"" \
"\n"
show_command "6" \
"${YW}$(translate "Update PVE enterprise repository (Only if using enterprise):")${CL}\n\n" \
"${CUS}sed -i 's/bookworm/trixie/g' /etc/apt/sources.list.d/pve-enterprise.list${CL}" \
"$(translate "Skip this step if using no-subscription repository")" \
"\n\n"
show_command "7" \
"${YW}$(translate "Add new PVE 9 enterprise repository (deb822 format) (Only if using enterprise):")${CL}\n\n" \
"${CUS}cat > /etc/apt/sources.list.d/pve-enterprise.sources << EOF
Types: deb
URIs: https://enterprise.proxmox.com/debian/pve
Suites: trixie
Components: pve-enterprise
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF${CL}" \
"$(translate "Only if using enterprise subscription")" \
"\n\n"
show_command "8" \
"$(translate "OR add new PVE 9 no-subscription repository:")\n\n" \
"cat > /etc/apt/sources.list.d/proxmox.sources << EOF
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF" \
"$(translate "Only if using no-subscription repository")" \
"\n\n"
show_command "8.1" \
"$(translate "Refresh APT index and verify repositories:")\n\n" \
"apt update && apt policy | sed -n '1,120p'" \
"$(translate "Ensure there are no errors and that proxmox-ve candidate shows 9.x")" \
"\n"
show_command "9" \
"${YW}$(translate "Update Ceph repository (Only if using Ceph):")${CL}\n\n" \
"${CUS}cat > /etc/apt/sources.list.d/ceph.sources << EOF
Types: deb
URIs: http://download.proxmox.com/debian/ceph-squid
Suites: trixie
Components: no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF${CL}" \
"$(translate "Use enterprise URL if you have subscription.")" \
"\n\n"
show_command "10" \
"$(translate "Remove old repository files:")\n\n" \
"rm -f /etc/apt/sources.list.d/pve-enterprise.list /etc/apt/sources.list.d/ceph.list" \
"$(translate "Also comment any remaining 'bookworm' entries in *.list if present.")" \
"\n"
show_command "11" \
"$(translate "Update package index:")\n\n" \
"apt update" \
"" \
"\n"
show_command "12" \
"$(translate "Disable kernel audit messages (optional but recommended):")\n\n" \
"systemctl disable --now systemd-journald-audit.socket" \
"" \
"\n"
show_command "13" \
"$(translate "Start the main system upgrade:")\n\n" \
"apt dist-upgrade" \
"$(translate "This will take time. Answer prompts carefully - see notes below.")\n" \
"\n"
echo -e "${TAB}${BOLD}$(translate "UPGRADE PROMPTS - RECOMMENDED ANSWERS:")${CL}"
echo -e
echo -e "${TAB}${BGN}/etc/issue:${CL} ${YW}$(translate "Keep current version (N)")${CL}"
echo -e "${TAB}${BGN}/etc/lvm/lvm.conf:${CL} ${YW}$(translate "Install maintainer's version (Y)")${CL}"
echo -e "${TAB}${BGN}/etc/ssh/sshd_config:${CL} ${YW}$(translate "Install maintainer's version (Y)")${CL}"
echo -e "${TAB}${BGN}/etc/default/grub:${CL} ${YW}$(translate "Keep current version (N) if modified")${CL}"
echo -e "${TAB}${BGN}/etc/chrony/chrony.conf:${CL} ${YW}$(translate "Install maintainer's version (Y)")${CL}"
echo -e "${TAB}${BGN}$(translate "Service restarts:")${CL} ${YW}$(translate "Use default (Yes)")${CL}"
echo -e "${TAB}${BGN}apt-listchanges:${CL} ${YW}$(translate "Press 'q' to exit")${CL}"
echo -e
echo -e
echo -e
show_command "13.1" \
"${YW}$(translate "If booting in EFI mode with root on LVM: install GRUB for EFI")${CL}\n\n" \
"[ -d /sys/firmware/efi ] && apt install grub-efi-amd64" \
"$(translate "Per official known issues; ensures proper boot after upgrade")" \
"\n"
show_command "14" \
"$(translate "Run checklist again to verify upgrade:")\n\n" \
"pve8to9 --full" \
"$(translate "Should show fewer or no issues")" \
"\n"
show_command "15" \
"$(translate "Reboot the system:")\n\n" \
"reboot" \
"" \
"\n"
show_command "16" \
"$(translate "After reboot, verify PVE version:")\n\n" \
"pveversion" \
"$(translate "Should show pve-manager/9.x.x")" \
"\n"
show_command "17" \
"$(translate "Optional: Modernize repository sources:")\n\n" \
"apt modernize-sources" \
"$(translate "Converts to deb822; keeps .list backups as .bak")" \
"\n"
echo -e "${TAB}${BL}------------------------------------------------------------------------${CL}"
echo -e
echo -e
echo -e "${TAB}${BOLD}$(translate "CLUSTER UPGRADE NOTES:")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "Upgrade one node at a time")${CL}"
echo -e "${TAB}${BGN}$(translate "Migrate VMs away from node being upgraded")${CL}"
echo -e "${TAB}${BGN}$(translate "Wait for each node to complete before starting next")${CL}"
echo -e "${TAB}${BGN}$(translate "HA groups will be migrated to HA rules automatically")${CL}"
echo -e
echo -e
echo -e
echo -e "${TAB}${BOLD}$(translate "TROUBLESHOOTING:")${CL}"
echo -e
echo -e "${TAB}${BGN}$(translate "If GUI does not load:")${CL} ${YW}Check with 'systemctl status pveproxy' and restart with 'systemctl restart pveproxy'${CL}"
echo -e "${TAB}${BGN}$(translate "If ZFS errors occur:")${CL} ${YW}Ensure the 'zfsutils-linux' package is up to date${CL}"
echo -e "${TAB}${BGN}$(translate "If network does not work:")${CL} ${YW}Check /etc/network/interfaces and ensure 'ifupdown2' is installed${CL}"
echo -e "${TAB}${BGN}$(translate "If upgrade fails:")${CL} ${YW}apt -f install${CL}"
echo -e "${TAB}${BGN}$(translate "If repositories error:")${CL} ${YW}Check /etc/apt/sources.list*${CL}"
echo -e "${TAB}${BGN}$(translate "If 'proxmox-ve' removal warning:")${CL} ${YW}Fix repository configuration (ensure PVE 9 repo active)${CL}"
echo -e "${TAB}${BGN}$(translate "Emergency recovery:")${CL} ${YW}Boot from rescue system${CL}"
echo -e
echo -e
echo -e
msg_success "$(translate "Press Enter to return to menu...")"
echo -e
read -r
clear
exit 0
#bash <(curl -fsSL "$REPO_URL/scripts/utilities/upgrade_pve8_to_pve9.sh")
}
# Main execution
show_proxmox_upgrade_manual_guide

View File

@@ -0,0 +1,180 @@
#!/bin/bash
# ==========================================================
# ProxMenuX - Upgrade PVE 8 → 9 (Simplified, per official guide)
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.0
# Last Updated: 14/08/2025
# ==========================================================
REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
LOG="/var/log/pve8-a-pve9-$(date +%Y%m%d-%H%M%S).log"
: > "$LOG"
disable_translation_post_upgrade() {
translate() { echo "$1"; }
}
disable_translation_post_upgrade
if [[ "$pve_version" -ge 9 ]]; then
disable_translation_post_upgrade
fi
run_pve8to9_check2() {
local tmp
tmp="$(mktemp)"
echo -e
set -o pipefail
pve8to9 --full 2>&1 | tee -a "$LOG" | tee "$tmp"
local rc=${PIPESTATUS[0]}
local fails warns
fails=$(grep -c 'FAIL:' "$tmp" || true)
warns=$(grep -c 'WARN:' "$tmp" || true)
if (( fails > 0 )); then
echo -e
echo -e "${BFR}${RD}[ERROR] $(translate "Pre-check found") $fails $(translate "blocking issue(s).")\n$(translate "Please resolve the problem(s) as described above, then re-run the upgrade script.")${CL}"
echo -e
local repair_commands=()
local repair_descriptions=()
# Error 1: systemd-boot meta-package
if grep -q 'systemd-boot meta-package installed' "$tmp"; then
repair_commands+=("apt install systemd-boot-efi systemd-boot-tools -y && apt remove systemd-boot -y")
repair_descriptions+=("$(translate "Fix systemd-boot meta-package conflict")")
echo -e "${YW}$(translate "Fix systemd-boot:") ${CL}apt install systemd-boot-efi systemd-boot-tools -y && apt remove systemd-boot -y"
fi
# Error 2: Ceph version incompatible
if grep -q -E '(ceph.*version|ceph.*incompatible)' "$tmp"; then
repair_commands+=("ceph versions && pveceph upgrade")
repair_descriptions+=("$(translate "Upgrade Ceph to compatible version")")
echo -e "${YW}$(translate "Fix Ceph version:") ${CL}ceph versions && pveceph upgrade"
fi
# Error 3: Repository configuration issues
if grep -q -E '(repository.*issue|repo.*problem|sources.*error)' "$tmp"; then
repair_commands+=("cleanup_duplicate_repos && configure_repositories")
repair_descriptions+=("$(translate "Fix repository configuration")")
echo -e "${YW}$(translate "Fix repositories:") ${CL}cleanup_duplicate_repos && configure_repositories"
fi
# Error 4: Package conflicts
if grep -q -E '(package.*conflict|dependency.*problem)' "$tmp"; then
repair_commands+=("apt update && apt autoremove -y && apt autoclean")
repair_descriptions+=("$(translate "Resolve package conflicts")")
echo -e "${YW}$(translate "Fix package conflicts:") ${CL}apt update && apt autoremove -y && apt autoclean"
fi
# Error 5: Disk space issues
if grep -q -E '(disk.*space|storage.*full|no.*space)' "$tmp"; then
repair_commands+=("apt clean && apt autoremove -y && journalctl --vacuum-time=7d")
repair_descriptions+=("$(translate "Free up disk space")")
echo -e "${YW}$(translate "Fix disk space:") ${CL}apt clean && apt autoremove -y && journalctl --vacuum-time=7d"
fi
# Error 6: Network/DNS issues
if grep -q -E '(network.*error|dns.*problem|connection.*failed)' "$tmp"; then
repair_commands+=("systemctl restart networking && systemctl restart systemd-resolved")
repair_descriptions+=("$(translate "Fix network connectivity")")
echo -e "${YW}$(translate "Fix network:") ${CL}systemctl restart networking && systemctl restart systemd-resolved"
fi
echo -e
if [[ ${#repair_commands[@]} -gt 0 ]]; then
echo -e "${BFR}${CY}$(translate "Repair Options:")${CL}"
echo -e "${TAB}${GN}1.${CL} $(translate "Try automatic repair of detected issues")"
echo -e "${TAB}${GN}2.${CL} $(translate "Show manual repair commands")"
echo -e
echo -n "$(translate "Select option [1-2] (default: 2): ")"
read -r repair_choice
case "$repair_choice" in
1)
echo -e
msg_info2 "$(translate "Attempting automatic repair...")"
local repair_success=0
for i in "${!repair_commands[@]}"; do
echo -e "${TAB}${YW}$(translate "Executing:") ${repair_descriptions[$i]}${CL}"
if eval "${repair_commands[$i]}"; then
msg_ok "${repair_descriptions[$i]} - $(translate "Success")"
else
msg_error "${repair_descriptions[$i]} - $(translate "Failed")"
repair_success=1
fi
done
if [[ $repair_success -eq 0 ]]; then
echo -e
msg_info2 "$(translate "Re-running pre-check after repairs...")"
sleep 2
run_pve8to9_check
return $?
else
echo -e
msg_error "$(translate "Some repairs failed. Please fix manually and re-run the script.")"
fi
;;
2)
echo -e
echo -e "$(translate "${BFR}${CY}Manual Repair Commands:${CL}")"
for i in "${!repair_commands[@]}"; do
echo -e "${TAB}${BL}# ${repair_descriptions[$i]}${CL}"
echo -e
echo -e "${TAB}${repair_commands[$i]}"
echo -e
done
echo -e
msg_info2 "$(translate "Once finished, re-run the script 'PVE 8 to 9 check' to verify that all issues.")"
echo -e
msg_success "$(translate "Press Enter to exit the script after reading instructions...")"
read -r
rm -f "$tmp"
exit 1
;;
esac
fi
msg_success "$(translate "Press Enter to continue")"
read -r
fi
echo -e
msg_ok "$(translate "Checklist post-upgrade finished. Warnings:") $warns"
echo -e
msg_success "$(translate "Press Enter to continue")"
read -r
rm -f "$tmp"
return $rc
}
show_proxmenux_logo
msg_title "$(translate "Run PVE 8 to 9 check")"
run_pve8to9_check2

View File

@@ -56,7 +56,7 @@ install_system_utils() {
}
ensure_repositories() {
ensure_repositories_() {
local sources_file="/etc/apt/sources.list"
local need_update=false
@@ -85,6 +85,98 @@ EOF
return 0
}
ensure_repositories() {
local pve_version need_update=false
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
if [[ -z "$pve_version" ]]; then
msg_error "Unable to detect Proxmox version."
return 1
fi
if (( pve_version >= 9 )); then
# ===== PVE 9 (Debian 13 - trixie) =====
# proxmox.sources (no-subscription) ─ create if missing
if [[ ! -f /etc/apt/sources.list.d/proxmox.sources ]]; then
cat > /etc/apt/sources.list.d/proxmox.sources <<'EOF'
Enabled: true
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
need_update=true
fi
# debian.sources ─ create if missing
if [[ ! -f /etc/apt/sources.list.d/debian.sources ]]; then
cat > /etc/apt/sources.list.d/debian.sources <<'EOF'
Types: deb
URIs: http://deb.debian.org/debian/
Suites: trixie trixie-updates
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
Types: deb
URIs: http://security.debian.org/debian-security/
Suites: trixie-security
Components: main contrib non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
EOF
need_update=true
fi
# apt-get update only if needed or lists are empty
if [[ "$need_update" == true ]] || [[ ! -d /var/lib/apt/lists || -z "$(ls -A /var/lib/apt/lists 2>/dev/null)" ]]; then
msg_info "$(translate "Updating APT package lists...")"
apt-get update >/dev/null 2>&1 || apt-get update
fi
else
# ===== PVE 8 (Debian 12 - bookworm) =====
local sources_file="/etc/apt/sources.list"
# Debian base (create or append minimal lines if missing)
if ! grep -qE 'deb .* bookworm .* main' "$sources_file" 2>/dev/null; then
{
echo "deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware"
echo "deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware"
echo "deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware"
} >> "$sources_file"
need_update=true
fi
# Proxmox no-subscription list (classic) if missing
if [[ ! -f /etc/apt/sources.list.d/pve-no-subscription.list ]]; then
echo "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" \
> /etc/apt/sources.list.d/pve-no-subscription.list
need_update=true
fi
fi
# apt-get update only if needed or lists are empty
if [[ "$need_update" == true ]] || [[ ! -d /var/lib/apt/lists || -z "$(ls -A /var/lib/apt/lists 2>/dev/null)" ]]; then
msg_info "$(translate "Updating APT package lists...")"
apt-get update >/dev/null 2>&1 || apt-get update
fi
return 0
}
install_single_package() {
local package="$1"
local command_name="${2:-$package}"
@@ -143,10 +235,12 @@ EOF
"btop" "$(translate "Modern resource monitor")" "OFF"
"iftop" "$(translate "Real-time network usage")" "OFF"
"iotop" "$(translate "Monitor disk I/O usage")" "OFF"
"iperf3" "$(translate "Network performance testing")" "OFF"
#"iperf3" "$(translate "Network performance testing")" "OFF"
"intel-gpu-tools" "$(translate "tools for the Intel graphics driver")" "OFF"
"s-tui" "$(translate "Stress-Terminal UI")" "OFF"
"ipset" "$(translate "Manage IP sets")" "OFF"
"iptraf-ng" "$(translate "Network monitoring tool")" "OFF"
"mlocate" "$(translate "Locate files quickly")" "OFF"
"plocate" "$(translate "Locate files quickly")" "OFF"
"msr-tools" "$(translate "Access CPU MSRs")" "OFF"
"net-tools" "$(translate "Legacy networking tools")" "OFF"
"sshpass" "$(translate "Non-interactive SSH login")" "OFF"
@@ -190,7 +284,7 @@ EOF
local warning=0
declare -A package_to_command=(
["mlocate"]="locate"
["plocate"]="locate"
["msr-tools"]="rdmsr"
["net-tools"]="netstat"
["libguestfs-tools"]="virt-filesystems"
@@ -302,10 +396,12 @@ EOF
"btop:Modern resource monitor"
"iftop:Real-time network usage"
"iotop:Monitor disk I/O usage"
"iperf3:Network performance testing"
#"iperf3:Network performance testing"
"intel-gpu-tools:tools for the Intel graphics driver"
"s-tui:Stress-Terminal UI"
"ipset:Manage IP sets"
"iptraf-ng:Network monitoring tool"
"locate:Locate files quickly"
"plocate:Locate files quickly"
"rdmsr:Access CPU MSRs"
"netstat:Legacy networking tools"
"sshpass:Non-interactive SSH login"
@@ -361,10 +457,12 @@ EOF
"btop:btop:Modern resource monitor"
"iftop:iftop:Real-time network usage"
"iotop:iotop:Monitor disk I/O usage"
"iperf3:iperf3:Network performance testing"
#"iperf3:iperf3:Network performance testing"
"intel-gpu-tools:tools for the Intel graphics driver"
"s-tui:Stress-Terminal UI"
"ipset:ipset:Manage IP sets"
"iptraf-ng:iptraf-ng:Network monitoring tool"
"mlocate:locate:Locate files quickly"
"plocate:locate:Locate files quickly"
"msr-tools:rdmsr:Access CPU MSRs"
"net-tools:netstat:Legacy networking tools"
"sshpass:sshpass:Non-interactive SSH login"

File diff suppressed because it is too large Load Diff

View File

@@ -52,8 +52,7 @@ NEON_PURPLE_BLUE="\033[38;5;99m"
WHITE="\033[38;5;15m"
RESET="\033[0m"
DARK_GRAY="\033[38;5;244m"
DARK_GRAY="\033[38;5;244m"
DARK_GRAY="\033[38;5;244m"
ORANGE="\033[38;5;208m"
YW="\033[33m"
YWB="\033[1;33m"
GN="\033[1;92m"
@@ -137,6 +136,12 @@ msg_info2() {
echo -e "${TAB}${BOLD}${YW}${HOLD}${msg}${CL}"
}
# Display info message with spinner
msg_info3() {
local msg="$1"
echo -ne "${TAB}${YW}${HOLD}${msg}${CL}"
}
# Display success message
msg_success() {
if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID > /dev/null; then
@@ -165,7 +170,7 @@ msg_warn() {
fi
printf "\e[?25h"
local msg="$1"
echo -e "${BFR}${TAB}${NV}${CL} ${YWB}${msg}${CL}"
echo -e "${BFR}${TAB}${CL} ${YWB}${msg}${CL}"
}
@@ -179,6 +184,12 @@ msg_ok() {
echo -e "${BFR}${TAB}${CM}${GN}${msg}${CL}"
}
msg_ok2() {
printf "\e[?25h"
local msg="$1"
echo -e "${BFR}${TAB}${CM}${GN}${msg}${CL}"
}
# Display error message
msg_error() {

View File

@@ -109,8 +109,10 @@ function select_linux_iso_official() {
"Ubuntu 24.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/24.04/ubuntu-24.04.2-live-server-amd64.iso"
"Ubuntu 22.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso"
"Ubuntu 20.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/20.04/ubuntu-20.04.6-live-server-amd64.iso"
"Debian 13|Desktop|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-13.0.0-amd64-DVD-1.iso"
"Debian 12|Desktop|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-12.10.0-amd64-DVD-1.iso"
"Debian 11|Desktop|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-dvd/debian-11.11.0-amd64-DVD-1.iso"
"Debian 13 Netinst|CLI|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-13.0.0-amd64-netinst.iso"
"Debian 12 Netinst|CLI|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.10.0-amd64-netinst.iso"
"Debian 11 Netinst|CLI|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-cd/debian-11.11.0-amd64-netinst.iso"
"Fedora Workstation 42|Desktop|ProxMenux|https://download.fedoraproject.org/pub/fedora/linux/releases/42/Workstation/x86_64/iso/Fedora-Workstation-Live-42-1.1.x86_64.iso"
@@ -165,12 +167,13 @@ function select_linux_iso_official() {
function select_linux_cloudinit() {
local CLOUDINIT_OPTIONS=(
"1" "Arch Linux (Cloud-Init automated) │ Helper Scripts"
"2" "Debian 12 (Cloud-Init automated) │ Helper Scripts"
"3" "Ubuntu 22.04 (Cloud-Init automated) │ Helper Scripts"
"4" "Ubuntu 24.04 (Cloud-Init automated) │ Helper Scripts"
"5" "Ubuntu 24.10 (Cloud-Init automated) │ Helper Scripts"
"6" "Ubuntu 25.04 (Cloud-Init automated) │ Helper Scripts"
"7" "$(translate "Return to Main Menu")"
"2" "Debian 13 (Cloud-Init automated) │ Helper Scripts"
"3" "Debian 12 (Cloud-Init automated) │ Helper Scripts"
"4" "Ubuntu 22.04 (Cloud-Init automated) │ Helper Scripts"
"5" "Ubuntu 24.04 (Cloud-Init automated) │ Helper Scripts"
"6" "Ubuntu 24.10 (Cloud-Init automated) │ Helper Scripts"
"7" "Ubuntu 25.04 (Cloud-Init automated) │ Helper Scripts"
"8" "$(translate "Return to Main Menu")"
)
local script_selection
@@ -185,24 +188,27 @@ function select_linux_cloudinit() {
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/archlinux-vm.sh")
;;
2)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh")
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-13-vm.sh")
;;
3)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2204-vm.sh")
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/debian-vm.sh")
;;
4)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2404-vm.sh")
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2204-vm.sh")
;;
5)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2410-vm.sh")
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2404-vm.sh")
;;
6)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2410-vm.sh")
;;
7)
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2504-vm.sh")
echo -e
echo -e "after installation, checkout:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/272"
echo -e
;;
7)
8)
return
;;
esac

View File

@@ -46,7 +46,7 @@ function select_nas_iso() {
"4" "OpenMediaVault VM (Debian based)"
"5" "XigmaNAS VM (FreeBSD based)"
"6" "Rockstor VM (openSUSE based)"
"7" "ZimaOS VM (R0GGER proxmox-zimaos)"
"7" "ZimaOS VM (Proxmox-zimaos)"
"8" "Umbrel OS VM (Helper Scripts)"
"9" "$(translate "Return to Main Menu")"
)
@@ -54,7 +54,7 @@ function select_nas_iso() {
local NAS_TYPE
NAS_TYPE=$(dialog --backtitle "ProxMenux" \
--title "$(translate "NAS Systems")" \
--menu "\n$(translate "Select the NAS system to install:")" 18 70 10 \
--menu "\n$(translate "Select the NAS system to install:")" 20 70 10 \
"${NAS_OPTIONS[@]}" 3>&1 1>&2 2>&3)
@@ -103,18 +103,9 @@ function select_nas_iso() {
HN="Rockstor"
;;
7)
HN="ZimaOS-VM"
if ! confirm_vm_creation; then
return 1
fi
bash -c "$(wget -qLO - https://raw.githubusercontent.com/R0GGER/proxmox-zimaos/refs/heads/main/zimaos_zimacube.sh)"
echo -e
bash <(curl -s "$REPO_URL/scripts/vm/zimaos.sh")
msg_success "$(translate "Press Enter to return to menu...")"
read -r
whiptail --title "Proxmox VE - ZimaOS" \
--msgbox "$(translate "ZimaOS installer script by R0GGER\n\nVisit the GitHub repo to learn more, contribute, or support the project:\n\nhttps://github.com/R0GGER/proxmox-zimaos")" 15 70
return 1
;;
8)

1207
scripts/vm/zimaos.sh Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
1.1.4
1.1.5

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB