240 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
37 changed files with 13459 additions and 278 deletions

View File

@@ -1,3 +1,56 @@
## 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

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"
}

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"
},
{
@@ -1304,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"
},
{
@@ -1672,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",
@@ -1874,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",
@@ -2091,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"
},
@@ -2148,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",
@@ -2224,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"
},
@@ -2252,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",
@@ -2330,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",
@@ -2480,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",
@@ -2965,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
}
},
@@ -3406,14 +3491,10 @@
12
],
"notes": [
"Show Login Credentials, type `cat ~/paperless.creds` 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",
"default_credentials": {
"username": "admin",
"password": null
}
"type": "ct"
},
{
"name": "Part-DB",
@@ -3462,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",
@@ -3871,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": {
@@ -4122,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",
@@ -4161,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"
},
@@ -4203,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",
@@ -4416,7 +4553,7 @@
"script": "ct/swizzin.sh",
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/swizzin.sh",
"categories": [
15
13
],
"notes": [
"Installation might take a long time if choosing to install many apps. Be patient.",
@@ -4595,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",

File diff suppressed because it is too large Load Diff

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

@@ -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

@@ -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

@@ -232,6 +232,10 @@ convert_direct_method() {
fi
msg_ok "$(translate 'Direct conversion completed for container') $CONTAINER_ID"
echo -e
msg_success "Press Enter to continue..."
read -r
}
cleanup_and_finalize() {
@@ -258,7 +262,7 @@ main() {
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 continue")"
msg_success "$(translate "Press Enter to return to menu...")"
read -r
exit 0
}

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

View File

@@ -35,37 +35,33 @@ show_main_menu() {
"1" "$(translate 'Convert Privileged to Unprivileged')" \
"2" "$(translate 'Convert Unprivileged to Privileged')" \
"3" "$(translate 'Show Container Privilege Status')" \
"4" "$(translate 'Exit')" 3>&1 1>&2 2>&3)
"4" "$(translate "Help & Info (commands)")" \
"5" "$(translate 'Exit')" 3>&1 1>&2 2>&3)
case $CHOICE in
1)
bash <(curl -fsSL "$REPO_URL/scripts/lcx/lxc-privileged-to-unprivileged.sh")
bash <(curl -s "$REPO_URL/scripts/lxc/lxc-privileged-to-unprivileged.sh")
;;
2)
convert_unprivileged_to_privileged
bash <(curl -s "$REPO_URL/scripts/lxc/lxc-unprivileged-to-privileged.sh")
;;
3)
show_container_status
;;
4)
exit 0
bash <(curl -s "$REPO_URL/scripts/lxc/lxc-conversion-manual-guide.sh")
;;
5)
exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh")
;;
*)
exit 0
;;
exec bash <(curl -s "$REPO_URL/scripts/menus/main_menu.sh")
esac
}
convert_unprivileged_to_privileged() {
bash <(curl -fsSL "$REPO_URL/scripts/lcx/lxc-privileged-to-unprivileged.sh")
}
show_container_status() {
show_proxmenux_logo
msg_info "$(translate 'Gathering container privilege information...')"
@@ -75,6 +71,7 @@ show_container_status() {
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')"
@@ -87,17 +84,21 @@ show_container_status() {
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 --backtitle "ProxMenux" --title "$(translate 'Container Status')" --textbox "$TEMP_FILE" 25 80
dialog --title "$(translate 'Container Status')" --textbox "$TEMP_FILE" 25 80
rm -f "$TEMP_FILE"
show_main_menu
show_main_menu
}
show_main_menu
show_main_menu

View File

@@ -122,7 +122,7 @@ show_menu() {
case $OPTION in
1) exec bash <(curl -s "$REPO_URL/scripts/menus/menu_post_install.sh") ;;
2) exec bash <(curl -s "$REPO_URL/scripts/help_info_menu.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") ;;

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

@@ -479,47 +479,38 @@ apply_network_optimizations() {
# Core buffers & queues
net.core.netdev_max_backlog = 8192
net.core.optmem_max = 8192
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.somaxconn = 8151
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.somaxconn = 8192
# IPv4 security hardening
net.ipv4.conf.all.accept_redirects = 0
# 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.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_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.secure_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.default.log_martians = 1
# ICMP handling
net.ipv4.icmp_echo_ignore_broadcasts = 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
# TCP/IP tuning
# TCP/IP
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_base_mss = 1024
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_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_wmem = 8192 65536 16777216
net.ipv4.tcp_mtu_probing = 1
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
@@ -655,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
@@ -665,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
@@ -700,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
@@ -846,6 +843,7 @@ run_complete_optimization() {
ensure_tools_json
apt_upgrade
cleanup
remove_subscription_banner
#configure_time_sync
skip_apt_languages
@@ -857,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

View File

@@ -605,7 +605,7 @@ install_system_utils_() {
command -v "$1" >/dev/null 2>&1
}
ensure_repositories() {
ensure_repositories_() {
local sources_file="/etc/apt/sources.list"
local need_update=false
@@ -635,6 +635,106 @@ EOF
}
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}"
@@ -1225,47 +1325,36 @@ apply_network_optimizations() {
# Core buffers & queues
net.core.netdev_max_backlog = 8192
net.core.optmem_max = 8192
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.somaxconn = 8151
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.somaxconn = 8192
# IPv4 security hardening
net.ipv4.conf.all.accept_redirects = 0
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.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_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.secure_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.default.log_martians = 1
# ICMP handling
net.ipv4.icmp_echo_ignore_broadcasts = 1
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
# TCP/IP tuning
# TCP/IP
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_base_mss = 1024
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_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_wmem = 8192 65536 16777216
net.ipv4.tcp_mtu_probing = 1
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
@@ -1817,7 +1906,7 @@ EOF
install_fail2ban() {
install_fail2ban_() {
msg_info2 "$(translate "Installing and configuring Fail2Ban to protect the web interface...")"
@@ -1965,6 +2054,127 @@ EOF
install_fail2ban() {
msg_info2 "$(translate "Installing and configuring Fail2Ban to protect Proxmox web interface and SSH...")"
local deb_codename
deb_codename=$(grep -oP '^VERSION_CODENAME=\K.*' /etc/os-release 2>/dev/null)
if ! grep -RqsE "debian.*(bookworm|trixie)" /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null; then
msg_warn "$(translate "Debian repositories missing; creating default source file")"
local src="/etc/apt/sources.list.d/debian.sources"
cat > "$src" <<EOF
Types: deb
URIs: http://deb.debian.org/debian
Suites: ${deb_codename} ${deb_codename}-updates
Components: main contrib non-free non-free-firmware
Types: deb
URIs: http://security.debian.org/debian-security
Suites: ${deb_codename}-security
Components: main contrib non-free non-free-firmware
EOF
msg_ok "$(translate "Debian repositories configured for ${deb_codename}")"
fi
msg_info "$(translate "Installing Fail2Ban...")"
if ! DEBIAN_FRONTEND=noninteractive apt-get update -y >/dev/null 2>&1 || \
! DEBIAN_FRONTEND=noninteractive apt-get install -y fail2ban >/dev/null 2>&1; then
msg_error "$(translate "Failed to install Fail2Ban")"
return 1
fi
msg_ok "$(translate "Fail2Ban installed successfully")"
mkdir -p /etc/fail2ban/filter.d /etc/fail2ban/jail.d
msg_info "$(translate "Configuring Proxmox filter...")"
cat > /etc/fail2ban/filter.d/proxmox.conf <<'EOF'
[Definition]
failregex = pvedaemon\[.*authentication failure; rhost=<HOST> user=.* msg=.*
ignoreregex =
EOF
msg_ok "$(translate "Proxmox filter configured")"
msg_info "$(translate "Configuring Proxmox jail...")"
cat > /etc/fail2ban/jail.d/proxmox.conf <<'EOF'
[proxmox]
enabled = true
port = 8006
filter = proxmox
logpath = /var/log/daemon.log
maxretry = 3
bantime = 3600
findtime = 600
EOF
msg_ok "$(translate "Proxmox jail configured")"
msg_info "$(translate "Configuring global Fail2Ban settings and SSH jail...")"
cat > /etc/fail2ban/jail.local <<'EOF'
[DEFAULT]
ignoreip = 127.0.0.1
bantime = 86400
maxretry = 2
findtime = 1800
backend = systemd
banaction = nftables
banaction_allports = nftables[type=allports]
[sshd]
enabled = true
filter = sshd
logpath = /var/log/auth.log
maxretry = 2
findtime = 3600
bantime = 32400
EOF
msg_ok "$(translate "Global settings and SSH jail configured")"
touch /var/log/auth.log /var/log/daemon.log
chown root:adm /var/log/auth.log /var/log/daemon.log 2>/dev/null || true
chmod 640 /var/log/auth.log /var/log/daemon.log 2>/dev/null || true
systemctl daemon-reload
systemctl enable --now fail2ban >/dev/null 2>&1
sleep 2
if systemctl is-active --quiet fail2ban; then
msg_ok "$(translate "Fail2Ban is running correctly")"
else
msg_error "$(translate "Fail2Ban is NOT running!")"
journalctl -u fail2ban --no-pager -n 20
fi
if [ -S /var/run/fail2ban/fail2ban.sock ]; then
msg_ok "$(translate "Fail2Ban socket exists!")"
else
msg_warn "$(translate "Warning: Fail2Ban socket does not exist!")"
fi
if fail2ban-client ping >/dev/null 2>&1; then
msg_ok "$(translate "fail2ban-client successfully communicated with the server")"
else
msg_error "$(translate "fail2ban-client could not communicate with the server")"
fi
msg_success "$(translate "Fail2Ban installation and configuration completed successfully!")"
}
# ==========================================================
@@ -3080,124 +3290,19 @@ add_repo_test() {
configure_figurine_() {
msg_info2 "$(translate "Installing and configuring Figurine...")"
# Variables
local version="1.3.0"
local file="figurine_linux_amd64_v${version}.tar.gz"
local url="https://github.com/arsham/figurine/releases/download/v${version}/${file}"
local temp_dir
temp_dir=$(mktemp -d)
local install_dir="/usr/local/bin"
local profile_script="/etc/profile.d/figurine.sh"
local bin_path="${install_dir}/figurine"
msg_info "$(translate "Downloading Figurine v${version}...")"
if command -v figurine &> /dev/null; then
rm -f "$bin_path" > /dev/null 2>&1
msg_ok "$(translate "Previous installation removed")"
fi
wget -qO "${temp_dir}/${file}" "$url" > /dev/null 2>&1
msg_ok "$(translate "Download completed")"
msg_info "$(translate "Extracting package...")"
tar -xf "${temp_dir}/${file}" -C "${temp_dir}" > /dev/null 2>&1
msg_ok "$(translate "Extraction successful")"
if [[ ! -f "${temp_dir}/deploy/figurine" ]]; then
msg_error "$(translate "Binary not found in extracted content.")"
return 1
fi
msg_info "$(translate "Installing binary to ${install_dir}...")"
mv "${temp_dir}/deploy/figurine" "$bin_path" > /dev/null 2>&1
chmod +x "$bin_path" > /dev/null 2>&1
msg_ok "$(translate "Binary installed")"
msg_info "$(translate "Creating figurine welcome message at ${profile_script}...")"
cat << 'EOF' > "$profile_script"
/usr/local/bin/figurine -f "3d.flf" $(hostname)
EOF
chmod +x "$profile_script" > /dev/null 2>&1
msg_ok "$(translate "Welcome message script created")"
local bashrc="$HOME/.bashrc"
if ! grep -q "alias aptup=" "$bashrc"; then
msg_info "$(translate "Adding useful aliases to ~/.bashrc...")"
cat <<EOF >> "$bashrc"
# ProxMenux Figurine aliases and tools
alias aptup='apt update && apt dist-upgrade'
alias lxcclean='curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean-lxcs.sh | bash'
alias lxcupdate='curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/update-lxcs.sh | bash'
alias kernelclean='curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/kernel-clean.sh | bash'
alias cpugov='curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/scaling-governor.sh | bash'
alias updatecerts='pvecm updatecerts'
alias seqwrite='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4M --size=32G --readwrite=write --ramp_time=4'
alias seqread='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4M --size=32G --readwrite=read --ramp_time=4'
alias ranwrite='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4k --size=4G --readwrite=randwrite --ramp_time=4'
alias ranread='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4k --size=4G --readwrite=randread --ramp_time=4'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias ls='ls --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
EOF
msg_ok "$(translate "Aliases added to .bashrc")"
sed -i '/\${marker_begin}/d;/\${marker_end}/d' .bashrc
else
msg_info "$(translate "Aliases already present. Skipping addition.")"
msg_ok "$(translate "Aliases added to .bashrc")"
fi
msg_info "$(translate "Cleaning up temporary files...")"
rm -rf "$temp_dir" > /dev/null 2>&1
msg_ok "$(translate "Cleanup completed")"
msg_success "$(translate "Figurine installation and configuration completed successfully.")"
}
configure_figurine() {
msg_info2 "$(translate "Installing and configuring Figurine...")"
local version="1.3.0"
local file="figurine_linux_amd64_v${version}.tar.gz"
local url="https://github.com/arsham/figurine/releases/download/v${version}/${file}"
local temp_dir
temp_dir=$(mktemp -d)
local temp_dir; temp_dir=$(mktemp -d)
local install_dir="/usr/local/bin"
local profile_script="/etc/profile.d/figurine.sh"
local bin_path="${install_dir}/figurine"
local bashrc="/root/.bashrc"
local marker_begin="# BEGIN PMX_FIGURINE"
local marker_end="# END PMX_FIGURINE"
cleanup_dir() { rm -rf "$temp_dir" 2>/dev/null || true; }
trap cleanup_dir EXIT
cleanup() {
rm -rf "$temp_dir" 2>/dev/null || true
}
trap cleanup EXIT
[[ -f "$bashrc" ]] || touch "$bashrc"
if command -v figurine &>/dev/null; then
msg_info "$(translate "Updating Figurine binary...")"
@@ -3205,13 +3310,12 @@ configure_figurine() {
msg_info "$(translate "Downloading Figurine v${version}...")"
fi
if ! wget -qO "${temp_dir}/${file}" "$url" > /dev/null 2>&1; then
if ! wget -qO "${temp_dir}/${file}" "$url"; then
msg_error "$(translate "Failed to download Figurine")"
return 1
fi
if ! tar -xf "${temp_dir}/${file}" -C "${temp_dir}" > /dev/null 2>&1; then
if ! tar -xf "${temp_dir}/${file}" -C "${temp_dir}"; then
msg_error "$(translate "Failed to extract package")"
return 1
fi
@@ -3223,39 +3327,54 @@ configure_figurine() {
fi
msg_info "$(translate "Installing binary to ${install_dir}...")"
install -m 0755 -o root -g root "${temp_dir}/deploy/figurine" "$bin_path"
install -m 0755 -o root -g root "${temp_dir}/deploy/figurine" "$bin_path" > /dev/null 2>&1
cat > "$profile_script" << 'EOF'
/usr/local/bin/figurine -f "3d.flf" $(hostname)
EOF
chmod +x "$profile_script" > /dev/null 2>&1
chmod +x "$profile_script"
ensure_aliases() {
local bashrc="/root/.bashrc"
[[ -f "$bashrc" ]] || touch "$bashrc"
if grep -q "^${marker_begin}$" "$bashrc" 2>/dev/null; then
sed -i "/^${marker_begin}$/,/^${marker_end}$/d" "$bashrc"
fi
local -a ALIASES=(
"aptup=apt update && apt dist-upgrade"
"lxcclean=curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean-lxcs.sh | bash"
"lxcupdate=curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/update-lxcs.sh | bash"
"kernelclean=curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/kernel-clean.sh | bash"
"cpugov=curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/scaling-governor.sh | bash"
"lxctrim=curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/fstrim.sh | bash"
"updatecerts=pvecm updatecerts"
"seqwrite=sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4M --size=32G --readwrite=write --ramp_time=4"
"seqread=sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4M --size=32G --readwrite=read --ramp_time=4"
"ranwrite=sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4k --size=4G --readwrite=randwrite --ramp_time=4"
"ranread=sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4k --size=4G --readwrite=randread --ramp_time=4"
)
for entry in "${ALIASES[@]}"; do
local name="${entry%%=*}"
local cmd="${entry#*=}"
local esc_cmd
esc_cmd=$(printf "%s" "$cmd" | sed -e 's/[\\/&]/\\&/g')
if grep -Eq "^alias[[:space:]]+$name=" "$bashrc"; then
if ! grep -Eq "^alias[[:space:]]+$name='${esc_cmd}'$" "$bashrc"; then
sed -i -E "s|^alias[[:space:]]+$name=.*$|alias $name='${esc_cmd}'|" "$bashrc"
fi
else
printf "alias %s='%s'\n" "$name" "$cmd" >> "$bashrc"
fi
done
cat >> "$bashrc" << 'EOF'
${marker_begin}
# ProxMenux Figurine aliases and tools
alias aptup='apt update && apt dist-upgrade'
alias lxcclean='curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean-lxcs.sh | bash'
alias lxcupdate='curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/update-lxcs.sh | bash'
alias kernelclean='curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/kernel-clean.sh | bash'
alias cpugov='curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/scaling-governor.sh | bash'
alias lxctrim='curl -fsSL -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/fstrim.sh | bash'
alias updatecerts='pvecm updatecerts'
alias seqwrite='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4M --size=32G --readwrite=write --ramp_time=4'
alias seqread='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4M --size=32G --readwrite=read --ramp_time=4'
alias ranwrite='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4k --size=4G --readwrite=randwrite --ramp_time=4'
alias ranread='sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4k --size=4G --readwrite=randread --ramp_time=4'
${marker_end}
EOF
awk '!seen[$0]++' "$bashrc" > "${bashrc}.tmp" && mv "${bashrc}.tmp" "$bashrc"
}
ensure_aliases
msg_ok "$(translate "Aliases added to .bashrc")"
sed -i '/\${marker_begin}/d;/\${marker_end}/d' .bashrc
msg_success "$(translate "Figurine installation and configuration completed successfully.")"
register_tool "figurine" true
@@ -3264,6 +3383,112 @@ EOF
configure_figurine() {
msg_info2 "$(translate "Installing and configuring Figurine...")"
local version="1.3.0"
local file="figurine_linux_amd64_v${version}.tar.gz"
local url="https://github.com/arsham/figurine/releases/download/v${version}/${file}"
local temp_dir; temp_dir=$(mktemp -d)
local install_dir="/usr/local/bin"
local profile_script="/etc/profile.d/figurine.sh"
local bin_path="${install_dir}/figurine"
local bashrc="/root/.bashrc"
cleanup_dir() { rm -rf "$temp_dir" 2>/dev/null || true; }
trap cleanup_dir EXIT
[[ -f "$bashrc" ]] || touch "$bashrc"
if command -v figurine &>/dev/null; then
msg_info "$(translate "Updating Figurine binary...")"
else
msg_info "$(translate "Downloading Figurine v${version}...")"
fi
if ! wget -qO "${temp_dir}/${file}" "$url"; then
msg_error "$(translate "Failed to download Figurine")"
return 1
fi
if ! tar -xf "${temp_dir}/${file}" -C "${temp_dir}"; then
msg_error "$(translate "Failed to extract package")"
return 1
fi
msg_ok "$(translate "Extraction successful")"
if [[ ! -f "${temp_dir}/deploy/figurine" ]]; then
msg_error "$(translate "Binary not found in extracted content.")"
return 1
fi
msg_info "$(translate "Installing binary to ${install_dir}...")"
install -m 0755 -o root -g root "${temp_dir}/deploy/figurine" "$bin_path"
cat > "$profile_script" << 'EOF'
/usr/local/bin/figurine -f "3d.flf" $(hostname)
EOF
chmod +x "$profile_script"
ensure_aliases() {
local bashrc="/root/.bashrc"
[[ -f "$bashrc" ]] || touch "$bashrc"
local -a ALIASES=(
"aptup=apt update && apt dist-upgrade"
"lxcclean=curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean-lxcs.sh | bash"
"lxcupdate=curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/update-lxcs.sh | bash"
"kernelclean=curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/kernel-clean.sh | bash"
"cpugov=curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/scaling-governor.sh | bash"
"lxctrim=curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/fstrim.sh | bash"
"updatecerts=pvecm updatecerts"
"seqwrite=sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4M --size=32G --readwrite=write --ramp_time=4"
"seqread=sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4M --size=32G --readwrite=read --ramp_time=4"
"ranwrite=sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4k --size=4G --readwrite=randwrite --ramp_time=4"
"ranread=sync; fio --randrepeat=1 --ioengine=libaio --direct=1 --name=test --filename=test --bs=4k --size=4G --readwrite=randread --ramp_time=4"
)
for entry in "${ALIASES[@]}"; do
local name="${entry%%=*}"
local cmd="${entry#*=}"
local esc_cmd
esc_cmd=$(printf "%s" "$cmd" | sed -e 's/[\\/&]/\\&/g')
if grep -Eq "^alias[[:space:]]+$name=" "$bashrc"; then
if ! grep -Eq "^alias[[:space:]]+$name='${esc_cmd}'$" "$bashrc"; then
sed -i -E "s|^alias[[:space:]]+$name=.*$|alias $name='${esc_cmd}'|" "$bashrc"
fi
else
printf "alias %s='%s'\n" "$name" "$cmd" >> "$bashrc"
fi
done
awk '!seen[$0]++' "$bashrc" > "${bashrc}.tmp" && mv "${bashrc}.tmp" "$bashrc"
}
ensure_aliases
msg_ok "$(translate "Aliases added to .bashrc")"
msg_success "$(translate "Figurine installation and configuration completed successfully.")"
register_tool "figurine" true
}
# ==========================================================

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

@@ -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}"

View File

@@ -72,7 +72,7 @@ ask_run_mode() {
msg_ok "$(translate "Run mode: Unattended")"
export DEBIAN_FRONTEND=noninteractive
export APT_LISTCHANGES_FRONTEND=none
return 0
exit 0
fi
@@ -617,13 +617,12 @@ run_pve8to9_check() {
# 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"
repair_commands+=("apt remove -y systemd-boot")
repair_descriptions+=("$(translate "Remove obsolete systemd-boot meta-package")")
echo -e "${YW}$(translate "Fix systemd-boot:") ${CL}apt remove -y systemd-boot"
fi
# Error 2: Kernel version mismatch
if grep -q -E '(kernel.*mismatch|kernel.*version)' "$tmp"; then
if grep -q -E 'FAIL:.*(kernel.*mismatch|kernel.*version.*mismatch)' "$tmp"; then
repair_commands+=("update-grub")
repair_descriptions+=("$(translate "Update kernel to compatible version")")
echo -e "${YW}$(translate "Fix kernel version:") ${CL}update-grub"
@@ -651,18 +650,12 @@ run_pve8to9_check() {
fi
# Error 6: Disk space issues
if grep -q -E '(disk.*space|storage.*full|no.*space)' "$tmp"; then
if grep -q -E 'FAIL:.*(disk space|no space left|storage.*full)' "$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 7: 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
@@ -990,47 +983,45 @@ run_pve8to9_check2() {
# 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"
repair_commands+=("apt remove -y systemd-boot")
repair_descriptions+=("$(translate "Remove obsolete systemd-boot meta-package")")
echo -e "${YW}$(translate "Fix systemd-boot:") ${CL}apt remove -y systemd-boot"
fi
# Error 2: Kernel version mismatch
if grep -q -E 'FAIL:.*(kernel.*mismatch|kernel.*version.*mismatch)' "$tmp"; then
repair_commands+=("update-grub")
repair_descriptions+=("$(translate "Update kernel to compatible version")")
echo -e "${YW}$(translate "Fix kernel version:") ${CL}update-grub"
fi
# Error 2: Ceph version incompatible
# Error 3: 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
# Error 4: 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
# Error 5: 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
# Error 6: Disk space issues
if grep -q -E 'FAIL:.*(disk space|no space left|storage.*full)' "$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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB