mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-10-02 16:16:19 +00:00
Compare commits
240 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5cb9e13ca7 | ||
|
0187010f94 | ||
|
2c2ed21e59 | ||
|
f8b2ccec40 | ||
|
e858dc582d | ||
|
dd737f4b46 | ||
|
f0bc238b6d | ||
|
af55424850 | ||
|
902534baff | ||
|
6daa630040 | ||
|
0b2b86673b | ||
|
6aa5b58208 | ||
|
4430201cd2 | ||
|
7c7963a83e | ||
|
e2202cd2d8 | ||
|
a931be83bc | ||
|
7350bea345 | ||
|
9b1e39dbb4 | ||
|
15cd118845 | ||
|
d58dff047c | ||
|
a2f83c896c | ||
|
6ef77c731c | ||
|
29b0f61958 | ||
|
e944b2ecdd | ||
|
41819c46a3 | ||
|
13f391a6f0 | ||
|
85a3d44f2c | ||
|
0792392058 | ||
|
ff5083ada0 | ||
|
62841677bc | ||
|
1761cf53a2 | ||
|
a771efc5fa | ||
|
ed049da76a | ||
|
5d1d357a2e | ||
|
30d0706a1c | ||
|
e9667e1266 | ||
|
73109483e7 | ||
|
a9c1acf204 | ||
|
81c4f5814c | ||
|
c595f6d781 | ||
|
24bb6b1d3d | ||
|
49eeb6020d | ||
|
7c272bd2a2 | ||
|
cfbd865937 | ||
|
fe472f33ef | ||
|
8d6b3d650f | ||
|
3b0d5b5eb7 | ||
|
875e8a99bd | ||
|
6c19d81844 | ||
|
ba535a931f | ||
|
45dca5218d | ||
|
da3cb9971b | ||
|
b39270dc1e | ||
|
ae8a7d0de9 | ||
|
2d501415bf | ||
|
da639ccaac | ||
|
a352770e2d | ||
|
e3e1899466 | ||
|
e67288e623 | ||
|
4019e49b07 | ||
|
cd8711f3bc | ||
|
0d119379de | ||
|
aa2b6ff112 | ||
|
3482f7dc98 | ||
|
16c321f114 | ||
|
a81e7f3c44 | ||
|
d7cc001521 | ||
|
eb11962231 | ||
|
9f73b8f159 | ||
|
873a4abe24 | ||
|
56bc584f5e | ||
|
2a9f2f3c2e | ||
|
ee719cdd39 | ||
|
a571b57b30 | ||
|
5ee7a23bea | ||
|
fe159ea195 | ||
|
8fcdf6176b | ||
|
715166bbca | ||
|
1d58072c70 | ||
|
d667cde699 | ||
|
4cd8889c38 | ||
|
93896f6fb7 | ||
|
3b3f0387bb | ||
|
2875c9af95 | ||
|
93ef1bfccc | ||
|
a886af1d87 | ||
|
d731ff3ae6 | ||
|
d44864637d | ||
|
674ee34ec6 | ||
|
a93eeda243 | ||
|
80fd92e2a1 | ||
|
d4ff2da473 | ||
|
9b7b271580 | ||
|
e1b340966a | ||
|
7ec4c331af | ||
|
3102d596ee | ||
|
af56dc546e | ||
|
15d47499fa | ||
|
53a34d0470 | ||
|
3ee675cefe | ||
|
d98c7bdc03 | ||
|
bb4f1ebed6 | ||
|
c8f73ea23b | ||
|
8292b12787 | ||
|
0f518e3c35 | ||
|
1c2f67d43d | ||
|
a5560a3123 | ||
|
1332096360 | ||
|
80381a6375 | ||
|
acf92bd005 | ||
|
da4f8a3a19 | ||
|
3a332192e3 | ||
|
1fdb1d87cc | ||
|
b99aa55d7a | ||
|
de20da2dad | ||
|
9444f0a68b | ||
|
48fd223a28 | ||
|
0845efe419 | ||
|
57b7ba91bc | ||
|
97af8a4892 | ||
|
d6f237e289 | ||
|
aba7109b35 | ||
|
d3ec71052e | ||
|
1be63f396b | ||
|
9308742146 | ||
|
b32241082d | ||
|
1f8504d685 | ||
|
97c5c48150 | ||
|
afe84dc46a | ||
|
ffafd42f03 | ||
|
7dca715c91 | ||
|
7695e1d8dd | ||
|
84b86d1db7 | ||
|
bae3ef6460 | ||
|
97c6ec8875 | ||
|
d33128dc26 | ||
|
10bdecabb6 | ||
|
de88f530c8 | ||
|
fb511b7596 | ||
|
322665ce91 | ||
|
baeca1fcfb | ||
|
095b98c36a | ||
|
29bb7e7608 | ||
|
e3d137efba | ||
|
207e915393 | ||
|
614e629a2b | ||
|
f35de5c749 | ||
|
c1623bd4df | ||
|
8690da5017 | ||
|
696adcdc24 | ||
|
2756bd06c1 | ||
|
4893f6ea00 | ||
|
35a7348197 | ||
|
cdd6333d0a | ||
|
54399b5b5d | ||
|
f6b192cc1e | ||
|
cd231b90d8 | ||
|
87fe788358 | ||
|
3e9bd21ea8 | ||
|
b6d4029797 | ||
|
ec65e96148 | ||
|
926f1f971f | ||
|
5d69fad73f | ||
|
a796761023 | ||
|
5d1338e485 | ||
|
ce25a167f1 | ||
|
1c44969580 | ||
|
b6e04e3ede | ||
|
84c26be703 | ||
|
d201160722 | ||
|
e112361b43 | ||
|
3e69795c9d | ||
|
b11baf2e5d | ||
|
233770b553 | ||
|
187db73798 | ||
|
0e3fc6f682 | ||
|
d11e3a4ac4 | ||
|
d3b4ca3e66 | ||
|
f37fbbfb8b | ||
|
52b7aac424 | ||
|
d42f3f8f0c | ||
|
91b5c7c9bc | ||
|
48feebc092 | ||
|
14e2d66d96 | ||
|
10d844a195 | ||
|
bbf91ae5d6 | ||
|
cb82eda49a | ||
|
bc1dbb1c27 | ||
|
9496a7f1ce | ||
|
7241fa31b4 | ||
|
fed7216436 | ||
|
ffe7d7c4c6 | ||
|
f430ac8d6c | ||
|
70dfd7c9a3 | ||
|
ed3140932b | ||
|
3cd2bd6ce8 | ||
|
982bf45fc4 | ||
|
aaba8569fc | ||
|
4111e15eb9 | ||
|
2012478f26 | ||
|
88869d3239 | ||
|
f3c2549b18 | ||
|
57e3b839d0 | ||
|
faf3f43413 | ||
|
52e5bb3386 | ||
|
89405f6670 | ||
|
73111c4139 | ||
|
04e9c5db8c | ||
|
69278902de | ||
|
efa95b0858 | ||
|
660128cd5c | ||
|
ef1e052e47 | ||
|
0b346bc343 | ||
|
2272eaf833 | ||
|
4adee98bce | ||
|
cbdb2c0705 | ||
|
4f438aabbf | ||
|
b6ccc06963 | ||
|
5b89a15bfc | ||
|
5596ae551d | ||
|
1360df592a | ||
|
13684ff83c | ||
|
ae88f7870e | ||
|
810b6da60c | ||
|
7bdf3e08f9 | ||
|
fdad2a087f | ||
|
c437a8c426 | ||
|
ef861e6d1d | ||
|
928a008688 | ||
|
638a124adb | ||
|
c2a63ae9bb | ||
|
28cf31e6e7 | ||
|
3cf416167d | ||
|
ebf03923a0 | ||
|
82797d2421 | ||
|
52b6be946c | ||
|
dc46724d7b | ||
|
ed7d43b6a9 | ||
|
6f3fc51278 | ||
|
a446acc282 |
53
CHANGELOG.md
53
CHANGELOG.md
@@ -1,3 +1,56 @@
|
|||||||
|
## 2025-01-10
|
||||||
|
|
||||||
|
### New version v1.1.6
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 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
|
## 2025-08-20
|
||||||
|
|
||||||
### New version v1.1.5
|
### New version v1.1.5
|
||||||
|
@@ -106,12 +106,12 @@ uninstall_proxmenu() {
|
|||||||
local force_clean="$2"
|
local force_clean="$2"
|
||||||
|
|
||||||
if [ "$force_clean" != "force" ]; then
|
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
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Uninstalling ProxMenu..."
|
echo "Uninstalling ProxMenux..."
|
||||||
|
|
||||||
if [ -f "$VENV_PATH/bin/activate" ]; then
|
if [ -f "$VENV_PATH/bin/activate" ]; then
|
||||||
echo "Removing googletrans and virtual environment..."
|
echo "Removing googletrans and virtual environment..."
|
||||||
@@ -151,7 +151,7 @@ uninstall_proxmenu() {
|
|||||||
sed -i '/This system is optimised by: ProxMenux/d' /etc/motd
|
sed -i '/This system is optimised by: ProxMenux/d' /etc/motd
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "ProxMenu has been uninstalled."
|
echo "ProxMenux has been uninstalled."
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ show_progress() {
|
|||||||
local total="$2"
|
local total="$2"
|
||||||
local message="$3"
|
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
|
echo
|
||||||
msg_info2 "$message"
|
msg_info2 "$message"
|
||||||
}
|
}
|
||||||
|
@@ -365,6 +365,22 @@
|
|||||||
"notes": [],
|
"notes": [],
|
||||||
"type": "ct"
|
"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",
|
"name": "Baby Buddy",
|
||||||
"slug": "babybuddy",
|
"slug": "babybuddy",
|
||||||
@@ -498,7 +514,9 @@
|
|||||||
"categories": [
|
"categories": [
|
||||||
13
|
13
|
||||||
],
|
],
|
||||||
"notes": [],
|
"notes": [
|
||||||
|
"Starting Booklore (Web UI) may take up to 2 minutes after a restart or fresh installation."
|
||||||
|
],
|
||||||
"type": "ct"
|
"type": "ct"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1304,7 +1322,9 @@
|
|||||||
"categories": [
|
"categories": [
|
||||||
14
|
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"
|
"type": "ct"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1672,6 +1692,21 @@
|
|||||||
],
|
],
|
||||||
"type": "ct"
|
"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",
|
"name": "Heimdall Dashboard",
|
||||||
"slug": "heimdall-dashboard",
|
"slug": "heimdall-dashboard",
|
||||||
@@ -1874,6 +1909,7 @@
|
|||||||
13
|
13
|
||||||
],
|
],
|
||||||
"notes": [
|
"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",
|
"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",
|
"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",
|
"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": [
|
"notes": [
|
||||||
"WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing.",
|
"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!",
|
"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"
|
"type": "ct"
|
||||||
},
|
},
|
||||||
@@ -2148,7 +2184,6 @@
|
|||||||
],
|
],
|
||||||
"notes": [
|
"notes": [
|
||||||
"First start can take a few minutes",
|
"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."
|
"When updating, if you had modified cache-ispn.xml: Re-apply your changes to the new file, otherwise leave it unchanged."
|
||||||
],
|
],
|
||||||
"type": "ct",
|
"type": "ct",
|
||||||
@@ -2224,7 +2259,7 @@
|
|||||||
3
|
3
|
||||||
],
|
],
|
||||||
"notes": [
|
"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"
|
"type": "ct"
|
||||||
},
|
},
|
||||||
@@ -2252,6 +2287,18 @@
|
|||||||
"notes": [],
|
"notes": [],
|
||||||
"type": "ct"
|
"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",
|
"name": "Librespeed Rust",
|
||||||
"slug": "librespeed-rust",
|
"slug": "librespeed-rust",
|
||||||
@@ -2330,6 +2377,24 @@
|
|||||||
"notes": [],
|
"notes": [],
|
||||||
"type": "ct"
|
"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",
|
"name": "lldap",
|
||||||
"slug": "lldap",
|
"slug": "lldap",
|
||||||
@@ -2480,6 +2545,26 @@
|
|||||||
"notes": [],
|
"notes": [],
|
||||||
"type": "ct"
|
"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",
|
"name": "MediaMTX",
|
||||||
"slug": "mediamtx",
|
"slug": "mediamtx",
|
||||||
@@ -2965,11 +3050,11 @@
|
|||||||
"notes": [
|
"notes": [
|
||||||
"This uses Docker under the hood, as this can not easily be installed bare-metal. ",
|
"This uses Docker under the hood, as this can not easily be installed bare-metal. ",
|
||||||
"The initial starting process can be take 1-2min. ",
|
"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",
|
"type": "ct",
|
||||||
"default_credentials": {
|
"default_credentials": {
|
||||||
"username": "root",
|
"username": "admin@example.org",
|
||||||
"password": null
|
"password": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -3406,14 +3491,10 @@
|
|||||||
12
|
12
|
||||||
],
|
],
|
||||||
"notes": [
|
"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`)."
|
"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",
|
"type": "ct"
|
||||||
"default_credentials": {
|
|
||||||
"username": "admin",
|
|
||||||
"password": null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Part-DB",
|
"name": "Part-DB",
|
||||||
@@ -3462,6 +3543,23 @@
|
|||||||
],
|
],
|
||||||
"type": "pve"
|
"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",
|
"name": "PeaNUT",
|
||||||
"slug": "peanut",
|
"slug": "peanut",
|
||||||
@@ -3871,7 +3969,7 @@
|
|||||||
],
|
],
|
||||||
"notes": [
|
"notes": [
|
||||||
"Set a root password if using autologin. This will be the PBS password. `passwd root`",
|
"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",
|
"type": "ct",
|
||||||
"default_credentials": {
|
"default_credentials": {
|
||||||
@@ -4122,6 +4220,32 @@
|
|||||||
],
|
],
|
||||||
"type": "ct"
|
"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",
|
"name": "RevealJS",
|
||||||
"slug": "revealjs",
|
"slug": "revealjs",
|
||||||
@@ -4161,7 +4285,8 @@
|
|||||||
],
|
],
|
||||||
"notes": [
|
"notes": [
|
||||||
"Check our configuration guide for help: `https://github.com/community-scripts/ProxmoxVE/discussions/2388`",
|
"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"
|
"type": "ct"
|
||||||
},
|
},
|
||||||
@@ -4203,6 +4328,18 @@
|
|||||||
],
|
],
|
||||||
"type": "pve"
|
"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",
|
"name": "seelf",
|
||||||
"slug": "seelf",
|
"slug": "seelf",
|
||||||
@@ -4416,7 +4553,7 @@
|
|||||||
"script": "ct/swizzin.sh",
|
"script": "ct/swizzin.sh",
|
||||||
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/swizzin.sh",
|
"script_url": "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/swizzin.sh",
|
||||||
"categories": [
|
"categories": [
|
||||||
15
|
13
|
||||||
],
|
],
|
||||||
"notes": [
|
"notes": [
|
||||||
"Installation might take a long time if choosing to install many apps. Be patient.",
|
"Installation might take a long time if choosing to install many apps. Be patient.",
|
||||||
@@ -4595,6 +4732,20 @@
|
|||||||
"notes": [],
|
"notes": [],
|
||||||
"type": "ct"
|
"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",
|
"name": "Traefik",
|
||||||
"slug": "traefik",
|
"slug": "traefik",
|
||||||
|
1294
scripts/backup_restore/backup_host4.sh
Normal file
1294
scripts/backup_restore/backup_host4.sh
Normal file
File diff suppressed because it is too large
Load Diff
@@ -92,6 +92,9 @@ cleanup_duplicate_repos_pve9() {
|
|||||||
local cleaned_count=0
|
local cleaned_count=0
|
||||||
declare -A seen_repos
|
declare -A seen_repos
|
||||||
|
|
||||||
|
if [ ! -s "$sources_file" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then
|
if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then
|
||||||
|
901
scripts/global/share-common.func
Normal file
901
scripts/global/share-common.func
Normal 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
|
||||||
|
}
|
@@ -240,14 +240,16 @@ EOF
|
|||||||
return 0
|
return 0
|
||||||
fi
|
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
|
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
|
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
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
msg_info "$(translate "Updating packages...")"
|
msg_info "$(translate "Updating packages...")"
|
||||||
apt-get install pv -y > /dev/null 2>&1
|
apt-get install pv -y > /dev/null 2>&1
|
||||||
msg_ok "$(translate "Packages updated successfully")"
|
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}"
|
echo -e "${TAB}${GN}🖥️ $(translate "Proxmox VE")${CL}: ${BL}$target_version (Debian $OS_CODENAME)${CL}"
|
||||||
|
|
||||||
msg_ok "$(translate "Proxmox VE 9.x configuration completed.")"
|
msg_ok "$(translate "Proxmox VE 9.x configuration completed.")"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
@@ -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"
|
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=$(grep "^lxc.idmap" /etc/pve/lxc/"$id".conf | grep 'u 0' | awk '{print $5}')
|
||||||
UID_SHIFT=${UID_SHIFT:-100000}
|
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
|
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_uid=$((UID_SHIFT + uid))
|
||||||
real_gid=$((UID_SHIFT + gid))
|
real_gid=$((UID_SHIFT + gid))
|
||||||
echo -e "${GREEN}$(translate 'User')${NC}: $username"
|
echo -e "${GREEN}$(translate 'User')${NC}: $username"
|
||||||
|
284
scripts/lxc/lxc-manual-guide.sh
Normal file
284
scripts/lxc/lxc-manual-guide.sh
Normal 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
|
@@ -232,6 +232,10 @@ convert_direct_method() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
msg_ok "$(translate 'Direct conversion completed for container') $CONTAINER_ID"
|
msg_ok "$(translate 'Direct conversion completed for container') $CONTAINER_ID"
|
||||||
|
|
||||||
|
echo -e
|
||||||
|
msg_success "Press Enter to continue..."
|
||||||
|
read -r
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup_and_finalize() {
|
cleanup_and_finalize() {
|
||||||
@@ -258,7 +262,7 @@ main() {
|
|||||||
msg_ok "$(translate 'Converted container ID:') $CONTAINER_ID"
|
msg_ok "$(translate 'Converted container ID:') $CONTAINER_ID"
|
||||||
msg_ok "$(translate 'LXC conversion from privileged to unprivileged completed successfully!')"
|
msg_ok "$(translate 'LXC conversion from privileged to unprivileged completed successfully!')"
|
||||||
echo -e
|
echo -e
|
||||||
msg_success "$(translate "Press Enter to continue")"
|
msg_success "$(translate "Press Enter to return to menu...")"
|
||||||
read -r
|
read -r
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
144
scripts/lxc/lxc-unprivileged-to-privileged.sh
Normal file
144
scripts/lxc/lxc-unprivileged-to-privileged.sh
Normal 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
|
@@ -35,37 +35,33 @@ show_main_menu() {
|
|||||||
"1" "$(translate 'Convert Privileged to Unprivileged')" \
|
"1" "$(translate 'Convert Privileged to Unprivileged')" \
|
||||||
"2" "$(translate 'Convert Unprivileged to Privileged')" \
|
"2" "$(translate 'Convert Unprivileged to Privileged')" \
|
||||||
"3" "$(translate 'Show Container Privilege Status')" \
|
"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
|
case $CHOICE in
|
||||||
1)
|
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)
|
2)
|
||||||
convert_unprivileged_to_privileged
|
bash <(curl -s "$REPO_URL/scripts/lxc/lxc-unprivileged-to-privileged.sh")
|
||||||
;;
|
;;
|
||||||
3)
|
3)
|
||||||
show_container_status
|
show_container_status
|
||||||
;;
|
;;
|
||||||
4)
|
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
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
convert_unprivileged_to_privileged() {
|
|
||||||
|
|
||||||
bash <(curl -fsSL "$REPO_URL/scripts/lcx/lxc-privileged-to-unprivileged.sh")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
show_container_status() {
|
show_container_status() {
|
||||||
show_proxmenux_logo
|
|
||||||
msg_info "$(translate 'Gathering container privilege information...')"
|
msg_info "$(translate 'Gathering container privilege information...')"
|
||||||
|
|
||||||
|
|
||||||
@@ -75,6 +71,7 @@ show_container_status() {
|
|||||||
echo "=================================" >> "$TEMP_FILE"
|
echo "=================================" >> "$TEMP_FILE"
|
||||||
echo "" >> "$TEMP_FILE"
|
echo "" >> "$TEMP_FILE"
|
||||||
|
|
||||||
|
|
||||||
pct list | awk 'NR>1 {print $1, $3}' | while read id name; do
|
pct list | awk 'NR>1 {print $1, $3}' | while read id name; do
|
||||||
if pct config "$id" | grep -q "^unprivileged: 1"; then
|
if pct config "$id" | grep -q "^unprivileged: 1"; then
|
||||||
status="$(translate 'Unprivileged')"
|
status="$(translate 'Unprivileged')"
|
||||||
@@ -87,17 +84,21 @@ show_container_status() {
|
|||||||
printf "ID: %-4s | %-20s | %-12s | %s\n" "$id" "$name" "$status" "$running_status" >> "$TEMP_FILE"
|
printf "ID: %-4s | %-20s | %-12s | %s\n" "$id" "$name" "$status" "$running_status" >> "$TEMP_FILE"
|
||||||
done
|
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
|
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"
|
rm -f "$TEMP_FILE"
|
||||||
show_main_menu
|
|
||||||
|
|
||||||
|
|
||||||
|
show_main_menu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
show_main_menu
|
||||||
show_main_menu
|
|
||||||
|
@@ -122,7 +122,7 @@ show_menu() {
|
|||||||
|
|
||||||
case $OPTION in
|
case $OPTION in
|
||||||
1) exec bash <(curl -s "$REPO_URL/scripts/menus/menu_post_install.sh") ;;
|
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") ;;
|
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") ;;
|
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") ;;
|
5) exec bash <(curl -s "$REPO_URL/scripts/menus/storage_menu.sh") ;;
|
||||||
|
88
scripts/menus/share_menu.sh
Normal file
88
scripts/menus/share_menu.sh
Normal 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
|
@@ -36,15 +36,12 @@ while true; do
|
|||||||
|
|
||||||
case $OPTION in
|
case $OPTION in
|
||||||
1)
|
1)
|
||||||
clear
|
|
||||||
bash <(curl -s "$REPO_URL/scripts/storage/disk-passthrough.sh")
|
bash <(curl -s "$REPO_URL/scripts/storage/disk-passthrough.sh")
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
clear
|
|
||||||
bash <(curl -s "$REPO_URL/scripts/storage/disk-passthrough_ct.sh")
|
bash <(curl -s "$REPO_URL/scripts/storage/disk-passthrough_ct.sh")
|
||||||
;;
|
;;
|
||||||
3)
|
3)
|
||||||
clear
|
|
||||||
bash <(curl -s "$REPO_URL/scripts/storage/import-disk-image.sh")
|
bash <(curl -s "$REPO_URL/scripts/storage/import-disk-image.sh")
|
||||||
;;
|
;;
|
||||||
4)
|
4)
|
||||||
|
@@ -479,47 +479,38 @@ apply_network_optimizations() {
|
|||||||
|
|
||||||
# Core buffers & queues
|
# Core buffers & queues
|
||||||
net.core.netdev_max_backlog = 8192
|
net.core.netdev_max_backlog = 8192
|
||||||
net.core.optmem_max = 8192
|
net.core.rmem_max = 16777216
|
||||||
net.core.rmem_max = 16777216
|
net.core.wmem_max = 16777216
|
||||||
net.core.wmem_max = 16777216
|
net.core.somaxconn = 8192
|
||||||
net.core.somaxconn = 8151
|
|
||||||
|
|
||||||
# IPv4 security hardening
|
# IPv4
|
||||||
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.accept_source_route = 0
|
||||||
net.ipv4.conf.all.log_martians = 0
|
net.ipv4.conf.all.secure_redirects = 0
|
||||||
net.ipv4.conf.all.rp_filter = 1
|
net.ipv4.conf.all.send_redirects = 0
|
||||||
net.ipv4.conf.all.secure_redirects = 0
|
net.ipv4.conf.all.log_martians = 1
|
||||||
net.ipv4.conf.all.send_redirects = 0
|
|
||||||
|
|
||||||
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.accept_source_route = 0
|
||||||
net.ipv4.conf.default.log_martians = 0
|
net.ipv4.conf.default.secure_redirects = 0
|
||||||
net.ipv4.conf.default.rp_filter = 1
|
net.ipv4.conf.default.send_redirects = 0
|
||||||
net.ipv4.conf.default.secure_redirects = 0
|
net.ipv4.conf.default.log_martians = 1
|
||||||
net.ipv4.conf.default.send_redirects = 0
|
|
||||||
|
|
||||||
# ICMP handling
|
# rp_filter: loose multi-homed/bridges
|
||||||
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
|
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
||||||
|
|
||||||
# TCP/IP tuning
|
# TCP/IP
|
||||||
net.ipv4.ip_local_port_range = 1024 65535
|
net.ipv4.ip_local_port_range = 1024 65535
|
||||||
net.ipv4.tcp_base_mss = 1024
|
net.ipv4.tcp_mtu_probing = 1
|
||||||
net.ipv4.tcp_fin_timeout = 10
|
net.ipv4.tcp_rfc1337 = 1
|
||||||
net.ipv4.tcp_keepalive_intvl = 30
|
net.ipv4.tcp_sack = 1
|
||||||
net.ipv4.tcp_keepalive_probes= 3
|
net.ipv4.tcp_rmem = 8192 87380 16777216
|
||||||
net.ipv4.tcp_keepalive_time = 240
|
net.ipv4.tcp_wmem = 8192 65536 16777216
|
||||||
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
|
|
||||||
|
|
||||||
# Unix sockets
|
# Unix sockets
|
||||||
net.unix.max_dgram_qlen = 4096
|
net.unix.max_dgram_qlen = 4096
|
||||||
@@ -655,7 +646,8 @@ install_log2ram_auto() {
|
|||||||
else
|
else
|
||||||
|
|
||||||
ROOT_PART=$(lsblk -no NAME,MOUNTPOINT | grep ' /$' | awk '{print $1}')
|
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}
|
SYSTEM_DISK=${SYSTEM_DISK:-sda}
|
||||||
if [[ "$SYSTEM_DISK" == nvme* || "$(cat /sys/block/$SYSTEM_DISK/queue/rotational 2>/dev/null)" == "0" ]]; then
|
if [[ "$SYSTEM_DISK" == nvme* || "$(cat /sys/block/$SYSTEM_DISK/queue/rotational 2>/dev/null)" == "0" ]]; then
|
||||||
is_ssd=true
|
is_ssd=true
|
||||||
@@ -665,11 +657,16 @@ install_log2ram_auto() {
|
|||||||
if [[ "$is_ssd" == true ]]; then
|
if [[ "$is_ssd" == true ]]; then
|
||||||
msg_ok "$(translate "System disk is SSD or M.2. Proceeding with Log2RAM setup.")"
|
msg_ok "$(translate "System disk is SSD or M.2. Proceeding with Log2RAM setup.")"
|
||||||
else
|
else
|
||||||
msg_warn "$(translate "System disk is not SSD/M.2. Skipping Log2RAM installation.")"
|
if whiptail --yesno "$(translate "Do you want to install Log2RAM anyway to reduce log write load?")" \
|
||||||
return 0
|
10 70 --title "Log2RAM"; then
|
||||||
|
:
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if [[ -f /etc/log2ram.conf ]] && command -v log2ram >/dev/null 2>&1 && systemctl list-units --all | grep -q log2ram; then
|
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.")"
|
msg_ok "$(translate "Log2RAM is already installed and configured correctly.")"
|
||||||
register_tool "log2ram" true
|
register_tool "log2ram" true
|
||||||
@@ -700,7 +697,7 @@ install_log2ram_auto() {
|
|||||||
if ! command -v git >/dev/null 2>&1; then
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
apt-get update -qq >/dev/null 2>&1
|
apt-get update -qq >/dev/null 2>&1
|
||||||
apt-get install -y git >/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
|
fi
|
||||||
|
|
||||||
if ! git clone https://github.com/azlux/log2ram.git /tmp/log2ram >/dev/null 2>>/tmp/log2ram_install.log; then
|
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
|
ensure_tools_json
|
||||||
|
|
||||||
apt_upgrade
|
apt_upgrade
|
||||||
|
cleanup
|
||||||
remove_subscription_banner
|
remove_subscription_banner
|
||||||
#configure_time_sync
|
#configure_time_sync
|
||||||
skip_apt_languages
|
skip_apt_languages
|
||||||
@@ -857,7 +855,7 @@ run_complete_optimization() {
|
|||||||
configure_kernel_panic
|
configure_kernel_panic
|
||||||
force_apt_ipv4
|
force_apt_ipv4
|
||||||
apply_network_optimizations
|
apply_network_optimizations
|
||||||
disable_rpc
|
#disable_rpc
|
||||||
customize_bashrc
|
customize_bashrc
|
||||||
install_log2ram_auto
|
install_log2ram_auto
|
||||||
setup_persistent_network
|
setup_persistent_network
|
||||||
|
@@ -605,7 +605,7 @@ install_system_utils_() {
|
|||||||
command -v "$1" >/dev/null 2>&1
|
command -v "$1" >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_repositories() {
|
ensure_repositories_() {
|
||||||
local sources_file="/etc/apt/sources.list"
|
local sources_file="/etc/apt/sources.list"
|
||||||
local need_update=false
|
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() {
|
install_single_package() {
|
||||||
local package="$1"
|
local package="$1"
|
||||||
local command_name="${2:-$package}"
|
local command_name="${2:-$package}"
|
||||||
@@ -1225,47 +1325,36 @@ apply_network_optimizations() {
|
|||||||
|
|
||||||
# Core buffers & queues
|
# Core buffers & queues
|
||||||
net.core.netdev_max_backlog = 8192
|
net.core.netdev_max_backlog = 8192
|
||||||
net.core.optmem_max = 8192
|
net.core.rmem_max = 16777216
|
||||||
net.core.rmem_max = 16777216
|
net.core.wmem_max = 16777216
|
||||||
net.core.wmem_max = 16777216
|
net.core.somaxconn = 8192
|
||||||
net.core.somaxconn = 8151
|
|
||||||
|
|
||||||
# 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.accept_source_route = 0
|
||||||
net.ipv4.conf.all.log_martians = 0
|
net.ipv4.conf.all.secure_redirects = 0
|
||||||
net.ipv4.conf.all.rp_filter = 1
|
net.ipv4.conf.all.send_redirects = 0
|
||||||
net.ipv4.conf.all.secure_redirects = 0
|
net.ipv4.conf.all.log_martians = 1
|
||||||
net.ipv4.conf.all.send_redirects = 0
|
|
||||||
|
|
||||||
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.accept_source_route = 0
|
||||||
net.ipv4.conf.default.log_martians = 0
|
net.ipv4.conf.default.secure_redirects = 0
|
||||||
net.ipv4.conf.default.rp_filter = 1
|
net.ipv4.conf.default.send_redirects = 0
|
||||||
net.ipv4.conf.default.secure_redirects = 0
|
net.ipv4.conf.default.log_martians = 1
|
||||||
net.ipv4.conf.default.send_redirects = 0
|
|
||||||
|
|
||||||
# ICMP handling
|
net.ipv4.conf.all.rp_filter = 2
|
||||||
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
net.ipv4.conf.default.rp_filter = 2
|
||||||
|
|
||||||
|
# ICMP
|
||||||
|
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
||||||
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
||||||
|
|
||||||
# TCP/IP tuning
|
# TCP/IP
|
||||||
net.ipv4.ip_local_port_range = 1024 65535
|
net.ipv4.ip_local_port_range = 1024 65535
|
||||||
net.ipv4.tcp_base_mss = 1024
|
net.ipv4.tcp_mtu_probing = 1
|
||||||
net.ipv4.tcp_fin_timeout = 10
|
net.ipv4.tcp_rfc1337 = 1
|
||||||
net.ipv4.tcp_keepalive_intvl = 30
|
net.ipv4.tcp_sack = 1
|
||||||
net.ipv4.tcp_keepalive_probes= 3
|
net.ipv4.tcp_rmem = 8192 87380 16777216
|
||||||
net.ipv4.tcp_keepalive_time = 240
|
net.ipv4.tcp_wmem = 8192 65536 16777216
|
||||||
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
|
|
||||||
|
|
||||||
# Unix sockets
|
# Unix sockets
|
||||||
net.unix.max_dgram_qlen = 4096
|
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...")"
|
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_() {
|
configure_figurine_() {
|
||||||
msg_info2 "$(translate "Installing and configuring Figurine...")"
|
msg_info2 "$(translate "Installing and configuring Figurine...")"
|
||||||
|
|
||||||
# Variables
|
|
||||||
local version="1.3.0"
|
local version="1.3.0"
|
||||||
local file="figurine_linux_amd64_v${version}.tar.gz"
|
local file="figurine_linux_amd64_v${version}.tar.gz"
|
||||||
local url="https://github.com/arsham/figurine/releases/download/v${version}/${file}"
|
local url="https://github.com/arsham/figurine/releases/download/v${version}/${file}"
|
||||||
local temp_dir
|
local temp_dir; temp_dir=$(mktemp -d)
|
||||||
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 install_dir="/usr/local/bin"
|
local install_dir="/usr/local/bin"
|
||||||
local profile_script="/etc/profile.d/figurine.sh"
|
local profile_script="/etc/profile.d/figurine.sh"
|
||||||
local bin_path="${install_dir}/figurine"
|
local bin_path="${install_dir}/figurine"
|
||||||
local bashrc="/root/.bashrc"
|
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() {
|
[[ -f "$bashrc" ]] || touch "$bashrc"
|
||||||
rm -rf "$temp_dir" 2>/dev/null || true
|
|
||||||
}
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
|
|
||||||
if command -v figurine &>/dev/null; then
|
if command -v figurine &>/dev/null; then
|
||||||
msg_info "$(translate "Updating Figurine binary...")"
|
msg_info "$(translate "Updating Figurine binary...")"
|
||||||
@@ -3205,13 +3310,12 @@ configure_figurine() {
|
|||||||
msg_info "$(translate "Downloading Figurine v${version}...")"
|
msg_info "$(translate "Downloading Figurine v${version}...")"
|
||||||
fi
|
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")"
|
msg_error "$(translate "Failed to download Figurine")"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! tar -xf "${temp_dir}/${file}" -C "${temp_dir}"; then
|
||||||
if ! tar -xf "${temp_dir}/${file}" -C "${temp_dir}" > /dev/null 2>&1; then
|
|
||||||
msg_error "$(translate "Failed to extract package")"
|
msg_error "$(translate "Failed to extract package")"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -3223,39 +3327,54 @@ configure_figurine() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
msg_info "$(translate "Installing binary to ${install_dir}...")"
|
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'
|
cat > "$profile_script" << 'EOF'
|
||||||
/usr/local/bin/figurine -f "3d.flf" $(hostname)
|
/usr/local/bin/figurine -f "3d.flf" $(hostname)
|
||||||
EOF
|
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
|
local -a ALIASES=(
|
||||||
sed -i "/^${marker_begin}$/,/^${marker_end}$/d" "$bashrc"
|
"aptup=apt update && apt dist-upgrade"
|
||||||
fi
|
"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'
|
awk '!seen[$0]++' "$bashrc" > "${bashrc}.tmp" && mv "${bashrc}.tmp" "$bashrc"
|
||||||
${marker_begin}
|
}
|
||||||
# ProxMenux Figurine aliases and tools
|
|
||||||
alias aptup='apt update && apt dist-upgrade'
|
ensure_aliases
|
||||||
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
|
|
||||||
msg_ok "$(translate "Aliases added to .bashrc")"
|
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.")"
|
msg_success "$(translate "Figurine installation and configuration completed successfully.")"
|
||||||
register_tool "figurine" true
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
||||||
|
|
||||||
|
472
scripts/share/commands_share.sh
Normal file
472
scripts/share/commands_share.sh
Normal 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
|
271
scripts/share/group_manager.sh
Normal file
271
scripts/share/group_manager.sh
Normal 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
1292
scripts/share/guia.md
Normal file
File diff suppressed because it is too large
Load Diff
93
scripts/share/local-shared-manager.sh
Normal file
93
scripts/share/local-shared-manager.sh
Normal 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
|
480
scripts/share/lxc-mount-manager.sh
Normal file
480
scripts/share/lxc-mount-manager.sh
Normal 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
|
1103
scripts/share/lxc-mount-manager_minimal.sh
Normal file
1103
scripts/share/lxc-mount-manager_minimal.sh
Normal file
File diff suppressed because it is too large
Load Diff
687
scripts/share/nfs_client.sh
Normal file
687
scripts/share/nfs_client.sh
Normal 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
852
scripts/share/nfs_host.sh
Normal 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
|
572
scripts/share/nfs_lxc_server.sh
Normal file
572
scripts/share/nfs_lxc_server.sh
Normal 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
|
1085
scripts/share/samba_client.sh
Normal file
1085
scripts/share/samba_client.sh
Normal file
File diff suppressed because it is too large
Load Diff
1266
scripts/share/samba_host.sh
Normal file
1266
scripts/share/samba_host.sh
Normal file
File diff suppressed because it is too large
Load Diff
576
scripts/share/samba_lxc_server.sh
Normal file
576
scripts/share/samba_lxc_server.sh
Normal 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
|
@@ -56,7 +56,7 @@ install_system_utils() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ensure_repositories() {
|
ensure_repositories_() {
|
||||||
local sources_file="/etc/apt/sources.list"
|
local sources_file="/etc/apt/sources.list"
|
||||||
local need_update=false
|
local need_update=false
|
||||||
|
|
||||||
@@ -85,6 +85,98 @@ EOF
|
|||||||
return 0
|
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() {
|
install_single_package() {
|
||||||
local package="$1"
|
local package="$1"
|
||||||
local command_name="${2:-$package}"
|
local command_name="${2:-$package}"
|
||||||
|
@@ -72,7 +72,7 @@ ask_run_mode() {
|
|||||||
msg_ok "$(translate "Run mode: Unattended")"
|
msg_ok "$(translate "Run mode: Unattended")"
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
export APT_LISTCHANGES_FRONTEND=none
|
export APT_LISTCHANGES_FRONTEND=none
|
||||||
return 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
@@ -617,13 +617,12 @@ run_pve8to9_check() {
|
|||||||
|
|
||||||
# Error 1: systemd-boot meta-package
|
# Error 1: systemd-boot meta-package
|
||||||
if grep -q 'systemd-boot meta-package installed' "$tmp"; then
|
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_commands+=("apt remove -y systemd-boot")
|
||||||
repair_descriptions+=("$(translate "Fix systemd-boot meta-package conflict")")
|
repair_descriptions+=("$(translate "Remove obsolete systemd-boot meta-package")")
|
||||||
echo -e "${YW}$(translate "Fix systemd-boot:") ${CL}apt install systemd-boot-efi systemd-boot-tools -y && apt remove systemd-boot -y"
|
echo -e "${YW}$(translate "Fix systemd-boot:") ${CL}apt remove -y systemd-boot"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Error 2: Kernel version mismatch
|
# 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_commands+=("update-grub")
|
||||||
repair_descriptions+=("$(translate "Update kernel to compatible version")")
|
repair_descriptions+=("$(translate "Update kernel to compatible version")")
|
||||||
echo -e "${YW}$(translate "Fix kernel version:") ${CL}update-grub"
|
echo -e "${YW}$(translate "Fix kernel version:") ${CL}update-grub"
|
||||||
@@ -651,18 +650,12 @@ run_pve8to9_check() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Error 6: Disk space issues
|
# 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_commands+=("apt clean && apt autoremove -y && journalctl --vacuum-time=7d")
|
||||||
repair_descriptions+=("$(translate "Free up disk space")")
|
repair_descriptions+=("$(translate "Free up disk space")")
|
||||||
echo -e "${YW}$(translate "Fix disk space:") ${CL}apt clean && apt autoremove -y && journalctl --vacuum-time=7d"
|
echo -e "${YW}$(translate "Fix disk space:") ${CL}apt clean && apt autoremove -y && journalctl --vacuum-time=7d"
|
||||||
fi
|
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
|
echo -e
|
||||||
|
|
||||||
@@ -990,47 +983,45 @@ run_pve8to9_check2() {
|
|||||||
|
|
||||||
# Error 1: systemd-boot meta-package
|
# Error 1: systemd-boot meta-package
|
||||||
if grep -q 'systemd-boot meta-package installed' "$tmp"; then
|
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_commands+=("apt remove -y systemd-boot")
|
||||||
repair_descriptions+=("$(translate "Fix systemd-boot meta-package conflict")")
|
repair_descriptions+=("$(translate "Remove obsolete systemd-boot meta-package")")
|
||||||
echo -e "${YW}$(translate "Fix systemd-boot:") ${CL}apt install systemd-boot-efi systemd-boot-tools -y && apt remove systemd-boot -y"
|
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
|
fi
|
||||||
|
|
||||||
|
# Error 3: Ceph version incompatible
|
||||||
# Error 2: Ceph version incompatible
|
|
||||||
if grep -q -E '(ceph.*version|ceph.*incompatible)' "$tmp"; then
|
if grep -q -E '(ceph.*version|ceph.*incompatible)' "$tmp"; then
|
||||||
repair_commands+=("ceph versions && pveceph upgrade")
|
repair_commands+=("ceph versions && pveceph upgrade")
|
||||||
repair_descriptions+=("$(translate "Upgrade Ceph to compatible version")")
|
repair_descriptions+=("$(translate "Upgrade Ceph to compatible version")")
|
||||||
echo -e "${YW}$(translate "Fix Ceph version:") ${CL}ceph versions && pveceph upgrade"
|
echo -e "${YW}$(translate "Fix Ceph version:") ${CL}ceph versions && pveceph upgrade"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Error 3: Repository configuration issues
|
# Error 4: Repository configuration issues
|
||||||
if grep -q -E '(repository.*issue|repo.*problem|sources.*error)' "$tmp"; then
|
if grep -q -E '(repository.*issue|repo.*problem|sources.*error)' "$tmp"; then
|
||||||
repair_commands+=("cleanup_duplicate_repos && configure_repositories")
|
repair_commands+=("cleanup_duplicate_repos && configure_repositories")
|
||||||
repair_descriptions+=("$(translate "Fix repository configuration")")
|
repair_descriptions+=("$(translate "Fix repository configuration")")
|
||||||
echo -e "${YW}$(translate "Fix repositories:") ${CL}cleanup_duplicate_repos && configure_repositories"
|
echo -e "${YW}$(translate "Fix repositories:") ${CL}cleanup_duplicate_repos && configure_repositories"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Error 4: Package conflicts
|
# Error 5: Package conflicts
|
||||||
if grep -q -E '(package.*conflict|dependency.*problem)' "$tmp"; then
|
if grep -q -E '(package.*conflict|dependency.*problem)' "$tmp"; then
|
||||||
repair_commands+=("apt update && apt autoremove -y && apt autoclean")
|
repair_commands+=("apt update && apt autoremove -y && apt autoclean")
|
||||||
repair_descriptions+=("$(translate "Resolve package conflicts")")
|
repair_descriptions+=("$(translate "Resolve package conflicts")")
|
||||||
echo -e "${YW}$(translate "Fix package conflicts:") ${CL}apt update && apt autoremove -y && apt autoclean"
|
echo -e "${YW}$(translate "Fix package conflicts:") ${CL}apt update && apt autoremove -y && apt autoclean"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Error 5: Disk space issues
|
# 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_commands+=("apt clean && apt autoremove -y && journalctl --vacuum-time=7d")
|
||||||
repair_descriptions+=("$(translate "Free up disk space")")
|
repair_descriptions+=("$(translate "Free up disk space")")
|
||||||
echo -e "${YW}$(translate "Fix disk space:") ${CL}apt clean && apt autoremove -y && journalctl --vacuum-time=7d"
|
echo -e "${YW}$(translate "Fix disk space:") ${CL}apt clean && apt autoremove -y && journalctl --vacuum-time=7d"
|
||||||
fi
|
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
|
echo -e
|
||||||
|
|
||||||
|
|
||||||
|
@@ -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 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 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"
|
"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 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 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 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"
|
"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"
|
"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() {
|
function select_linux_cloudinit() {
|
||||||
local CLOUDINIT_OPTIONS=(
|
local CLOUDINIT_OPTIONS=(
|
||||||
"1" "Arch Linux (Cloud-Init automated) │ Helper Scripts"
|
"1" "Arch Linux (Cloud-Init automated) │ Helper Scripts"
|
||||||
"2" "Debian 12 (Cloud-Init automated) │ Helper Scripts"
|
"2" "Debian 13 (Cloud-Init automated) │ Helper Scripts"
|
||||||
"3" "Ubuntu 22.04 (Cloud-Init automated) │ Helper Scripts"
|
"3" "Debian 12 (Cloud-Init automated) │ Helper Scripts"
|
||||||
"4" "Ubuntu 24.04 (Cloud-Init automated) │ Helper Scripts"
|
"4" "Ubuntu 22.04 (Cloud-Init automated) │ Helper Scripts"
|
||||||
"5" "Ubuntu 24.10 (Cloud-Init automated) │ Helper Scripts"
|
"5" "Ubuntu 24.04 (Cloud-Init automated) │ Helper Scripts"
|
||||||
"6" "Ubuntu 25.04 (Cloud-Init automated) │ Helper Scripts"
|
"6" "Ubuntu 24.10 (Cloud-Init automated) │ Helper Scripts"
|
||||||
"7" "$(translate "Return to Main Menu")"
|
"7" "Ubuntu 25.04 (Cloud-Init automated) │ Helper Scripts"
|
||||||
|
"8" "$(translate "Return to Main Menu")"
|
||||||
)
|
)
|
||||||
|
|
||||||
local script_selection
|
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")
|
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/archlinux-vm.sh")
|
||||||
;;
|
;;
|
||||||
2)
|
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)
|
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)
|
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)
|
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)
|
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")
|
bash <(curl -s "https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/vm/ubuntu2504-vm.sh")
|
||||||
echo -e
|
echo -e
|
||||||
echo -e "after installation, checkout:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/272"
|
echo -e "after installation, checkout:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/272"
|
||||||
echo -e
|
echo -e
|
||||||
;;
|
;;
|
||||||
7)
|
8)
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
@@ -46,7 +46,7 @@ function select_nas_iso() {
|
|||||||
"4" "OpenMediaVault VM (Debian based)"
|
"4" "OpenMediaVault VM (Debian based)"
|
||||||
"5" "XigmaNAS VM (FreeBSD based)"
|
"5" "XigmaNAS VM (FreeBSD based)"
|
||||||
"6" "Rockstor VM (openSUSE based)"
|
"6" "Rockstor VM (openSUSE based)"
|
||||||
"7" "ZimaOS VM (R0GGER proxmox-zimaos)"
|
"7" "ZimaOS VM (Proxmox-zimaos)"
|
||||||
"8" "Umbrel OS VM (Helper Scripts)"
|
"8" "Umbrel OS VM (Helper Scripts)"
|
||||||
"9" "$(translate "Return to Main Menu")"
|
"9" "$(translate "Return to Main Menu")"
|
||||||
)
|
)
|
||||||
@@ -54,7 +54,7 @@ function select_nas_iso() {
|
|||||||
local NAS_TYPE
|
local NAS_TYPE
|
||||||
NAS_TYPE=$(dialog --backtitle "ProxMenux" \
|
NAS_TYPE=$(dialog --backtitle "ProxMenux" \
|
||||||
--title "$(translate "NAS Systems")" \
|
--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)
|
"${NAS_OPTIONS[@]}" 3>&1 1>&2 2>&3)
|
||||||
|
|
||||||
|
|
||||||
@@ -103,18 +103,9 @@ function select_nas_iso() {
|
|||||||
HN="Rockstor"
|
HN="Rockstor"
|
||||||
;;
|
;;
|
||||||
7)
|
7)
|
||||||
HN="ZimaOS-VM"
|
bash <(curl -s "$REPO_URL/scripts/vm/zimaos.sh")
|
||||||
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
|
|
||||||
msg_success "$(translate "Press Enter to return to menu...")"
|
msg_success "$(translate "Press Enter to return to menu...")"
|
||||||
read -r
|
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
|
return 1
|
||||||
;;
|
;;
|
||||||
8)
|
8)
|
||||||
|
1207
scripts/vm/zimaos.sh
Normal file
1207
scripts/vm/zimaos.sh
Normal file
File diff suppressed because it is too large
Load Diff
BIN
web/public/share/main-menu.png
Normal file
BIN
web/public/share/main-menu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
Reference in New Issue
Block a user