Compare commits

..

8 Commits
v5.10 ... v5.11

Author SHA1 Message Date
Kroese
08616f1057 build: Update Dockerfile (#518) 2023-12-29 00:24:36 +01:00
Kroese
e6193b1020 fix: Port forwarding (#517) 2023-12-28 21:30:28 +01:00
Kroese
f28b9903f3 fix: iptables for NAT (#516) 2023-12-28 21:20:38 +01:00
Kroese
7bf2d119ea feat: Validate mac address (#515) 2023-12-28 20:54:33 +01:00
Kroese
527bded1b2 feat: Detect default interface
* feat: Detect default interface
2023-12-28 20:25:04 +01:00
Kroese
1208c53ebb feat: Check network interface (#513) 2023-12-28 18:26:56 +01:00
Kroese
973efa2d27 feat: Show daemon log
* feat: Show daemon log
2023-12-28 17:58:07 +01:00
Kroese
d09588b915 fix: Refactor
* fix: Refactor
2023-12-28 16:08:12 +01:00
9 changed files with 140 additions and 97 deletions

View File

@@ -44,12 +44,7 @@ COPY --from=builder /qemu-host.bin /run/host.bin
RUN chmod +x /run/*.sh && chmod +x /run/*.bin RUN chmod +x /run/*.sh && chmod +x /run/*.bin
VOLUME /storage VOLUME /storage
EXPOSE 22 80 139 445 5000
EXPOSE 22
EXPOSE 80
EXPOSE 139
EXPOSE 445
EXPOSE 5000
ENV RAM_SIZE "1G" ENV RAM_SIZE "1G"
ENV DISK_SIZE "16G" ENV DISK_SIZE "16G"

View File

@@ -1,12 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
: ${VM_NET_DEV:='eth0'} [ -f "/run/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
[ ! -f "/run/qemu.pid" ] && echo "QEMU is not running yet.." && exit 0
[ -f "/run/qemu.count" ] && echo "QEMU is shutting down.." && exit 1
[ ! -f "/run/qemu.pid" ] && echo "QEMU not running yet.." && exit 0
file="/run/dsm.url" file="/run/dsm.url"
address="/run/qemu.ip"
[ ! -f "$file" ] && echo "DSM has not enabled networking yet.." && exit 1 [ ! -f "$file" ] && echo "DSM has not enabled networking yet.." && exit 1
location=$(cat "$file") location=$(cat "$file")
@@ -19,7 +19,7 @@ if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
echo "Failed to reach DSM at port $port" echo "Failed to reach DSM at port $port"
else else
echo "Failed to reach DSM at http://$location" echo "Failed to reach DSM at http://$location"
ip=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/) ip="$(cat "$address")"
fi fi
echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1 echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1

View File

@@ -14,6 +14,8 @@ if [[ "$GPU" == [Yy1]* ]] && [[ "$ARCH" == "amd64" ]]; then
DEF_OPTS="$DEF_OPTS -device virtio-vga,id=video0,max_outputs=1,bus=pcie.0,addr=0x1" DEF_OPTS="$DEF_OPTS -device virtio-vga,id=video0,max_outputs=1,bus=pcie.0,addr=0x1"
fi fi
[[ "$CONSOLE" != [Yy]* ]] && DEF_OPTS="$DEF_OPTS -daemonize -D $QEMU_LOG"
ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $EXTRA_OPTS $ARGUMENTS" ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $EXTRA_OPTS $ARGUMENTS"
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ') ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')

View File

@@ -19,35 +19,14 @@ cd /run
trap - ERR trap - ERR
if [[ "$CONSOLE" == [Yy]* ]]; then if [[ "$CONSOLE" == [Yy]* ]]; then
exec qemu-system-x86_64 -pidfile "$QEMU_PID" ${ARGS:+ $ARGS} exec qemu-system-x86_64 ${ARGS:+ $ARGS}
exit $?
fi fi
[[ "$DEBUG" == [Yy1]* ]] && info "$VERS" && set -x [[ "$DEBUG" == [Yy1]* ]] && info "$VERS" && set -x
msg=$(qemu-system-x86_64 -daemonize -pidfile "$QEMU_PID" ${ARGS:+ $ARGS}) msg=$(qemu-system-x86_64 ${ARGS:+ $ARGS})
{ set +x; } 2>/dev/null
if [[ "$msg" != "char"* || "$msg" != *"serial0)" ]]; then { set +x; } 2>/dev/null && terminal "$msg"
echo "$msg" tail -fn +0 "$QEMU_LOG" 2>/dev/null &
fi cat "$QEMU_TERM" 2>/dev/null & wait $! || true
dev="${msg#*/dev/p}" sleep 1 && finish 0
dev="/dev/p${dev%% *}"
if [ ! -c "$dev" ]; then
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000')
dev="${dev#*charserial0}"
dev="${dev#*pty:}"
dev="${dev%%$'\n'*}"
dev="${dev%%$'\r'*}"
fi
if [ ! -c "$dev" ]; then
error "Device '$dev' not found!"
finish 34
fi
cat "$dev" 2>/dev/null & wait $! || true
sleep 1
finish 0

View File

@@ -6,8 +6,8 @@ set -Eeuo pipefail
: ${DHCP:='N'} : ${DHCP:='N'}
: ${MAC:='02:11:32:AA:BB:CC'} : ${MAC:='02:11:32:AA:BB:CC'}
: ${VM_NET_DEV:=''}
: ${VM_NET_TAP:='dsm'} : ${VM_NET_TAP:='dsm'}
: ${VM_NET_DEV:='eth0'}
: ${VM_NET_MAC:="$MAC"} : ${VM_NET_MAC:="$MAC"}
: ${VM_NET_HOST:='VirtualDSM'} : ${VM_NET_HOST:='VirtualDSM'}
@@ -69,7 +69,7 @@ configureDHCP() {
return 0 return 0
} }
configureDNS () { configureDNS() {
# dnsmasq configuration: # dnsmasq configuration:
DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-range=$VM_NET_IP,$VM_NET_IP --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite --dhcp-option=option:netmask,255.255.255.0" DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-range=$VM_NET_IP,$VM_NET_IP --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite --dhcp-option=option:netmask,255.255.255.0"
@@ -90,7 +90,27 @@ configureDNS () {
return 0 return 0
} }
configureNAT () { configureNAT() {
# Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
if mknod /dev/net/tun c 10 200; then
chmod 666 /dev/net/tun
fi
fi
if [ ! -c /dev/net/tun ]; then
error "TUN device missing. $ADD_ERR --cap-add NET_ADMIN" && exit 25
fi
# Check port forwarding flag
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 ; rc=$?; } || :
if (( rc != 0 )); then
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1" && exit 24
fi
fi
# Create a bridge with a static IP for the VM guest # Create a bridge with a static IP for the VM guest
@@ -121,6 +141,9 @@ configureNAT () {
ip link set dev "$VM_NET_TAP" master dockerbridge ip link set dev "$VM_NET_TAP" master dockerbridge
# Add internet connection to the VM # Add internet connection to the VM
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE
iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp -j DNAT --to "$VM_NET_IP" iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp -j DNAT --to "$VM_NET_IP"
iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP" iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"
@@ -133,14 +156,6 @@ configureNAT () {
{ set +x; } 2>/dev/null { set +x; } 2>/dev/null
[[ "$DEBUG" == [Yy1]* ]] && echo [[ "$DEBUG" == [Yy1]* ]] && echo
# Check port forwarding flag
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 ; rc=$?; } || :
if (( rc != 0 )); then
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1" && exit 24
fi
fi
NET_OPTS="-netdev tap,ifname=$VM_NET_TAP,script=no,downscript=no,id=hostnet0" NET_OPTS="-netdev tap,ifname=$VM_NET_TAP,script=no,downscript=no,id=hostnet0"
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || : { exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
@@ -151,7 +166,7 @@ configureNAT () {
return 0 return 0
} }
closeNetwork () { closeNetwork() {
exec 30<&- || true exec 30<&- || true
exec 40<&- || true exec 40<&- || true
@@ -174,6 +189,42 @@ closeNetwork () {
ip link delete dockerbridge || true ip link delete dockerbridge || true
fi fi
return 0
}
getInfo() {
if [ -z "$VM_NET_DEV" ]; then
# Automaticly detect the default network interface
VM_NET_DEV=$(awk '$2 == 00000000 { print $1 }' /proc/net/route)
[ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0"
fi
if [ ! -d "/sys/class/net/$VM_NET_DEV" ]; then
error "Network interface '$VM_NET_DEV' does not exist inside the container!"
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 27
fi
VM_NET_MAC="${VM_NET_MAC//-/:}"
if [[ ${#VM_NET_MAC} == 12 ]]; then
m="$VM_NET_MAC"
VM_NET_MAC="${m:0:2}:${m:2:2}:${m:4:2}:${m:6:2}:${m:8:2}:${m:10:2}"
fi
if [[ ${#VM_NET_MAC} != 17 ]]; then
error "Invalid mac address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
fi
GATEWAY=$(ip r | grep default | awk '{print $3}')
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
echo "$IP" > /run/qemu.ip
if [[ "$DEBUG" == [Yy1]* ]]; then
info "Container IP is $IP with gateway $GATEWAY on interface $VM_NET_DEV" && echo
fi
return 0
} }
# ###################################### # ######################################
@@ -182,35 +233,13 @@ closeNetwork () {
fKill "server.sh" fKill "server.sh"
# Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
if mknod /dev/net/tun c 10 200; then
chmod 666 /dev/net/tun
fi
fi
if [ ! -c /dev/net/tun ]; then
error "TUN device missing. $ADD_ERR --cap-add NET_ADMIN" && exit 25
fi
# Create the necessary file structure for /dev/vhost-net
if [ ! -c /dev/vhost-net ]; then if [ ! -c /dev/vhost-net ]; then
if mknod /dev/vhost-net c 10 238; then if mknod /dev/vhost-net c 10 238; then
chmod 660 /dev/vhost-net chmod 660 /dev/vhost-net
fi fi
fi fi
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null getInfo
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
VM_NET_MAC="${VM_NET_MAC//-/:}"
GATEWAY=$(ip r | grep default | awk '{print $3}')
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
if [[ "$DEBUG" == [Yy1]* ]]; then
info "Container IP is $IP with gateway $GATEWAY" && echo
fi
if [[ "$DHCP" == [Yy1]* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then

View File

@@ -7,10 +7,12 @@ API_CMD=6
API_TIMEOUT=50 API_TIMEOUT=50
API_HOST="127.0.0.1:2210" API_HOST="127.0.0.1:2210"
QEMU_TERM=""
QEMU_PORT=7100 QEMU_PORT=7100
QEMU_TIMEOUT=50 QEMU_TIMEOUT=50
QEMU_PID="/run/qemu.pid" QEMU_PID="/run/qemu.pid"
QEMU_COUNT="/run/qemu.count" QEMU_LOG="/run/qemu.log"
QEMU_END="/run/qemu.end"
if [[ "$KVM" == [Nn]* ]]; then if [[ "$KVM" == [Nn]* ]]; then
API_TIMEOUT=$(( API_TIMEOUT*2 )) API_TIMEOUT=$(( API_TIMEOUT*2 ))
@@ -18,7 +20,9 @@ if [[ "$KVM" == [Nn]* ]]; then
fi fi
rm -f "$QEMU_PID" rm -f "$QEMU_PID"
rm -f "$QEMU_COUNT" rm -f "$QEMU_LOG"
rm -f "$QEMU_END"
touch "$QEMU_LOG"
_trap() { _trap() {
func="$1" ; shift func="$1" ; shift
@@ -56,19 +60,48 @@ finish() {
exit "$reason" exit "$reason"
} }
terminal() {
local msg=$1
if [[ "${msg,,}" != "char"* || "$msg" != *"serial0)" ]]; then
echo "$msg"
fi
local dev="${msg#*/dev/p}"
dev="/dev/p${dev%% *}"
if [ ! -c "$dev" ]; then
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000')
dev="${dev#*charserial0}"
dev="${dev#*pty:}"
dev="${dev%%$'\n'*}"
dev="${dev%%$'\r'*}"
fi
if [ ! -c "$dev" ]; then
error "Device '$dev' not found!"
finish 34 && return 34
fi
QEMU_TERM="$dev"
return 0
}
_graceful_shutdown() { _graceful_shutdown() {
local cnt=0
local code=$? local code=$?
local pid cnt response local pid url response
set +e set +e
if [ -f "$QEMU_COUNT" ]; then if [ -f "$QEMU_END" ]; then
echo && info "Ignored $1 signal, already shutting down..." echo && info "Received $1 signal while already shutting down..."
return return
fi fi
echo 0 > "$QEMU_COUNT" touch "$QEMU_END"
echo && info "Received $1 signal, sending shutdown command..." echo && info "Received $1 signal, sending shutdown command..."
if [ ! -f "$QEMU_PID" ]; then if [ ! -f "$QEMU_PID" ]; then
@@ -103,15 +136,12 @@ _graceful_shutdown() {
fi fi
while [ "$(cat $QEMU_COUNT)" -lt "$QEMU_TIMEOUT" ]; do while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do
! isAlive "$pid" && break ! isAlive "$pid" && break
sleep 1 sleep 1
cnt=$((cnt+1))
# Increase the counter
cnt=$(($(cat $QEMU_COUNT)+1))
echo $cnt > "$QEMU_COUNT"
[[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt/$QEMU_TIMEOUT)" [[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt/$QEMU_TIMEOUT)"
@@ -120,13 +150,17 @@ _graceful_shutdown() {
done done
if [ "$(cat $QEMU_COUNT)" -ge "$QEMU_TIMEOUT" ]; then if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then
echo && error "Shutdown timeout reached!" echo && error "Shutdown timeout reached, aborting..."
fi fi
finish "$code" && return "$code" finish "$code" && return "$code"
} }
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT if [[ "$CONSOLE" != [Yy]* ]]; then
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
fi
MON_OPTS="-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay" MON_OPTS="\
-pidfile $QEMU_PID \
-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay"

View File

@@ -2,16 +2,17 @@
set -Eeuo pipefail set -Eeuo pipefail
: ${DHCP:='N'} : ${DHCP:='N'}
: ${VM_NET_DEV:='eth0'}
info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "$1" "\E[0m\n" >&2; } info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "$1" "\E[0m\n" >&2; }
error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; } error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; }
file="/run/dsm.url" file="/run/dsm.url"
shutdown="/run/qemu.count" address="/run/qemu.ip"
shutdown="/run/qemu.end"
url="http://127.0.0.1:2210/read?command=10" url="http://127.0.0.1:2210/read?command=10"
resp_err="Guest returned an invalid response:" resp_err="Guest returned an invalid response:"
curl_err="Failed to connect to guest: curl error"
jq_err="Failed to parse response from guest: jq error" jq_err="Failed to parse response from guest: jq error"
while [ ! -f "$file" ] while [ ! -f "$file" ]
@@ -29,7 +30,7 @@ do
{ json=$(curl -m 20 -sk "$url"); rc=$?; } || : { json=$(curl -m 20 -sk "$url"); rc=$?; } || :
[ -f "$shutdown" ] && exit 1 [ -f "$shutdown" ] && exit 1
(( rc != 0 )) && error "Failed to connect to guest: curl error $rc" && continue (( rc != 0 )) && error "$curl_err $rc" && continue
{ result=$(echo "$json" | jq -r '.status'); rc=$?; } || : { result=$(echo "$json" | jq -r '.status'); rc=$?; } || :
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue (( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
@@ -68,7 +69,7 @@ if [[ "$location" != "20.20"* ]]; then
else else
ip=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/) ip="$(cat "$address")"
port="${location##*:}" port="${location##*:}"
if [[ "$ip" == "172."* ]]; then if [[ "$ip" == "172."* ]]; then

View File

@@ -38,8 +38,10 @@ STORAGE="/storage"
# Cleanup files # Cleanup files
rm -f /run/dsm.url rm -f /run/dsm.url
rm -f /run/qemu.ip
rm -f /run/qemu.log
rm -f /run/qemu.pid rm -f /run/qemu.pid
rm -f /run/qemu.count rm -f /run/qemu.end
# Cleanup dirs # Cleanup dirs
@@ -71,7 +73,7 @@ pKill() {
return 0 return 0
} }
fKill () { fKill() {
local name=$1 local name=$1
{ pkill -f "$name" || true; } 2>/dev/null { pkill -f "$name" || true; } 2>/dev/null
@@ -83,7 +85,7 @@ fKill () {
return 0 return 0
} }
getCountry () { getCountry() {
local url=$1 local url=$1
local query=$2 local query=$2
local rc json result local rc json result
@@ -102,7 +104,7 @@ getCountry () {
return 0 return 0
} }
setCountry () { setCountry() {
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN" [[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN" [[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
@@ -119,7 +121,7 @@ setCountry () {
return 0 return 0
} }
addPackage () { addPackage() {
local pkg=$1 local pkg=$1
local desc=$2 local desc=$2

View File

@@ -47,6 +47,7 @@ done
# Configure serial ports # Configure serial ports
SERIAL_OPTS="\ SERIAL_OPTS="\
-serial none \
-chardev pty,id=charserial0 \ -chardev pty,id=charserial0 \
-device isa-serial,chardev=charserial0,id=serial0 \ -device isa-serial,chardev=charserial0,id=serial0 \
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \ -device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \