mirror of
https://github.com/vdsm/virtual-dsm.git
synced 2026-08-29 12:17:07 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bcacf5e59 | ||
|
|
3bea87f087 | ||
|
|
f01e1564f7 | ||
|
|
cf8cdbf0c1 | ||
|
|
0813494ee0 | ||
|
|
0d71e30f50 | ||
|
|
c203deab9a | ||
|
|
9cf31df266 | ||
|
|
0cd08f2ee1 | ||
|
|
a32d215e90 | ||
|
|
c1f9a690f9 | ||
|
|
5de504deb3 | ||
|
|
250eb18238 | ||
|
|
ca80da59bd | ||
|
|
9cd86dfe82 | ||
|
|
5bb93651f7 | ||
|
|
0c268308a4 |
+1
-1
@@ -83,6 +83,6 @@ ENV RAM_SIZE="2G"
|
|||||||
ENV CPU_CORES="2"
|
ENV CPU_CORES="2"
|
||||||
ENV DISK_SIZE="256G"
|
ENV DISK_SIZE="256G"
|
||||||
|
|
||||||
HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh
|
HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD ["/run/check.sh"]
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]
|
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]
|
||||||
|
|||||||
+38
-36
@@ -2,13 +2,15 @@
|
|||||||
|
|
||||||
This page lists all the environment variables that can be used to configure the container.
|
This page lists all the environment variables that can be used to configure the container.
|
||||||
|
|
||||||
|
An empty default means the variable is unset and its value is determined automatically when applicable.
|
||||||
|
|
||||||
## 💽 Virtual DSM
|
## 💽 Virtual DSM
|
||||||
|
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `URL` | | URL or local path of the DSM `.pat` installation file. When unset, the default Virtual DSM image is downloaded automatically. |
|
| `URL` | | URL or local path to the DSM `.pat` installation file. Downloads the default Virtual DSM image automatically when unset. |
|
||||||
| `HOST_MAC` | | MAC address reported to DSM. |
|
| `HOST_MAC` | | MAC address reported to DSM. |
|
||||||
| `HOST_MODEL` | | Synology host device model reported to DSM. |
|
| `HOST_MODEL` | | Synology host model reported to DSM. |
|
||||||
| `HOST_SERIAL` | | Synology host serial number reported to DSM. |
|
| `HOST_SERIAL` | | Synology host serial number reported to DSM. |
|
||||||
| `GUEST_SERIAL` | | Synology guest serial number reported to DSM. |
|
| `GUEST_SERIAL` | | Synology guest serial number reported to DSM. |
|
||||||
|
|
||||||
@@ -16,57 +18,57 @@ This page lists all the environment variables that can be used to configure the
|
|||||||
|
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `CPU_CORES` | `2` | Number of CPU cores assigned to the VM. Can also be set to `max` or `half`. |
|
| `CPU_CORES` | `2` | Number of virtual CPU cores, such as `4`, `half`, or `max`. |
|
||||||
| `CPU_MODEL` | `host` | QEMU CPU model to use. |
|
| `CPU_MODEL` | `host` | QEMU CPU model. |
|
||||||
| `CPU_FLAGS` | | Additional QEMU CPU flags. |
|
| `CPU_FLAGS` | | Additional QEMU CPU flags. |
|
||||||
| `HOST_CPU` | | CPU name reported to DSM. Automatically selected when unset. |
|
| `HOST_CPU` | | CPU name reported to DSM. Selected automatically when unset. |
|
||||||
| `KVM` | `Y` | Enables KVM hardware acceleration. Set to `N` to disable. |
|
| `KVM` | `Y` | Enables KVM hardware acceleration. |
|
||||||
| `RAM_SIZE` | `2G` | Amount of RAM assigned to the VM, for example `2G`, `4G`, `max`, or `half`. |
|
| `RAM_SIZE` | `2G` | Amount of RAM assigned to DSM, such as `2G`, `4G`, `half`, or `max`. |
|
||||||
| `RAM_CHECK` | `Y` | Checks whether enough host memory is available before starting the VM. |
|
| `RAM_CHECK` | `Y` | Checks whether enough host memory is available before starting DSM. |
|
||||||
|
|
||||||
## 💾 Storage
|
## 💾 Storage
|
||||||
|
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `DISK_SIZE` | `256G` | Size of the main data disk. |
|
| `DISK_SIZE` | `256G` | Size of the main data disk. |
|
||||||
| `DISK_FMT` | `raw` | Disk image format, usually `raw` or `qcow2`. |
|
| `DISK_FMT` | `raw` | Disk image format: `raw` or `qcow2`. |
|
||||||
| `DISK_TYPE` | `scsi` | Disk controller/device type, such as `sata`, `scsi`, `nvme`, or `blk`. |
|
| `DISK_TYPE` | `scsi` | Disk device type, such as `sata`, `scsi`, `nvme`, or `blk`. |
|
||||||
| `DISK_CACHE` | `none` | QEMU disk cache mode, for example `none` or `writeback`. |
|
| `DISK_CACHE` | `none` | Disk cache mode, such as `none` or `writeback`. |
|
||||||
| `DISK_IO` | `native` | QEMU disk I/O mode, for example `native`, `threads`, or `io_uring`. |
|
| `DISK_IO` | `native` | Disk I/O mode, such as `native`, `threads`, or `io_uring`. |
|
||||||
| `DISK_DISCARD` | `unmap` | Enables TRIM/unmap support for the data disk. |
|
| `DISK_DISCARD` | `unmap` | Discard/TRIM mode for the primary disk. |
|
||||||
| `DISK_ROTATION` | `1` | Rotation rate reported to the guest. Use `1` for SSD-like storage. |
|
| `DISK_ROTATION` | `1` | Rotation rate reported to the guest. Use `1` to identify the disk as an SSD. |
|
||||||
| `DISK_FLAGS` | | Additional options used when creating qcow2 disks. |
|
| `DISK_FLAGS` | | Additional options used when creating `qcow2` disks. |
|
||||||
| `ALLOCATE` | `N` | Preallocates disk space when creating the data disk. |
|
| `ALLOCATE` | `N` | Preallocates space for the data disks. |
|
||||||
| `STORAGE` | `/storage` | Storage directory used for disks, firmware variables, and generated files. |
|
| `STORAGE` | `/storage` | Storage directory used for disks, settings, and downloads. |
|
||||||
|
|
||||||
## 🌐 Networking
|
## 🌐 Networking
|
||||||
|
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `NETWORK` | `Y` | Network mode. Common values are `Y` for NAT, `passt`, `slirp`, or `N` to disable networking. |
|
| `NETWORK` | | Network mode, such as `nat`, `passt`, `slirp`, or `N` to disable networking. |
|
||||||
| `DHCP` | `N` | Enables DHCP/macvtap mode so the VM receives an address from the external LAN. |
|
| `DHCP` | `N` | Enables macvtap networking so DSM receives an address from the external LAN through DHCP. |
|
||||||
| `IP` | | Guest IP address override. |
|
| `HOST` | `VirtualDSM` | Hostname assigned to DSM. |
|
||||||
|
| `IP` | | Overrides the automatically selected guest IPv4 address. |
|
||||||
| `MAC` | | Guest network adapter MAC address. |
|
| `MAC` | | Guest network adapter MAC address. |
|
||||||
| `HOST` | `VirtualDSM` | Hostname assigned to the VM. |
|
|
||||||
| `DEV` | `eth0` | Host/container network interface to use. |
|
|
||||||
| `MTU` | | Network MTU to use for the guest interface. |
|
|
||||||
| `MASK` | `255.255.255.0` | IPv4 netmask. |
|
|
||||||
| `TAP` | `dsm` | TAP/macvtap interface name. |
|
|
||||||
| `BRIDGE` | `docker` | Bridge name used for NAT networking. |
|
|
||||||
| `ADAPTER` | `virtio-net-pci` | QEMU network adapter model. |
|
| `ADAPTER` | `virtio-net-pci` | QEMU network adapter model. |
|
||||||
| `HOST_PORTS` | | Ports reserved for services running on the host/container side. |
|
| `DEV` | `eth0` | Container network interface used as the uplink. |
|
||||||
| `USER_PORTS` | | Additional ports to forward to the VM when using user-mode networking. |
|
| `MTU` | | MTU assigned to the guest network interface. |
|
||||||
| `DNSMASQ_OPTS` | | Additional dnsmasq options. |
|
| `MASK` | `255.255.255.0` | IPv4 netmask. |
|
||||||
| `DNSMASQ_DEBUG` | `N` | Enables dnsmasq log tailing. |
|
| `TAP` | `dsm` | TAP or macvtap interface name. |
|
||||||
|
| `BRIDGE` | `docker` | Bridge name used for NAT networking. |
|
||||||
|
| `HOST_PORTS` | | Ports excluded from guest forwarding. |
|
||||||
|
| `USER_PORTS` | | Additional ports to forward to DSM when using user-mode networking. |
|
||||||
|
| `DNSMASQ_OPTS` | | Additional options passed to dnsmasq. |
|
||||||
|
| `DNSMASQ_DEBUG` | `N` | Enables dnsmasq debug output. |
|
||||||
| `DNSMASQ_DISABLE` | `N` | Disables the internal dnsmasq resolver. |
|
| `DNSMASQ_DISABLE` | `N` | Disables the internal dnsmasq resolver. |
|
||||||
| `PASST_OPTS` | | Additional passt options. |
|
| `PASST_OPTS` | | Additional options passed to passt. |
|
||||||
| `PASST_DEBUG` | `N` | Enables passt debug output. |
|
| `PASST_DEBUG` | `N` | Enables passt debug output. |
|
||||||
|
|
||||||
## 🖥️ Display
|
## 🖥️ Display
|
||||||
|
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `DISPLAY` | `none` | QEMU display backend. Common values are `vnc`, `disabled`, or `none`. |
|
| `DISPLAY` | `none` | Display backend, such as `vnc`, `disabled`, or `none`. |
|
||||||
| `VGA` | `none` | QEMU video adapter model. |
|
| `VGA` | `none` | QEMU video adapter model. |
|
||||||
| `GPU` | `N` | Enables Intel iGPU acceleration. |
|
| `GPU` | `N` | Enables Intel iGPU acceleration. |
|
||||||
| `RENDERNODE` | `/dev/dri/renderD128` | Render node used for GPU acceleration. |
|
| `RENDERNODE` | `/dev/dri/renderD128` | Render node used for GPU acceleration. |
|
||||||
@@ -76,8 +78,8 @@ This page lists all the environment variables that can be used to configure the
|
|||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `SHUTDOWN` | `Y` | Enables graceful shutdown. |
|
| `SHUTDOWN` | `Y` | Enables graceful shutdown. |
|
||||||
| `TIMEOUT` | `115` | Timeout used while waiting for DSM to shut down. |
|
| `TIMEOUT` | `115` | Maximum time, in seconds, to wait before forcing DSM to stop. |
|
||||||
| `API_TIMEOUT` | `90` | Timeout used for the shutdown API call. |
|
| `API_TIMEOUT` | `90` | Maximum time, in seconds, to wait for the shutdown API call. |
|
||||||
|
|
||||||
## 🐞 Debugging
|
## 🐞 Debugging
|
||||||
|
|
||||||
@@ -87,5 +89,5 @@ This page lists all the environment variables that can be used to configure the
|
|||||||
| `TRACE` | `N` | Enables shell command tracing. |
|
| `TRACE` | `N` | Enables shell command tracing. |
|
||||||
| `COM_PORT` | `2210` | Internal communication port used by the DSM host helper. |
|
| `COM_PORT` | `2210` | Internal communication port used by the DSM host helper. |
|
||||||
| `CHR_PORT` | `12345` | Internal character device port used by the DSM host helper. |
|
| `CHR_PORT` | `12345` | Internal character device port used by the DSM host helper. |
|
||||||
| `HOST_DEBUG` | `N` | Enables debug output for the host helper. |
|
| `HOST_DEBUG` | `N` | Enables debug output for the DSM host helper. |
|
||||||
| `ARGUMENTS` | | Additional raw QEMU arguments appended to the generated command line. |
|
| `ARGUMENTS` | | Additional raw arguments appended to the QEMU command line. |
|
||||||
|
|||||||
@@ -66,12 +66,13 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
|
|||||||
|
|
||||||
## Requirements ⚙️
|
## Requirements ⚙️
|
||||||
|
|
||||||
- A Linux host with KVM support, or Docker Desktop / Podman on Windows 11 with nested virtualization enabled.
|
- Docker or Podman on a Linux host with KVM support.
|
||||||
- At least 2 GB of RAM available.
|
- Docker Desktop or Podman (Desktop) on Windows 11 with nested virtualization enabled.
|
||||||
- At least 32 GB of free disk space.
|
- At least 2 GB of available RAM.
|
||||||
|
- At least 32 GB of free disk space.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Docker Desktop on macOS and Windows 10 do not currently provide the required KVM support for this image.
|
> Docker Desktop on Linux, macOS, and Windows 10 does not currently provide KVM access to containers and is therefore not supported.
|
||||||
|
|
||||||
## FAQ 💬
|
## FAQ 💬
|
||||||
|
|
||||||
@@ -147,34 +148,6 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
|
|||||||
CPU_CORES: "4"
|
CPU_CORES: "4"
|
||||||
```
|
```
|
||||||
|
|
||||||
### How do I verify if my system supports KVM?
|
|
||||||
|
|
||||||
First check if your software is compatible using this chart:
|
|
||||||
|
|
||||||
| **Product** | **Linux** | **Win11** | **Win10** | **macOS** |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| Docker CLI | ✅ | ✅ | ❌ | ❌ |
|
|
||||||
| Docker Desktop | ❌ | ✅ | ❌ | ❌ |
|
|
||||||
| Podman CLI | ✅ | ✅ | ❌ | ❌ |
|
|
||||||
| Podman Desktop | ✅ | ✅ | ❌ | ❌ |
|
|
||||||
|
|
||||||
After that you can run the following commands in Linux to check your system:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt install cpu-checker
|
|
||||||
sudo kvm-ok
|
|
||||||
```
|
|
||||||
|
|
||||||
If you receive an error from `kvm-ok` indicating that KVM cannot be used, please check whether:
|
|
||||||
|
|
||||||
- the virtualization extensions (`Intel VT-x` or `AMD SVM`) are enabled in your BIOS.
|
|
||||||
|
|
||||||
- you enabled "nested virtualization" if you are running the container inside a virtual machine.
|
|
||||||
|
|
||||||
- you are not using a cloud provider, as most of them do not allow nested virtualization for their VPSs.
|
|
||||||
|
|
||||||
If you did not receive any error from `kvm-ok` but the container still complains about a missing KVM device, it could help to add `privileged: true` to your compose file (or `sudo` to your `docker` command) to rule out any permission issue.
|
|
||||||
|
|
||||||
### How do I assign an individual IP address to the container?
|
### How do I assign an individual IP address to the container?
|
||||||
|
|
||||||
By default, the container uses bridge networking, which shares the IP address with the host.
|
By default, the container uses bridge networking, which shares the IP address with the host.
|
||||||
@@ -263,7 +236,38 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
|
|||||||
|
|
||||||
### Are these all available options?
|
### Are these all available options?
|
||||||
|
|
||||||
No. For a complete overview of all supported settings, see the [environment variables](docs/environment.md) page.
|
No. For a complete overview of all supported settings, see the [environment variables](docs/environment.md) page.
|
||||||
|
|
||||||
|
### How do I verify that KVM is available?
|
||||||
|
|
||||||
|
First, make sure your platform and container runtime meet the [requirements](#requirements-️) listed above.
|
||||||
|
|
||||||
|
On a Linux host, install `cpu-checker` and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install cpu-checker
|
||||||
|
sudo kvm-ok
|
||||||
|
```
|
||||||
|
|
||||||
|
A working configuration should report:
|
||||||
|
|
||||||
|
```text
|
||||||
|
KVM acceleration can be used
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also verify that the KVM device exists:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -l /dev/kvm
|
||||||
|
```
|
||||||
|
|
||||||
|
If KVM is unavailable, check whether:
|
||||||
|
|
||||||
|
- Hardware virtualization (`Intel VT-x` or `AMD-V`) is enabled in your BIOS or UEFI.
|
||||||
|
- Nested virtualization is enabled when the host itself is a virtual machine.
|
||||||
|
- Your VPS or cloud provider supports nested virtualization.
|
||||||
|
|
||||||
|
If `kvm-ok` succeeds but the container still reports that KVM is unavailable, you can temporarily add `privileged: true` to your Compose file to rule out a permission or device-access issue.
|
||||||
|
|
||||||
### What are the differences compared to the standard DSM?
|
### What are the differences compared to the standard DSM?
|
||||||
|
|
||||||
|
|||||||
+7
-10
@@ -27,11 +27,11 @@ SYSTEM="$STORAGE/$BASE.system.img"
|
|||||||
[ ! -s "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82
|
[ ! -s "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82
|
||||||
|
|
||||||
if ! setOwner "$BOOT"; then
|
if ! setOwner "$BOOT"; then
|
||||||
error "Failed to set the owner for \"$BOOT\" !"
|
warn "failed to set the owner for \"$BOOT\" !"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! setOwner "$SYSTEM"; then
|
if ! setOwner "$SYSTEM"; then
|
||||||
error "Failed to set the owner for \"$SYSTEM\" !"
|
warn "failed to set the owner for \"$SYSTEM\" !"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fmt2ext() {
|
fmt2ext() {
|
||||||
@@ -404,15 +404,13 @@ convertDisk() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! rm -f "$SOURCE_FILE"; then
|
if ! mv "$TMP_FILE" "$DST_FILE"; then
|
||||||
rm -f "$TMP_FILE"
|
error "Failed to move converted $DISK_DESC image to $DST_FILE."
|
||||||
error "Failed to remove old $DISK_DESC image $SOURCE_FILE."
|
|
||||||
exit 79
|
exit 79
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! mv "$TMP_FILE" "$DST_FILE"; then
|
if ! rm -f "$SOURCE_FILE"; then
|
||||||
rm -f "$TMP_FILE"
|
error "Failed to remove old $DISK_DESC image $SOURCE_FILE."
|
||||||
error "Failed to move converted $DISK_DESC image to $DST_FILE."
|
|
||||||
exit 79
|
exit 79
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -635,8 +633,7 @@ addDisk () {
|
|||||||
|
|
||||||
if [ -f "$DISK_FILE" ]; then
|
if [ -f "$DISK_FILE" ]; then
|
||||||
if ! setOwner "$DISK_FILE"; then
|
if ! setOwner "$DISK_FILE"; then
|
||||||
error "Failed to set the owner for \"$DISK_FILE\" !"
|
warn "failed to set the owner for \"$DISK_FILE\" !"
|
||||||
exit 77
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+43
-18
@@ -36,7 +36,7 @@ if [ -n "$URL" ] && [ ! -s "$FILE" ] && [ ! -d "$DIR" ]; then
|
|||||||
printf -v BASE '%b' "${BASE//%/\\x}"
|
printf -v BASE '%b' "${BASE//%/\\x}"
|
||||||
BASE="${BASE//[!A-Za-z0-9._-]/_}"
|
BASE="${BASE//[!A-Za-z0-9._-]/_}"
|
||||||
fi
|
fi
|
||||||
if [[ "${URL,,}" != "http"* && "${URL,,}" != "file:"* ]] ; then
|
if [[ "${URL,,}" != "http"* && "${URL,,}" != "file:"* ]]; then
|
||||||
[ ! -s "$STORAGE/$BASE.pat" ] && error "Invalid URL: $URL" && exit 65
|
[ ! -s "$STORAGE/$BASE.pat" ] && error "Invalid URL: $URL" && exit 65
|
||||||
URL="file://$STORAGE/$BASE.pat"
|
URL="file://$STORAGE/$BASE.pat"
|
||||||
fi
|
fi
|
||||||
@@ -159,20 +159,44 @@ if [[ "$URL" == "file://"* ]]; then
|
|||||||
else
|
else
|
||||||
|
|
||||||
SIZE=0
|
SIZE=0
|
||||||
|
REASON=""
|
||||||
|
LOG=$(mktemp)
|
||||||
|
|
||||||
[[ "${URL,,}" == *"_72806.pat" ]] && SIZE=361010261
|
[[ "${URL,,}" == *"_72806.pat" ]] && SIZE=361010261
|
||||||
[[ "${URL,,}" == *"_69057.pat" ]] && SIZE=363837333
|
[[ "${URL,,}" == *"_69057.pat" ]] && SIZE=363837333
|
||||||
[[ "${URL,,}" == *"_42218.pat" ]] && SIZE=379637760
|
[[ "${URL,,}" == *"_42218.pat" ]] && SIZE=379637760
|
||||||
|
|
||||||
/run/progress.sh "$PAT" "$SIZE" "$MSG ([P])..." &
|
/run/progress.sh "$PAT" "$SIZE" "$MSG ([P])..." &
|
||||||
|
|
||||||
{ wget "$URL" -O "$PAT" -q --no-check-certificate --timeout=10 --no-http-keep-alive --show-progress "$PROGRESS"; rc=$?; } || :
|
{
|
||||||
|
LC_ALL=C wget "$URL" -O "$PAT" --no-verbose --no-check-certificate \
|
||||||
|
--timeout=30 --no-http-keep-alive --show-progress "$PROGRESS" \
|
||||||
|
--output-file="$LOG"
|
||||||
|
rc=$?
|
||||||
|
} || :
|
||||||
|
|
||||||
fKill "progress.sh"
|
fKill "progress.sh"
|
||||||
|
|
||||||
(( rc == 3 )) && error "$ERR , cannot write file (disk full?)" && exit 69
|
if (( rc != 0 )); then
|
||||||
(( rc == 4 )) && error "$ERR , network failure!" && exit 69
|
REASON=$(sed -n \
|
||||||
(( rc == 8 )) && error "$ERR , server issued an error response!" && exit 69
|
-e 's/^wget: //p' \
|
||||||
(( rc != 0 )) && error "$ERR , reason: $rc" && exit 69
|
-e 's/^[0-9-]\{10\} [0-9:]\{8\} ERROR //p' \
|
||||||
|
"$LOG" | tail -n 1)
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$LOG"
|
||||||
|
|
||||||
|
if (( rc == 3 )); then
|
||||||
|
error "$ERR because the file could not be written (disk full?)."
|
||||||
|
exit 69
|
||||||
|
elif (( rc != 0 )); then
|
||||||
|
if [ -n "$REASON" ]; then
|
||||||
|
error "$ERR: ${REASON%.}."
|
||||||
|
else
|
||||||
|
error "$ERR with exit status $rc."
|
||||||
|
fi
|
||||||
|
exit 69
|
||||||
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -228,7 +252,7 @@ if ! touch "$SYSTEM"; then
|
|||||||
error "Could not create file $SYSTEM for the system disk." && exit 98
|
error "Could not create file $SYSTEM for the system disk." && exit 98
|
||||||
fi
|
fi
|
||||||
|
|
||||||
! setOwner "$SYSTEM" && error "Failed to set the owner for \"$SYSTEM\" !"
|
! setOwner "$SYSTEM" && warn "failed to set the owner for \"$SYSTEM\" !"
|
||||||
|
|
||||||
if [[ "${FS,,}" == "btrfs" ]]; then
|
if [[ "${FS,,}" == "btrfs" ]]; then
|
||||||
{ chattr +C "$SYSTEM"; } || :
|
{ chattr +C "$SYSTEM"; } || :
|
||||||
@@ -249,14 +273,15 @@ fi
|
|||||||
|
|
||||||
PART="$TMP/partition.fdisk"
|
PART="$TMP/partition.fdisk"
|
||||||
|
|
||||||
{ echo "label: dos"
|
{
|
||||||
echo "label-id: 0x6f9ee2e9"
|
echo "label: dos"
|
||||||
echo "device: $SYSTEM"
|
echo "label-id: 0x6f9ee2e9"
|
||||||
echo "unit: sectors"
|
echo "device: $SYSTEM"
|
||||||
echo "sector-size: 512"
|
echo "unit: sectors"
|
||||||
echo ""
|
echo "sector-size: 512"
|
||||||
echo "${SYSTEM}1 : start= 2048, size= 16777216, type=83"
|
echo ""
|
||||||
echo "${SYSTEM}2 : start= 16779264, size= 4194304, type=82"
|
echo "${SYSTEM}1 : start= 2048, size= 16777216, type=83"
|
||||||
|
echo "${SYSTEM}2 : start= 16779264, size= 4194304, type=82"
|
||||||
} > "$PART"
|
} > "$PART"
|
||||||
|
|
||||||
sfdisk -q "$SYSTEM" < "$PART"
|
sfdisk -q "$SYSTEM" < "$PART"
|
||||||
@@ -302,7 +327,7 @@ fakeroot -- bash -c "set -Eeu;\
|
|||||||
|
|
||||||
rm -rf "$MOUNT"
|
rm -rf "$MOUNT"
|
||||||
echo "$BASE" > "$STORAGE/dsm.ver"
|
echo "$BASE" > "$STORAGE/dsm.ver"
|
||||||
! setOwner "$STORAGE/dsm.ver" && error "Failed to set the owner for \"$STORAGE/dsm.ver\" !"
|
! setOwner "$STORAGE/dsm.ver" && warn "failed to set the owner for \"$STORAGE/dsm.ver\" !"
|
||||||
|
|
||||||
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
|
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
|
||||||
rm -f "$PAT"
|
rm -f "$PAT"
|
||||||
@@ -311,11 +336,11 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f "$STORAGE/$BASE.pat" ]; then
|
if [ -f "$STORAGE/$BASE.pat" ]; then
|
||||||
! setOwner "$STORAGE/$BASE.pat" && error "Failed to set the owner for \"$STORAGE/$BASE.pat\" !"
|
! setOwner "$STORAGE/$BASE.pat" && warn "failed to set the owner for \"$STORAGE/$BASE.pat\" !"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
|
mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
|
||||||
! setOwner "$STORAGE/$BASE.boot.img" && error "Failed to set the owner for \"$STORAGE/$BASE.boot.img\" !"
|
! setOwner "$STORAGE/$BASE.boot.img" && warn "failed to set the owner for \"$STORAGE/$BASE.boot.img\" !"
|
||||||
|
|
||||||
rm -rf "$TMP"
|
rm -rf "$TMP"
|
||||||
|
|
||||||
|
|||||||
+9
-8
@@ -4,11 +4,12 @@ set -Eeuo pipefail
|
|||||||
msg="Checking memory..."
|
msg="Checking memory..."
|
||||||
enabled "$DEBUG" && echo "$msg"
|
enabled "$DEBUG" && echo "$msg"
|
||||||
|
|
||||||
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
app() {
|
||||||
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
|
echo "Virtual DSM"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
checkConfiguredMemory() {
|
checkConfiguredMemory() {
|
||||||
|
|
||||||
local wanted msg
|
local wanted msg
|
||||||
|
|
||||||
if disabled "$RAM_CHECK" || [[ "${RAM_SIZE,,}" == "max" || "${RAM_SIZE,,}" == "half" ]]; then
|
if disabled "$RAM_CHECK" || [[ "${RAM_SIZE,,}" == "max" || "${RAM_SIZE,,}" == "half" ]]; then
|
||||||
@@ -40,7 +41,6 @@ checkConfiguredMemory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configureHalfMemory() {
|
configureHalfMemory() {
|
||||||
|
|
||||||
local wanted
|
local wanted
|
||||||
|
|
||||||
if [[ "${RAM_SIZE,,}" != "half" ]]; then
|
if [[ "${RAM_SIZE,,}" != "half" ]]; then
|
||||||
@@ -50,7 +50,7 @@ configureHalfMemory() {
|
|||||||
if (( (RAM_AVAIL / 2) > RAM_SPARE )); then
|
if (( (RAM_AVAIL / 2) > RAM_SPARE )); then
|
||||||
wanted=$(( (RAM_AVAIL / 2) / 1048577 ))
|
wanted=$(( (RAM_AVAIL / 2) / 1048577 ))
|
||||||
RAM_SIZE="${wanted}M"
|
RAM_SIZE="${wanted}M"
|
||||||
info "Allocated $wanted MB of RAM for the virtual machine."
|
info "Allocated $wanted MB of RAM for $(app)."
|
||||||
else
|
else
|
||||||
RAM_SIZE="max"
|
RAM_SIZE="max"
|
||||||
fi
|
fi
|
||||||
@@ -59,7 +59,6 @@ configureHalfMemory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configureMaxMemory() {
|
configureMaxMemory() {
|
||||||
|
|
||||||
local wanted
|
local wanted
|
||||||
|
|
||||||
if [[ "${RAM_SIZE,,}" != "max" ]]; then
|
if [[ "${RAM_SIZE,,}" != "max" ]]; then
|
||||||
@@ -83,13 +82,12 @@ configureMaxMemory() {
|
|||||||
wanted=$(( wanted / 1048577 ))
|
wanted=$(( wanted / 1048577 ))
|
||||||
RAM_SIZE="${wanted}M"
|
RAM_SIZE="${wanted}M"
|
||||||
|
|
||||||
info "Allocated $wanted MB of RAM for the virtual machine."
|
info "Allocated $wanted MB of RAM for $(app)."
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
checkMinimumMemory() {
|
checkMinimumMemory() {
|
||||||
|
|
||||||
local wanted
|
local wanted
|
||||||
|
|
||||||
wanted=$(numfmt --from=iec "$RAM_SIZE")
|
wanted=$(numfmt --from=iec "$RAM_SIZE")
|
||||||
@@ -103,6 +101,9 @@ checkMinimumMemory() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMemoryInfo
|
||||||
|
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
|
||||||
|
|
||||||
checkConfiguredMemory
|
checkConfiguredMemory
|
||||||
configureHalfMemory
|
configureHalfMemory
|
||||||
configureMaxMemory
|
configureMaxMemory
|
||||||
|
|||||||
+270
-124
@@ -126,6 +126,11 @@ maskToCIDR() {
|
|||||||
local mask="$1"
|
local mask="$1"
|
||||||
local prefix=""
|
local prefix=""
|
||||||
|
|
||||||
|
if ! command -v ipcalc > /dev/null 2>&1; then
|
||||||
|
error "Required command 'ipcalc' is not installed!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
prefix=$(ipcalc -n -b "0.0.0.0/$mask" 2>/dev/null | awk '
|
prefix=$(ipcalc -n -b "0.0.0.0/$mask" 2>/dev/null | awk '
|
||||||
/^Netmask:/ {
|
/^Netmask:/ {
|
||||||
for (i = 1; i <= NF; i++) {
|
for (i = 1; i <= NF; i++) {
|
||||||
@@ -137,7 +142,7 @@ maskToCIDR() {
|
|||||||
}
|
}
|
||||||
')
|
')
|
||||||
|
|
||||||
if [[ ! "$prefix" =~ ^[0-9]+$ ]] || (( prefix < 1 || prefix > 30 )); then
|
if [[ ! "$prefix" =~ ^[0-9]+$ ]] || (( prefix < 0 || prefix > 32 )); then
|
||||||
error "Invalid MASK: '$mask'"
|
error "Invalid MASK: '$mask'"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -365,9 +370,6 @@ configureDNS() {
|
|||||||
arguments+=" --interface=$fa"
|
arguments+=" --interface=$fa"
|
||||||
arguments+=" --bind-interfaces"
|
arguments+=" --bind-interfaces"
|
||||||
|
|
||||||
# Set pid file
|
|
||||||
arguments+=" --pid-file=$DNSMASQ_PID"
|
|
||||||
|
|
||||||
# Workaround NET_RAW capability
|
# Workaround NET_RAW capability
|
||||||
arguments+=" --no-ping"
|
arguments+=" --no-ping"
|
||||||
|
|
||||||
@@ -382,6 +384,9 @@ configureDNS() {
|
|||||||
# Set local dns resolver to dnsmasq when needed
|
# Set local dns resolver to dnsmasq when needed
|
||||||
[ -f /etc/resolv.dnsmasq ] && arguments+=" --resolv-file=/etc/resolv.dnsmasq"
|
[ -f /etc/resolv.dnsmasq ] && arguments+=" --resolv-file=/etc/resolv.dnsmasq"
|
||||||
|
|
||||||
|
# Set pid file
|
||||||
|
arguments+=" --pid-file=$DNSMASQ_PID"
|
||||||
|
|
||||||
# Enable logging to file
|
# Enable logging to file
|
||||||
local log="/var/log/dnsmasq.log"
|
local log="/var/log/dnsmasq.log"
|
||||||
rm -f "$log"
|
rm -f "$log"
|
||||||
@@ -413,37 +418,38 @@ configureDNS() {
|
|||||||
|
|
||||||
getHostPorts() {
|
getHostPorts() {
|
||||||
|
|
||||||
local ports=""
|
|
||||||
local port=""
|
|
||||||
local num=""
|
local num=""
|
||||||
local proto=""
|
local port=""
|
||||||
|
local ports=""
|
||||||
local mode="${1:-tcp}"
|
local mode="${1:-tcp}"
|
||||||
local list="${HOST_PORTS// /},"
|
local list="${HOST_PORTS// /},"
|
||||||
|
|
||||||
for port in ${list//,/ }; do
|
for port in ${list//,/ }; do
|
||||||
|
|
||||||
proto="tcp"
|
[ -z "$port" ] && continue
|
||||||
num="$port"
|
|
||||||
|
|
||||||
if [[ "$port" == *"/udp" ]]; then
|
|
||||||
proto="udp"
|
|
||||||
num="${port%/udp}"
|
|
||||||
elif [[ "$port" == *"/tcp" ]]; then
|
|
||||||
proto="tcp"
|
|
||||||
num="${port%/tcp}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ -z "$num" ] && continue
|
|
||||||
|
|
||||||
case "$mode" in
|
case "$mode" in
|
||||||
"all" )
|
|
||||||
ports+="$num/$proto," ;;
|
|
||||||
"tcp" )
|
"tcp" )
|
||||||
[[ "$proto" == "tcp" ]] && ports+="$num," ;;
|
[[ "$port" == *"/udp" ]] && continue
|
||||||
"udp" )
|
num="${port%/tcp}"
|
||||||
[[ "$proto" == "udp" ]] && ports+="$num," ;;
|
;;
|
||||||
|
"all" )
|
||||||
|
if [[ "$port" == *"/udp" ]]; then
|
||||||
|
num="${port%/udp}"
|
||||||
|
[ -n "$num" ] && ports+="$num/udp,"
|
||||||
|
else
|
||||||
|
num="${port%/tcp}"
|
||||||
|
[ -n "$num" ] && ports+="$num/tcp,"
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
[ -n "$num" ] && ports+="$num,"
|
||||||
|
|
||||||
done
|
done
|
||||||
|
|
||||||
# Remove duplicates
|
# Remove duplicates
|
||||||
@@ -455,21 +461,18 @@ getHostPorts() {
|
|||||||
|
|
||||||
getUserPorts() {
|
getUserPorts() {
|
||||||
|
|
||||||
local ssh="22/tcp"
|
local defaults="22/tcp,5000/tcp,5001/tcp"
|
||||||
local dsm="5000/tcp,5001/tcp"
|
local list="$defaults,${USER_PORTS// /},"
|
||||||
|
|
||||||
local list="$ssh,$dsm,"
|
local num=""
|
||||||
list+="${USER_PORTS// /},"
|
local ports=""
|
||||||
|
local proto=""
|
||||||
|
local userport=""
|
||||||
|
local hostport=""
|
||||||
|
|
||||||
local exclude=""
|
local exclude=""
|
||||||
exclude=$(getHostPorts "all")
|
exclude=$(getHostPorts "all")
|
||||||
|
|
||||||
local ports=""
|
|
||||||
local userport=""
|
|
||||||
local hostport=""
|
|
||||||
local proto=""
|
|
||||||
local num=""
|
|
||||||
|
|
||||||
for userport in ${list//,/ }; do
|
for userport in ${list//,/ }; do
|
||||||
|
|
||||||
proto="tcp"
|
proto="tcp"
|
||||||
@@ -488,6 +491,7 @@ getUserPorts() {
|
|||||||
for hostport in ${exclude//,/ }; do
|
for hostport in ${exclude//,/ }; do
|
||||||
|
|
||||||
if [[ "$num/$proto" == "$hostport" ]]; then
|
if [[ "$num/$proto" == "$hostport" ]]; then
|
||||||
|
|
||||||
num=""
|
num=""
|
||||||
|
|
||||||
if [[ "$hostport" != "${WEB_PORT:-}/tcp" ]]; then
|
if [[ "$hostport" != "${WEB_PORT:-}/tcp" ]]; then
|
||||||
@@ -505,6 +509,7 @@ getUserPorts() {
|
|||||||
|
|
||||||
# Remove duplicates
|
# Remove duplicates
|
||||||
echo "${ports//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g'
|
echo "${ports//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g'
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -808,14 +813,16 @@ configurePasst() {
|
|||||||
createBridge() {
|
createBridge() {
|
||||||
|
|
||||||
local gateway="$1"
|
local gateway="$1"
|
||||||
local rc
|
local rc msg=""
|
||||||
|
|
||||||
# Create a bridge with a static IP for the VM guest
|
# Create a bridge with a static IP for the VM guest
|
||||||
{ ip link add dev "$BRIDGE" type bridge; rc=$?; } || :
|
{ msg=$(ip link add dev "$BRIDGE" type bridge 2>&1); rc=$?; } || :
|
||||||
|
|
||||||
if (( rc != 0 )); then
|
if (( rc != 0 )); then
|
||||||
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
|
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
|
||||||
warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
|
[ -n "$msg" ] && echo "$msg" >&2
|
||||||
|
warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$GUEST_MTU" != "0" ]]; then
|
if [[ "$GUEST_MTU" != "0" ]]; then
|
||||||
@@ -840,11 +847,16 @@ createBridge() {
|
|||||||
createTap() {
|
createTap() {
|
||||||
|
|
||||||
local tuntap="$1"
|
local tuntap="$1"
|
||||||
|
local rc msg=""
|
||||||
|
|
||||||
# Set tap to the bridge created
|
# Set tap to the bridge created
|
||||||
if ! ip tuntap add dev "$TAP" mode tap; then
|
{ msg=$(ip tuntap add dev "$TAP" mode tap 2>&1); rc=$?; } || :
|
||||||
|
|
||||||
|
if (( rc != 0 )); then
|
||||||
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
|
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
|
||||||
warn "$tuntap" && return 1
|
[ -n "$msg" ] && echo "$msg" >&2
|
||||||
|
warn "$tuntap"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$GUEST_MTU" != "0" ]]; then
|
if [[ "$GUEST_MTU" != "0" ]]; then
|
||||||
@@ -870,11 +882,80 @@ createTap() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showRules() {
|
||||||
|
|
||||||
|
local table="$1"
|
||||||
|
local chain="$2"
|
||||||
|
local label="$3"
|
||||||
|
local rules=""
|
||||||
|
|
||||||
|
rules=$(iptables -t "$table" -S "$chain" 2>/dev/null |
|
||||||
|
awk '$1 == "-A"' || true)
|
||||||
|
|
||||||
|
[ -n "$rules" ] || return 0
|
||||||
|
|
||||||
|
printf "Existing %s rules:\n\n%s\n\n" "$label" "$rules"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
checkExistingTables() {
|
||||||
|
|
||||||
|
local msg=""
|
||||||
|
local rules=""
|
||||||
|
local conflicts=""
|
||||||
|
|
||||||
|
rules=$(iptables -t nat -S PREROUTING 2>/dev/null |
|
||||||
|
awk '$1 == "-A"' || true)
|
||||||
|
|
||||||
|
conflicts=$(grep -E -- \
|
||||||
|
'^-A PREROUTING .*(-j DNAT|-j REDIRECT)( |$)' \
|
||||||
|
<<< "$rules" || true)
|
||||||
|
|
||||||
|
if [ -n "$conflicts" ]; then
|
||||||
|
msg="your existing NAT rules may take precedence over VM port forwarding"
|
||||||
|
|
||||||
|
if enabled "$DEBUG"; then
|
||||||
|
warn "${msg}."
|
||||||
|
else
|
||||||
|
warn "${msg}; enable DEBUG=Y to inspect them."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rules=$(iptables -t filter -S FORWARD 2>/dev/null |
|
||||||
|
awk '$1 == "-A"' || true)
|
||||||
|
|
||||||
|
conflicts=$(grep -E -- \
|
||||||
|
'^-A FORWARD .*(-j DROP|-j REJECT)( |$)' \
|
||||||
|
<<< "$rules" || true)
|
||||||
|
|
||||||
|
if [ -n "$conflicts" ]; then
|
||||||
|
msg="your existing firewall rules may block traffic forwarded to or from the VM"
|
||||||
|
|
||||||
|
if enabled "$DEBUG"; then
|
||||||
|
warn "${msg}."
|
||||||
|
else
|
||||||
|
warn "${msg}; enable DEBUG=Y to inspect them."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if enabled "$DEBUG"; then
|
||||||
|
showRules nat PREROUTING "NAT PREROUTING"
|
||||||
|
showRules filter FORWARD "filter FORWARD"
|
||||||
|
showRules nat POSTROUTING "NAT POSTROUTING"
|
||||||
|
showRules mangle FORWARD "mangle FORWARD"
|
||||||
|
showRules mangle POSTROUTING "mangle POSTROUTING"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
configureTables() {
|
configureTables() {
|
||||||
|
|
||||||
local ip="$1"
|
local ip="$1"
|
||||||
local subnet="$2"
|
local subnet="$2"
|
||||||
local exclude="$3"
|
local exclude=""
|
||||||
|
local port=""
|
||||||
|
local dnat_chain="QEMU_DNAT"
|
||||||
local rule_tag="remove"
|
local rule_tag="remove"
|
||||||
local tables_err="failed to configure IP tables!"
|
local tables_err="failed to configure IP tables!"
|
||||||
local tables="the 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
|
local tables="the 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
|
||||||
@@ -885,53 +966,71 @@ configureTables() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$exclude" ]; then
|
checkExistingTables
|
||||||
if [[ "$exclude" != *","* ]]; then
|
exclude=$(getHostPorts)
|
||||||
exclude=" ! --dport $exclude"
|
|
||||||
else
|
|
||||||
exclude=" -m multiport ! --dports $exclude"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# NAT traffic from bridge subnet to Docker uplink
|
# NAT traffic from the VM subnet leaving through any external interface.
|
||||||
if ! iptables -t nat -A POSTROUTING \
|
if ! iptables -t nat -A POSTROUTING \
|
||||||
-o "$DEV" \
|
! -o "$BRIDGE" \
|
||||||
-s "$subnet" \
|
-s "$subnet" \
|
||||||
! -d "$subnet" \
|
! -d "$subnet" \
|
||||||
-m comment --comment "$rule_tag" \
|
-m comment --comment "$rule_tag" \
|
||||||
-j MASQUERADE > /dev/null 2>&1; then
|
-j MASQUERADE > /dev/null 2>&1; then
|
||||||
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
|
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
|
||||||
|
|
||||||
if ! iptables -t nat -A POSTROUTING \
|
if ! iptables -t nat -A POSTROUTING \
|
||||||
-o "$DEV" \
|
! -o "$BRIDGE" \
|
||||||
-s "$subnet" \
|
-s "$subnet" \
|
||||||
! -d "$subnet" \
|
! -d "$subnet" \
|
||||||
-m comment --comment "$rule_tag" \
|
-m comment --comment "$rule_tag" \
|
||||||
-j MASQUERADE; then
|
-j MASQUERADE; then
|
||||||
warn "$tables" && return 1
|
warn "$tables"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
# Use a dedicated chain so protected TCP ports do not depend on multiport support.
|
||||||
if ! iptables -t nat -A PREROUTING \
|
if ! iptables -t nat -N "$dnat_chain"; then
|
||||||
-i "$DEV" \
|
warn "$tables_err"
|
||||||
-d "$UPLINK" \
|
return 1
|
||||||
-p tcp${exclude} \
|
|
||||||
-m comment --comment "$rule_tag" \
|
|
||||||
-j DNAT --to "$ip"; then
|
|
||||||
warn "$tables_err" && return 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! iptables -t nat -A PREROUTING \
|
# Keep container-owned TCP ports handled by the container.
|
||||||
-i "$DEV" \
|
for port in ${exclude//,/ }; do
|
||||||
-d "$UPLINK" \
|
|
||||||
-p udp \
|
[ -z "$port" ] && continue
|
||||||
|
|
||||||
|
if ! iptables -t nat -A "$dnat_chain" \
|
||||||
|
-p tcp \
|
||||||
|
--dport "$port" \
|
||||||
|
-m comment --comment "$rule_tag" \
|
||||||
|
-j RETURN; then
|
||||||
|
warn "$tables_err"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
# Forward every remaining protocol and port to the VM.
|
||||||
|
if ! iptables -t nat -A "$dnat_chain" \
|
||||||
-m comment --comment "$rule_tag" \
|
-m comment --comment "$rule_tag" \
|
||||||
-j DNAT --to "$ip"; then
|
-j DNAT --to "$ip"; then
|
||||||
warn "$tables_err" && return 1
|
warn "$tables_err"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Process incoming traffic addressed to the container through the VM chain.
|
||||||
|
if ! iptables -t nat -A PREROUTING \
|
||||||
|
! -i "$BRIDGE" \
|
||||||
|
-m addrtype --dst-type LOCAL \
|
||||||
|
-m comment --comment "$rule_tag" \
|
||||||
|
-j "$dnat_chain"; then
|
||||||
|
warn "$tables_err"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if (( KERNEL > 4 )); then
|
if (( KERNEL > 4 )); then
|
||||||
# Hack for guest VMs complaining about "bad udp checksums in 5 packets"
|
# Hack for guest VMs complaining about "bad udp checksums in 5 packets".
|
||||||
iptables -t mangle -A POSTROUTING \
|
iptables -t mangle -A POSTROUTING \
|
||||||
-s "$subnet" \
|
-s "$subnet" \
|
||||||
-p udp \
|
-p udp \
|
||||||
@@ -955,22 +1054,26 @@ configureTables() {
|
|||||||
-m comment --comment "$rule_tag" \
|
-m comment --comment "$rule_tag" \
|
||||||
-j TCPMSS --clamp-mss-to-pmtu > /dev/null 2>&1 || true
|
-j TCPMSS --clamp-mss-to-pmtu > /dev/null 2>&1 || true
|
||||||
|
|
||||||
# Allow forwarding from bridge -> dev
|
# Allow forwarding from the VM bridge to external interfaces.
|
||||||
if ! iptables -A FORWARD \
|
if ! iptables -A FORWARD \
|
||||||
-i "$BRIDGE" \
|
-i "$BRIDGE" \
|
||||||
-o "$DEV" \
|
! -o "$BRIDGE" \
|
||||||
|
-s "$subnet" \
|
||||||
-m comment --comment "$rule_tag" \
|
-m comment --comment "$rule_tag" \
|
||||||
-j ACCEPT; then
|
-j ACCEPT; then
|
||||||
warn "$tables_err" && return 1
|
warn "$tables_err"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Allow forwarding from dev -> guest
|
# Allow forwarding from external interfaces to the VM.
|
||||||
if ! iptables -A FORWARD \
|
if ! iptables -A FORWARD \
|
||||||
-i "$DEV" \
|
! -i "$BRIDGE" \
|
||||||
-o "$BRIDGE" \
|
-o "$BRIDGE" \
|
||||||
|
-d "$ip" \
|
||||||
-m comment --comment "$rule_tag" \
|
-m comment --comment "$rule_tag" \
|
||||||
-j ACCEPT; then
|
-j ACCEPT; then
|
||||||
warn "$tables_err" && return 1
|
warn "$tables_err"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@@ -979,15 +1082,21 @@ configureTables() {
|
|||||||
configureNAT() {
|
configureNAT() {
|
||||||
|
|
||||||
local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun"
|
local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun"
|
||||||
local rc
|
local msg=""
|
||||||
|
local rc ip subnet forwarding=""
|
||||||
|
|
||||||
enabled "$DEBUG" && echo "Configuring NAT networking..."
|
enabled "$DEBUG" && echo "Configuring NAT networking..."
|
||||||
|
|
||||||
# Create the necessary file structure for /dev/net/tun
|
# Create the necessary file structure for /dev/net/tun
|
||||||
if [ ! -c /dev/net/tun ]; then
|
if [ ! -c /dev/net/tun ]; then
|
||||||
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
|
[ ! -d /dev/net ] && mkdir -m 755 /dev/net > /dev/null 2>&1 || :
|
||||||
if mknod /dev/net/tun c 10 200; then
|
|
||||||
|
{ msg=$(mknod /dev/net/tun c 10 200 2>&1); rc=$?; } || :
|
||||||
|
|
||||||
|
if (( rc == 0 )); then
|
||||||
chmod 666 /dev/net/tun
|
chmod 666 /dev/net/tun
|
||||||
|
elif ! enabled "$ROOTLESS" || enabled "$DEBUG"; then
|
||||||
|
[ -n "$msg" ] && echo "$msg" >&2
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -997,7 +1106,10 @@ configureNAT() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check port forwarding flag
|
# Check port forwarding flag
|
||||||
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
[ -r /proc/sys/net/ipv4/ip_forward ] &&
|
||||||
|
forwarding=$(< /proc/sys/net/ipv4/ip_forward)
|
||||||
|
|
||||||
|
if [[ "$forwarding" != "1" ]]; then
|
||||||
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
|
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
|
||||||
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
||||||
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
|
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
|
||||||
@@ -1006,8 +1118,6 @@ configureNAT() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local ip exclude subnet
|
|
||||||
|
|
||||||
if [ -n "$IP" ]; then
|
if [ -n "$IP" ]; then
|
||||||
ip=$(guestIP "$IP" 2)
|
ip=$(guestIP "$IP" 2)
|
||||||
else
|
else
|
||||||
@@ -1030,8 +1140,7 @@ configureNAT() {
|
|||||||
GUEST_MTU=$(minMTU "$GUEST_MTU" "$(getMTU "$BRIDGE")" "$(getMTU "$TAP")")
|
GUEST_MTU=$(minMTU "$GUEST_MTU" "$(getMTU "$BRIDGE")" "$(getMTU "$TAP")")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exclude=$(getHostPorts)
|
configureTables "$ip" "$subnet" || return 1
|
||||||
configureTables "$ip" "$subnet" "$exclude" || return 1
|
|
||||||
|
|
||||||
NET_OPTS="-netdev tap,id=hostnet0,ifname=$TAP"
|
NET_OPTS="-netdev tap,id=hostnet0,ifname=$TAP"
|
||||||
|
|
||||||
@@ -1065,9 +1174,13 @@ setTables() {
|
|||||||
|
|
||||||
testTables() {
|
testTables() {
|
||||||
|
|
||||||
# Test actual ruleset access instead of only checking the binary version.
|
local table=""
|
||||||
iptables -w -t nat -S > /dev/null 2>&1 || return 1
|
|
||||||
iptables-save -t nat > /dev/null 2>&1 || return 1
|
# Test every table required by the networking rules.
|
||||||
|
for table in nat filter; do
|
||||||
|
iptables -t "$table" -S > /dev/null 2>&1 || return 1
|
||||||
|
iptables-save -t "$table" > /dev/null 2>&1 || return 1
|
||||||
|
done
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -1075,13 +1188,24 @@ testTables() {
|
|||||||
selectTables() {
|
selectTables() {
|
||||||
|
|
||||||
local mode=""
|
local mode=""
|
||||||
|
local current=""
|
||||||
local modes=()
|
local modes=()
|
||||||
|
|
||||||
# Prefer nftables for Podman namespaces, but retain legacy first for Docker.
|
# Keep the currently selected backend when it is fully functional.
|
||||||
if [[ "${ENGINE,,}" == "podman" ]]; then
|
if testTables; then
|
||||||
modes=( "nft" "legacy" )
|
return 0
|
||||||
else
|
fi
|
||||||
|
|
||||||
|
current=$(iptables --version 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [[ "$current" == *"nf_tables"* ]]; then
|
||||||
|
modes=( "legacy" )
|
||||||
|
elif [[ "$current" == *"legacy"* ]]; then
|
||||||
|
modes=( "nft" )
|
||||||
|
elif [[ "${ENGINE,,}" == "docker" ]]; then
|
||||||
modes=( "legacy" "nft" )
|
modes=( "legacy" "nft" )
|
||||||
|
else
|
||||||
|
modes=( "nft" "legacy" )
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for mode in "${modes[@]}"; do
|
for mode in "${modes[@]}"; do
|
||||||
@@ -1100,6 +1224,7 @@ clearTables() {
|
|||||||
local line=""
|
local line=""
|
||||||
local rules=""
|
local rules=""
|
||||||
local failed="N"
|
local failed="N"
|
||||||
|
local dnat_chain="QEMU_DNAT"
|
||||||
local rule_tag="remove"
|
local rule_tag="remove"
|
||||||
local re="--comment[[:space:]]+\"?$rule_tag\"?([[:space:]]|\$)"
|
local re="--comment[[:space:]]+\"?$rule_tag\"?([[:space:]]|\$)"
|
||||||
|
|
||||||
@@ -1107,30 +1232,46 @@ clearTables() {
|
|||||||
|
|
||||||
# Store the current iptables ruleset.
|
# Store the current iptables ruleset.
|
||||||
! rules=$(iptables-save 2> /dev/null) && return 1
|
! rules=$(iptables-save 2> /dev/null) && return 1
|
||||||
[ -z "$rules" ] && return 0
|
|
||||||
|
|
||||||
# Delete every rule tagged with our unique identifier,
|
if [ -n "$rules" ]; then
|
||||||
# leaving all other rules intact.
|
|
||||||
while IFS= read -r line; do
|
|
||||||
|
|
||||||
case "$line" in
|
# Delete every rule tagged with our unique identifier,
|
||||||
\*nat ) table="nat" ;;
|
# leaving all other rules intact.
|
||||||
\*filter ) table="filter" ;;
|
while IFS= read -r line; do
|
||||||
\*mangle ) table="mangle" ;;
|
|
||||||
\*raw ) table="raw" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [[ "$line" == -A* ]] && [[ "$line" =~ $re ]]; then
|
case "$line" in
|
||||||
line="${line/-A /-D }"
|
\*nat ) table="nat" ;;
|
||||||
|
\*filter ) table="filter" ;;
|
||||||
|
\*mangle ) table="mangle" ;;
|
||||||
|
\*raw ) table="raw" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
# Parse the quoting produced by iptables-save before deleting the rule.
|
if [[ "$line" == -A* ]] && [[ "$line" =~ $re ]]; then
|
||||||
if ! printf '%s\n' "$line" |
|
line="${line/-A /-D }"
|
||||||
xargs -r iptables -t "$table" > /dev/null 2>&1; then
|
|
||||||
failed="Y"
|
# Parse the quoting produced by iptables-save before deleting the rule.
|
||||||
|
if ! printf '%s\n' "$line" |
|
||||||
|
xargs -r iptables -t "$table" > /dev/null 2>&1; then
|
||||||
|
failed="Y"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
done <<< "$rules"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove the dedicated DNAT chain after deleting its rules and references.
|
||||||
|
if iptables -t nat -S "$dnat_chain" > /dev/null 2>&1; then
|
||||||
|
|
||||||
|
if ! iptables -t nat -F "$dnat_chain" > /dev/null 2>&1; then
|
||||||
|
failed="Y"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
done <<< "$rules"
|
if ! iptables -t nat -X "$dnat_chain" > /dev/null 2>&1; then
|
||||||
|
failed="Y"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
enabled "$failed" && return 1
|
enabled "$failed" && return 1
|
||||||
return 0
|
return 0
|
||||||
@@ -1140,11 +1281,14 @@ clearTables() {
|
|||||||
# Cleanup
|
# Cleanup
|
||||||
# ######################################
|
# ######################################
|
||||||
|
|
||||||
closeBridge() {
|
closeInterfaces() {
|
||||||
|
|
||||||
local pids=( "$PASST_PID" "$DNSMASQ_PID" )
|
local pids=( "$PASST_PID" "$DNSMASQ_PID" )
|
||||||
mKill "${pids[@]}"
|
mKill "${pids[@]}"
|
||||||
|
|
||||||
|
exec 30>&- 2>/dev/null || true
|
||||||
|
exec 40>&- 2>/dev/null || true
|
||||||
|
|
||||||
ip link set "$TAP" down promisc off &> /dev/null || :
|
ip link set "$TAP" down promisc off &> /dev/null || :
|
||||||
ip link delete "$TAP" &> /dev/null || :
|
ip link delete "$TAP" &> /dev/null || :
|
||||||
|
|
||||||
@@ -1157,7 +1301,7 @@ closeBridge() {
|
|||||||
|
|
||||||
closeWeb() {
|
closeWeb() {
|
||||||
|
|
||||||
local pids=( "$WEB_PID" "$WSD_PID" )
|
local pids=( "${WEB_PID:-}" "${WSD_PID:-}" )
|
||||||
mKill "${pids[@]}"
|
mKill "${pids[@]}"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@@ -1171,17 +1315,14 @@ closeNetwork() {
|
|||||||
|
|
||||||
disabled "$NETWORK" && return 0
|
disabled "$NETWORK" && return 0
|
||||||
|
|
||||||
exec 30>&- 2>/dev/null || true
|
closeInterfaces
|
||||||
exec 40>&- 2>/dev/null || true
|
|
||||||
|
|
||||||
closeBridge
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanUp() {
|
cleanUp() {
|
||||||
|
|
||||||
closeBridge
|
closeInterfaces
|
||||||
|
|
||||||
# Clean up old files
|
# Clean up old files
|
||||||
rm -f "$PASST_PID" "$PASST_SOCKET"
|
rm -f "$PASST_PID" "$PASST_SOCKET"
|
||||||
@@ -1232,6 +1373,11 @@ validateMask() {
|
|||||||
|
|
||||||
PREFIX=$(maskToCIDR "$MASK") || exit 28
|
PREFIX=$(maskToCIDR "$MASK") || exit 28
|
||||||
|
|
||||||
|
if (( PREFIX < 1 || PREFIX > 24 )); then
|
||||||
|
error "Unsupported MASK: '$MASK' (supported range: /1 through /24)"
|
||||||
|
exit 28
|
||||||
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1350,23 +1496,26 @@ configureMAC() {
|
|||||||
container=$(containerID)
|
container=$(containerID)
|
||||||
|
|
||||||
if [ -z "$MAC" ]; then
|
if [ -z "$MAC" ]; then
|
||||||
|
|
||||||
file="$STORAGE/dsm.mac"
|
file="$STORAGE/dsm.mac"
|
||||||
[ -s "$file" ] && MAC=$(<"$file")
|
|
||||||
MAC="${MAC//[![:print:]]/}"
|
if [ -s "$file" ]; then
|
||||||
|
if ! MAC=$(readFile "$file"); then
|
||||||
|
error "Failed to read MAC address from \"$file\" !"
|
||||||
|
exit 28
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$MAC" ]; then
|
if [ -z "$MAC" ]; then
|
||||||
|
|
||||||
# Generate a Synology-style MAC address based on a stable container identifier when possible.
|
# Generate a Synology-style MAC address based on a stable container identifier when possible.
|
||||||
MAC=$(echo "$container" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/')
|
MAC=$(echo "$container" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/')
|
||||||
|
|
||||||
if ! echo "${MAC^^}" > "$file"; then
|
if ! writeFile "${MAC^^}" "$file"; then
|
||||||
error "Failed to write MAC address to \"$file\" !"
|
error "Failed to write MAC address to \"$file\" !"
|
||||||
exit 28
|
exit 28
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! setOwner "$file"; then
|
|
||||||
error "Failed to set the owner for \"$file\" !"
|
|
||||||
exit 28
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1383,8 +1532,7 @@ configureMAC() {
|
|||||||
exit 28
|
exit 28
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Keep the guest-facing gateway MAC stable across runs, otherwise Windows guests
|
# Keep the guest-facing gateway MAC stable across runs.
|
||||||
# may detect a new network every boot.
|
|
||||||
GATEWAY_MAC=$(gatewayMAC "$MAC")
|
GATEWAY_MAC=$(gatewayMAC "$MAC")
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@@ -1578,7 +1726,7 @@ else
|
|||||||
# Configure tap interface
|
# Configure tap interface
|
||||||
if ! configureNAT; then
|
if ! configureNAT; then
|
||||||
|
|
||||||
closeBridge
|
closeInterfaces
|
||||||
NETWORK="user"
|
NETWORK="user"
|
||||||
|
|
||||||
if ! enabled "$ROOTLESS" || enabled "$DEBUG"; then
|
if ! enabled "$ROOTLESS" || enabled "$DEBUG"; then
|
||||||
@@ -1620,10 +1768,8 @@ else
|
|||||||
|
|
||||||
showGuestInfo
|
showGuestInfo
|
||||||
|
|
||||||
if [[ "${NETWORK,,}" == "passt" || "${NETWORK,,}" == "slirp" ]]; then
|
if isUserMode && [ -z "$USER_PORTS" ]; then
|
||||||
if [ -z "$USER_PORTS" ]; then
|
info "Notice: because user-mode networking is active, when you need to forward custom ports to DSM, add them to the \"USER_PORTS\" variable."
|
||||||
info "Notice: because user-mode networking is active, when you need to forward custom ports to DSM, add them to the \"USER_PORTS\" variable."
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
+79
-6
@@ -25,12 +25,29 @@ detectEngine() {
|
|||||||
|
|
||||||
if [ -f "/run/.containerenv" ]; then
|
if [ -f "/run/.containerenv" ]; then
|
||||||
ENGINE="${container:-}"
|
ENGINE="${container:-}"
|
||||||
|
|
||||||
if [[ "${ENGINE,,}" == *"podman"* ]]; then
|
if [[ "${ENGINE,,}" == *"podman"* ]]; then
|
||||||
ROOTLESS="Y"
|
|
||||||
ENGINE="Podman"
|
ENGINE="Podman"
|
||||||
else
|
else
|
||||||
[ -z "$ENGINE" ] && ENGINE="Kubernetes"
|
[ -z "$ENGINE" ] && ENGINE="Kubernetes"
|
||||||
fi
|
fi
|
||||||
|
elif [ -f "/.dockerenv" ]; then
|
||||||
|
ENGINE="Docker"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
detectRootless() {
|
||||||
|
|
||||||
|
local uid_map=""
|
||||||
|
|
||||||
|
uid_map=$(awk '{$1=$1; print}' /proc/self/uid_map 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [[ "$uid_map" == "0 0 4294967295" ]]; then
|
||||||
|
ROOTLESS="N"
|
||||||
|
else
|
||||||
|
ROOTLESS="Y"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@@ -53,7 +70,6 @@ checkPrivileged() {
|
|||||||
max_cap=$(((1 << (last_cap + 1)) - 1))
|
max_cap=$(((1 << (last_cap + 1)) - 1))
|
||||||
|
|
||||||
if [ "$cap_bnd" -eq "$max_cap" ]; then
|
if [ "$cap_bnd" -eq "$max_cap" ]; then
|
||||||
ROOTLESS="N"
|
|
||||||
PRIVILEGED="Y"
|
PRIVILEGED="Y"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -123,11 +139,67 @@ checkFilesystem() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finiteMemoryLimit() {
|
||||||
|
|
||||||
|
local limit="$1"
|
||||||
|
local sentinel="4611686018427387904"
|
||||||
|
local i=0
|
||||||
|
local left=""
|
||||||
|
local right=""
|
||||||
|
|
||||||
|
[[ "$limit" =~ ^[0-9]+$ ]] || return 1
|
||||||
|
|
||||||
|
(( ${#limit} < ${#sentinel} )) && return 0
|
||||||
|
(( ${#limit} > ${#sentinel} )) && return 1
|
||||||
|
|
||||||
|
for (( i=0; i<${#sentinel}; i++ )); do
|
||||||
|
left="${limit:i:1}"
|
||||||
|
right="${sentinel:i:1}"
|
||||||
|
|
||||||
|
(( left < right )) && return 0
|
||||||
|
(( left > right )) && return 1
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
getMemoryInfo() {
|
||||||
|
|
||||||
|
local host_total=""
|
||||||
|
local host_avail=""
|
||||||
|
local limit=""
|
||||||
|
local current=""
|
||||||
|
local available=""
|
||||||
|
|
||||||
|
host_total=$(free -b | awk '/^Mem:/ {print $2; exit}')
|
||||||
|
host_avail=$(free -b | awk '/^Mem:/ {print $7; exit}')
|
||||||
|
|
||||||
|
RAM_TOTAL="$host_total"
|
||||||
|
RAM_AVAIL="$host_avail"
|
||||||
|
|
||||||
|
if [ -r /sys/fs/cgroup/memory.max ] && [ -r /sys/fs/cgroup/memory.current ]; then
|
||||||
|
limit=$(< /sys/fs/cgroup/memory.max)
|
||||||
|
current=$(< /sys/fs/cgroup/memory.current)
|
||||||
|
elif [ -r /sys/fs/cgroup/memory/memory.limit_in_bytes ] && [ -r /sys/fs/cgroup/memory/memory.usage_in_bytes ]; then
|
||||||
|
limit=$(< /sys/fs/cgroup/memory/memory.limit_in_bytes)
|
||||||
|
current=$(< /sys/fs/cgroup/memory/memory.usage_in_bytes)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if finiteMemoryLimit "$limit" && [[ "$current" =~ ^[0-9]+$ ]]; then
|
||||||
|
(( limit < RAM_TOTAL )) && RAM_TOTAL="$limit"
|
||||||
|
|
||||||
|
available=$(( limit - current ))
|
||||||
|
(( available < 0 )) && available=0
|
||||||
|
(( available < RAM_AVAIL )) && RAM_AVAIL="$available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
normalizeRamSize() {
|
normalizeRamSize() {
|
||||||
|
|
||||||
# Read memory
|
# Read host and container memory limits.
|
||||||
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
getMemoryInfo
|
||||||
RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
|
|
||||||
|
|
||||||
RAM_SPARE=500000000
|
RAM_SPARE=500000000
|
||||||
RAM_MINIMUM=136314880
|
RAM_MINIMUM=136314880
|
||||||
@@ -232,6 +304,7 @@ PROCESS="${APP,,}"
|
|||||||
PROCESS="${PROCESS// /-}"
|
PROCESS="${PROCESS// /-}"
|
||||||
|
|
||||||
detectEngine
|
detectEngine
|
||||||
|
detectRootless
|
||||||
|
|
||||||
echo "❯ Starting $APP for $ENGINE v$(</etc/version)..."
|
echo "❯ Starting $APP for $ENGINE v$(</etc/version)..."
|
||||||
echo "❯ For support visit $SUPPORT"
|
echo "❯ For support visit $SUPPORT"
|
||||||
@@ -273,7 +346,7 @@ SPACE_GB=$(formatBytes "$SPACE" "down")
|
|||||||
AVAIL_MEM=$(formatBytes "$RAM_AVAIL" "down")
|
AVAIL_MEM=$(formatBytes "$RAM_AVAIL" "down")
|
||||||
TOTAL_MEM=$(formatBytes "$RAM_TOTAL" "up")
|
TOTAL_MEM=$(formatBytes "$RAM_TOTAL" "up")
|
||||||
|
|
||||||
echo "❯ CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..."
|
echo "❯ CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
checkKvm
|
checkKvm
|
||||||
|
|||||||
+38
-14
@@ -16,13 +16,15 @@ WEB_PID="/run/nginx.pid"
|
|||||||
WSD_PID="$QEMU_DIR/websocketd.pid"
|
WSD_PID="$QEMU_DIR/websocketd.pid"
|
||||||
|
|
||||||
prepareWebFiles() {
|
prepareWebFiles() {
|
||||||
cp -r /var/www/* "$QEMU_DIR"
|
|
||||||
rm -f "$WSD_PID" "$WEB_PID"
|
cp -r /var/www/* "$QEMU_DIR" || return 1
|
||||||
|
rm -f "$WSD_PID" "$WEB_PID" || return 1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
configureWebPorts() {
|
configureWebPorts() {
|
||||||
|
|
||||||
sed -i "s/listen 5000 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf
|
sed -i "s/listen 5000 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf
|
||||||
sed -i "s/proxy_pass http:\/\/127.0.0.1:8004\/;/proxy_pass http:\/\/127.0.0.1:$WSD_PORT\/;/g" /etc/nginx/sites-enabled/web.conf
|
sed -i "s/proxy_pass http:\/\/127.0.0.1:8004\/;/proxy_pass http:\/\/127.0.0.1:$WSD_PORT\/;/g" /etc/nginx/sites-enabled/web.conf
|
||||||
|
|
||||||
@@ -32,7 +34,7 @@ configureWebPorts() {
|
|||||||
configureIpv6Listen() {
|
configureIpv6Listen() {
|
||||||
|
|
||||||
# shellcheck disable=SC2143
|
# shellcheck disable=SC2143
|
||||||
if [ -f /proc/net/if_inet6 ] && [[ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null)" != "1" ]] && [ -n "$(ifconfig -a | grep inet6)" ]; then
|
if [ -f /proc/net/if_inet6 ] && [[ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null)" != "1" ]]; then
|
||||||
sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
|
sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -44,8 +46,8 @@ configureWebServer() {
|
|||||||
mkdir -p /etc/nginx/sites-enabled
|
mkdir -p /etc/nginx/sites-enabled
|
||||||
cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf
|
cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf
|
||||||
|
|
||||||
configureWebPorts
|
configureWebPorts || return 1
|
||||||
configureIpv6Listen
|
configureIpv6Listen || return 1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -53,24 +55,46 @@ configureWebServer() {
|
|||||||
startWebServer() {
|
startWebServer() {
|
||||||
|
|
||||||
# Start webserver
|
# Start webserver
|
||||||
nginx -e stderr
|
nginx -e stderr || return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
startWebsocketServer() {
|
startWebsocketServer() {
|
||||||
|
|
||||||
|
local log="/var/log/websocketd.log"
|
||||||
|
rm -f "$log"
|
||||||
|
|
||||||
# Start websocket server
|
# Start websocket server
|
||||||
websocketd --address 127.0.0.1 --port="$WSD_PORT" /run/socket.sh >/var/log/websocketd.log &
|
websocketd --address 127.0.0.1 --port="$WSD_PORT" /run/socket.sh > "$log" 2>&1 &
|
||||||
echo "$!" > "$WSD_PID"
|
local pid=$!
|
||||||
|
|
||||||
|
if ! echo "$pid" > "$WSD_PID"; then
|
||||||
|
kill "$pid" 2>/dev/null || :
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 0.1
|
||||||
|
|
||||||
|
if ! isAlive "$pid"; then
|
||||||
|
rm -f "$WSD_PID"
|
||||||
|
[ -s "$log" ] && cat "$log" >&2
|
||||||
|
error "Failed to start websocket server!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareWebFiles
|
prepareWebFiles || return 1
|
||||||
|
|
||||||
html "Starting $APP for $ENGINE..."
|
html "Starting $APP for $ENGINE..."
|
||||||
|
|
||||||
if ! disabled "${WEB:-}"; then
|
disabled "${WEB:-}" && return 0
|
||||||
configureWebServer
|
|
||||||
startWebServer
|
configureWebServer || return 1
|
||||||
startWebsocketServer
|
|
||||||
fi
|
startWebServer || return 1
|
||||||
|
startWebsocketServer || return 1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
+137
-10
@@ -8,6 +8,7 @@ error () { printf "%b%s%b" "\E[1;31m❯ " "ERROR: ${1:-}" "\E[0m\n" >&2; }
|
|||||||
warn () { printf "%b%s%b" "\E[1;31m❯ " "Warning: ${1:-}" "\E[0m\n" >&2; }
|
warn () { printf "%b%s%b" "\E[1;31m❯ " "Warning: ${1:-}" "\E[0m\n" >&2; }
|
||||||
|
|
||||||
strip() {
|
strip() {
|
||||||
|
|
||||||
local value="${1:-}"
|
local value="${1:-}"
|
||||||
|
|
||||||
# Remove surrounding whitespace
|
# Remove surrounding whitespace
|
||||||
@@ -28,24 +29,38 @@ strip() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enabled() {
|
enabled() {
|
||||||
case "$(strip "${1:-}")" in
|
|
||||||
Y|y|YES|Yes|yes|TRUE|True|true|1|ON|On|on) return 0 ;;
|
local value
|
||||||
|
value=$(strip "${1:-}")
|
||||||
|
|
||||||
|
case "${value,,}" in
|
||||||
|
y|yes|true|1|on|enable|enabled) return 0 ;;
|
||||||
*) return 1 ;;
|
*) return 1 ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
disabled() {
|
disabled() {
|
||||||
case "$(strip "${1:-}")" in
|
|
||||||
N|n|NO|No|no|FALSE|False|false|0|OFF|Off|off) return 0 ;;
|
local value
|
||||||
|
value=$(strip "${1:-}")
|
||||||
|
|
||||||
|
case "${value,,}" in
|
||||||
|
n|no|none|false|0|off|disable|disabled) return 0 ;;
|
||||||
*) return 1 ;;
|
*) return 1 ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
formatBytes() {
|
formatBytes() {
|
||||||
|
|
||||||
local result
|
local result
|
||||||
result=$(numfmt --to=iec --suffix=B "$1" | sed -r 's/([A-Z])/ \1/' | sed 's/ B/ bytes/g;')
|
|
||||||
|
if ! result=$(numfmt --to=iec --suffix=B "$1" | sed -r 's/([A-Z])/ \1/' | sed 's/ B/ bytes/g;'); then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
local unit="${result//[0-9. ]}"
|
local unit="${result//[0-9. ]}"
|
||||||
result="${result//[a-zA-Z ]/}"
|
result="${result//[a-zA-Z ]/}"
|
||||||
|
|
||||||
if [[ "${2:-}" == "up" ]]; then
|
if [[ "${2:-}" == "up" ]]; then
|
||||||
if [[ "$result" == *"."* ]]; then
|
if [[ "$result" == *"."* ]]; then
|
||||||
result="${result%%.*}"
|
result="${result%%.*}"
|
||||||
@@ -56,11 +71,13 @@ formatBytes() {
|
|||||||
result="${result%%.*}"
|
result="${result%%.*}"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$result $unit"
|
echo "$result $unit"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
isAlive() {
|
isAlive() {
|
||||||
|
|
||||||
local pid="$1"
|
local pid="$1"
|
||||||
[ -z "$pid" ] && return 1
|
[ -z "$pid" ] && return 1
|
||||||
|
|
||||||
@@ -72,6 +89,7 @@ isAlive() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
waitPid() {
|
waitPid() {
|
||||||
|
|
||||||
local i=0
|
local i=0
|
||||||
local pid="$1"
|
local pid="$1"
|
||||||
local timeout="${2:-10}"
|
local timeout="${2:-10}"
|
||||||
@@ -86,6 +104,7 @@ waitPid() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
waitPidFile() {
|
waitPidFile() {
|
||||||
|
|
||||||
local i=0
|
local i=0
|
||||||
local pid=""
|
local pid=""
|
||||||
local file="$1"
|
local file="$1"
|
||||||
@@ -106,6 +125,7 @@ waitPidFile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pKill() {
|
pKill() {
|
||||||
|
|
||||||
local pid="$1"
|
local pid="$1"
|
||||||
local timeout="${2:-10}"
|
local timeout="${2:-10}"
|
||||||
|
|
||||||
@@ -119,6 +139,7 @@ pKill() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fWait() {
|
fWait() {
|
||||||
|
|
||||||
local i=0
|
local i=0
|
||||||
local name="$1"
|
local name="$1"
|
||||||
local timeout="${2:-10}"
|
local timeout="${2:-10}"
|
||||||
@@ -138,6 +159,7 @@ fWait() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fKill() {
|
fKill() {
|
||||||
|
|
||||||
local name="$1"
|
local name="$1"
|
||||||
local timeout="${2:-10}"
|
local timeout="${2:-10}"
|
||||||
|
|
||||||
@@ -150,6 +172,7 @@ fKill() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sKill() {
|
sKill() {
|
||||||
|
|
||||||
local pid=""
|
local pid=""
|
||||||
local file="$1"
|
local file="$1"
|
||||||
|
|
||||||
@@ -165,6 +188,7 @@ sKill() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mKill() {
|
mKill() {
|
||||||
|
|
||||||
local timeout=10
|
local timeout=10
|
||||||
local files=("$@")
|
local files=("$@")
|
||||||
|
|
||||||
@@ -182,6 +206,7 @@ mKill() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setOwner() {
|
setOwner() {
|
||||||
|
|
||||||
local file="$1"
|
local file="$1"
|
||||||
local dir uid gid
|
local dir uid gid
|
||||||
|
|
||||||
@@ -197,6 +222,7 @@ setOwner() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
makeDir() {
|
makeDir() {
|
||||||
|
|
||||||
local path="$1"
|
local path="$1"
|
||||||
local dir uid gid
|
local dir uid gid
|
||||||
|
|
||||||
@@ -204,25 +230,123 @@ makeDir() {
|
|||||||
! mkdir -p "$path" && return 1
|
! mkdir -p "$path" && return 1
|
||||||
|
|
||||||
dir=$(dirname -- "$path")
|
dir=$(dirname -- "$path")
|
||||||
uid=$(stat -c '%u' "$dir") || return 1
|
|
||||||
gid=$(stat -c '%g' "$dir") || return 1
|
|
||||||
|
|
||||||
! chown "$uid:$gid" "$path" && return 1
|
if ! uid=$(stat -c '%u' "$dir") || ! gid=$(stat -c '%g' "$dir"); then
|
||||||
|
warn "failed to determine the owner for \"$path\"."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! chown "$uid:$gid" "$path"; then
|
||||||
|
warn "failed to set the owner for \"$path\"."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stateFile() {
|
||||||
|
|
||||||
|
local name="$1"
|
||||||
|
local prefix="${2:-$PROCESS}"
|
||||||
|
|
||||||
|
[[ "$name" == */* ]] && printf '%s\n' "$name" && return 0
|
||||||
|
|
||||||
|
printf '%s/%s.%s\n' "$STORAGE" "$prefix" "$name"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFile() {
|
||||||
|
|
||||||
|
local txt="$1"
|
||||||
|
local path="$2"
|
||||||
|
|
||||||
|
if ! printf '%s\n' "$txt" > "$path"; then
|
||||||
|
error "Failed to write file \"$path\" !"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! setOwner "$path"; then
|
||||||
|
warn "failed to set the owner for \"$path\"."
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
readFile() {
|
||||||
|
|
||||||
|
local path="$1"
|
||||||
|
local value
|
||||||
|
|
||||||
|
[ -s "$path" ] || return 0
|
||||||
|
|
||||||
|
value=$(<"$path") || return 1
|
||||||
|
value="${value//[![:print:]]/}"
|
||||||
|
|
||||||
|
printf '%s\n' "$value"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
writeState() {
|
||||||
|
|
||||||
|
local name="$1"
|
||||||
|
local value="$2"
|
||||||
|
local prefix="${3:-$PROCESS}"
|
||||||
|
local path
|
||||||
|
|
||||||
|
[ -z "$value" ] && return 0
|
||||||
|
|
||||||
|
path=$(stateFile "$name" "$prefix") || return 1
|
||||||
|
writeFile "$value" "$path"
|
||||||
|
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
readState() {
|
||||||
|
|
||||||
|
local name="$1"
|
||||||
|
local prefix="${2:-$PROCESS}"
|
||||||
|
local path
|
||||||
|
|
||||||
|
path=$(stateFile "$name" "$prefix") || return 1
|
||||||
|
readFile "$path"
|
||||||
|
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreState() {
|
||||||
|
|
||||||
|
local var="$1"
|
||||||
|
local name="$2"
|
||||||
|
local force="${3:-N}"
|
||||||
|
local prefix="${4:-$PROCESS}"
|
||||||
|
local value
|
||||||
|
|
||||||
|
if ! enabled "$force"; then
|
||||||
|
[ -z "${!var:-}" ] || return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
value=$(readState "$name" "$prefix") || return 1
|
||||||
|
[ -n "$value" ] || return 0
|
||||||
|
|
||||||
|
printf -v "$var" '%s' "$value" || return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
escape () {
|
escape () {
|
||||||
|
|
||||||
local s
|
local s
|
||||||
s=${1//&/\&}
|
s=${1//&/\&}
|
||||||
s=${s//</\<}
|
s=${s//</\<}
|
||||||
s=${s//>/\>}
|
s=${s//>/\>}
|
||||||
s=${s//'"'/\"}
|
s=${s//'"'/\"}
|
||||||
|
|
||||||
printf -- %s "$s"
|
printf -- %s "$s"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
html() {
|
html() {
|
||||||
|
|
||||||
local title
|
local title
|
||||||
local body
|
local body
|
||||||
local script
|
local script
|
||||||
@@ -254,6 +378,7 @@ html() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cpu() {
|
cpu() {
|
||||||
|
|
||||||
local ret
|
local ret
|
||||||
local cpu=""
|
local cpu=""
|
||||||
|
|
||||||
@@ -307,6 +432,7 @@ hasDisk() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getCountry() {
|
getCountry() {
|
||||||
|
|
||||||
local url=$1
|
local url=$1
|
||||||
local query=$2
|
local query=$2
|
||||||
local rc json result
|
local rc json result
|
||||||
@@ -345,6 +471,7 @@ setCountry() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addPackage() {
|
addPackage() {
|
||||||
|
|
||||||
local pkg=$1
|
local pkg=$1
|
||||||
local desc=$2
|
local desc=$2
|
||||||
|
|
||||||
@@ -361,8 +488,8 @@ addPackage() {
|
|||||||
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
|
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get -qq update
|
DEBIAN_FRONTEND=noninteractive apt-get -qq update || return 1
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
|
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null || return 1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user