Stage 7: command-line helpers dsv, duser, dfw, dsys (0.5.0)
- das-helpers replaces the dsv package and adds shared functions in /usr/lib/das/helpers.sh (bilingual messages, sudo re-exec, prompts). - dsv new creates a runit service (command, user, directory, environment, svlogd log) and checks it stays up; dsv rm deletes one not from a package. - duser: list, add (installer groups, --admin, key, password), del, passwd, lock/unlock (key logins too), key add|list|rm, groups +/-. - dfw: allow/deny/status/reload for the tcp_ports and udp_ports sets of the inet das table, kept in /etc/nftables.d/dfw.nft and checked with nft -c. - dsys: info, hostname, timezone, locale, keymap. - xtools-minimal on both images. - nftables.conf no longer suggests restarting the nftables service: its finish script flushes the whole ruleset, fail2ban's and Docker's included. - das-server-install 0.2.0 mentions the helpers when it finishes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Executable
+240
@@ -0,0 +1,240 @@
|
||||
#!/bin/bash
|
||||
# dfw - open and close ports in the Das OS server firewall (nftables).
|
||||
#
|
||||
# /etc/nftables.conf defines the "das" table with the sets tcp_ports and
|
||||
# udp_ports; dfw keeps their elements in /etc/nftables.d/dfw.nft, which that
|
||||
# file includes, and reloads only the "das" table. SSH is always open.
|
||||
set -euo pipefail
|
||||
|
||||
# shellcheck source=helpers.sh
|
||||
. "${DAS_HELPERS:-/usr/lib/das/helpers.sh}"
|
||||
|
||||
conf=/etc/nftables.conf
|
||||
ports_file=/etc/nftables.d/dfw.nft
|
||||
|
||||
usage() {
|
||||
if [[ -n $uk ]]; then
|
||||
cat <<'EOF'
|
||||
Використання: dfw КОМАНДА [ПОРТ...]
|
||||
|
||||
status стан брандмауера й відкриті порти (типова команда)
|
||||
allow ПОРТ... відкрити порти для вхідних з'єднань
|
||||
deny ПОРТ... закрити порти, відкриті через allow
|
||||
reload перечитати /etc/nftables.conf після редагування
|
||||
|
||||
ПОРТ: 80, 80/tcp, 53/udp, 8000-8010/tcp або назва служби (http, https).
|
||||
Без /udp мається на увазі tcp. SSH відкритий завжди.
|
||||
|
||||
Порти, які публікують контейнери Docker (-p 8080:80), відкриває сам Docker,
|
||||
dfw на них не впливає.
|
||||
EOF
|
||||
else
|
||||
cat <<'EOF'
|
||||
Usage: dfw COMMAND [PORT...]
|
||||
|
||||
status firewall state and open ports (the default)
|
||||
allow PORT... open ports for incoming connections
|
||||
deny PORT... close ports opened with allow
|
||||
reload load /etc/nftables.conf again after editing it
|
||||
|
||||
PORT: 80, 80/tcp, 53/udp, 8000-8010/tcp or a service name (http, https).
|
||||
Without /udp it means tcp. SSH is always open.
|
||||
|
||||
Ports that Docker containers publish (-p 8080:80) are opened by Docker
|
||||
itself, dfw does not affect them.
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
check_config() {
|
||||
grep -qs 'set tcp_ports' "$conf" ||
|
||||
die "$conf не з Das OS Server (немає набору tcp_ports); dfw працює лише з ним" \
|
||||
"$conf is not the Das OS Server one (no tcp_ports set); dfw only works with that"
|
||||
}
|
||||
|
||||
# Opened ports of one protocol from the ports file, one per line
|
||||
saved_ports() {
|
||||
[[ -r $ports_file ]] || return 0
|
||||
sed -n "s/^add element inet das $1_ports { \(.*\) }$/\1/p" "$ports_file" | tr -d ' ' | tr ',' '\n'
|
||||
}
|
||||
|
||||
# parse_port SPEC: prints "PORT PROTO" with PORT a number or a FIRST-LAST range
|
||||
parse_port() {
|
||||
local spec=$1 port proto=tcp first last
|
||||
if [[ $spec == */* ]]; then
|
||||
port=${spec%/*}
|
||||
proto=${spec##*/}
|
||||
else
|
||||
port=$spec
|
||||
fi
|
||||
[[ $proto == tcp || $proto == udp ]] || die "'$spec': протокол tcp або udp" "'$spec': the protocol is tcp or udp"
|
||||
if [[ $port =~ ^[a-z][a-z0-9-]*$ ]]; then
|
||||
# A service name from /etc/services
|
||||
first=$(getent services "$port/$proto" | awk '{ print $2 }')
|
||||
[[ -n $first ]] || die "'$spec': невідома служба $port/$proto" "'$spec': unknown service $port/$proto"
|
||||
port=${first%/*}
|
||||
fi
|
||||
if [[ $port =~ ^([0-9]+)-([0-9]+)$ ]]; then
|
||||
first=${BASH_REMATCH[1]}
|
||||
last=${BASH_REMATCH[2]}
|
||||
elif [[ $port =~ ^[0-9]+$ ]]; then
|
||||
first=$port
|
||||
last=$port
|
||||
else
|
||||
die "'$spec': порт — число, діапазон 8000-8010 або назва служби" \
|
||||
"'$spec': a port is a number, a range like 8000-8010 or a service name"
|
||||
fi
|
||||
((first >= 1 && last <= 65535 && first <= last)) ||
|
||||
die "'$spec': порти від 1 до 65535" "'$spec': ports are 1 to 65535"
|
||||
if ((first == last)); then
|
||||
echo "$first $proto"
|
||||
else
|
||||
echo "$first-$last $proto"
|
||||
fi
|
||||
}
|
||||
|
||||
firewall_on() {
|
||||
[[ -e /etc/runit/runsvdir/default/nftables ]]
|
||||
}
|
||||
|
||||
# save TCP_PORTS UDP_PORTS (newline-separated): writes the ports file, checks
|
||||
# the whole configuration and loads it, restoring the old file on an error
|
||||
save() {
|
||||
local tcp udp backup=""
|
||||
# sort -n -u would take 80 and 80-90 for duplicates
|
||||
tcp=$(sort -u <<<"$1" | sort -n | grep . | paste -sd, - | sed 's/,/, /g' || true)
|
||||
udp=$(sort -u <<<"$2" | sort -n | grep . | paste -sd, - | sed 's/,/, /g' || true)
|
||||
mkdir -p "${ports_file%/*}"
|
||||
if [[ -e $ports_file ]]; then
|
||||
backup=$(mktemp)
|
||||
cp "$ports_file" "$backup"
|
||||
fi
|
||||
{
|
||||
echo "# Ports opened with dfw; change them with dfw allow/deny"
|
||||
[[ -z $tcp ]] || echo "add element inet das tcp_ports { $tcp }"
|
||||
[[ -z $udp ]] || echo "add element inet das udp_ports { $udp }"
|
||||
} >"$ports_file"
|
||||
if ! nft -c -f "$conf"; then
|
||||
if [[ -n $backup ]]; then mv "$backup" "$ports_file"; else rm -f "$ports_file"; fi
|
||||
die "nftables не прийняв правила, нічого не змінено" "nftables rejected the rules, nothing changed"
|
||||
fi
|
||||
rm -f "$backup"
|
||||
if firewall_on; then
|
||||
nft -f "$conf"
|
||||
else
|
||||
warn "брандмауер вимкнений, зміни збережено; увімкнути: dsv enable nftables" \
|
||||
"the firewall is off, changes are saved; to turn it on: dsv enable nftables"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_allow() {
|
||||
local spec port proto tcp udp
|
||||
tcp=$(saved_ports tcp)
|
||||
udp=$(saved_ports udp)
|
||||
for spec in "$@"; do
|
||||
read -r port proto < <(parse_port "$spec")
|
||||
if [[ $port == 22 && $proto == tcp ]]; then
|
||||
say "22/tcp (SSH) відкритий завжди" "22/tcp (SSH) is always open"
|
||||
continue
|
||||
fi
|
||||
if grep -qx "$port" <<<"$(if [[ $proto == tcp ]]; then echo "$tcp"; else echo "$udp"; fi)"; then
|
||||
say "$port/$proto: уже відкритий" "$port/$proto: already open"
|
||||
continue
|
||||
fi
|
||||
if [[ $proto == tcp ]]; then tcp+=$'\n'$port; else udp+=$'\n'$port; fi
|
||||
say "$port/$proto: відкрито" "$port/$proto: open"
|
||||
done
|
||||
save "$tcp" "$udp"
|
||||
}
|
||||
|
||||
cmd_deny() {
|
||||
local spec port proto tcp udp list
|
||||
tcp=$(saved_ports tcp)
|
||||
udp=$(saved_ports udp)
|
||||
for spec in "$@"; do
|
||||
read -r port proto < <(parse_port "$spec")
|
||||
if [[ $port == 22 && $proto == tcp ]]; then
|
||||
die "SSH не закривається через dfw, щоб не втратити доступ до сервера; правило — у $conf" \
|
||||
"SSH is not closed with dfw, so as not to lose access to the server; the rule is in $conf"
|
||||
fi
|
||||
if [[ $proto == tcp ]]; then list=$tcp; else list=$udp; fi
|
||||
if ! grep -qx "$port" <<<"$list"; then
|
||||
say "$port/$proto: не був відкритий через dfw" "$port/$proto: was not opened with dfw"
|
||||
continue
|
||||
fi
|
||||
list=$(grep -vx "$port" <<<"$list" || true)
|
||||
if [[ $proto == tcp ]]; then tcp=$list; else udp=$list; fi
|
||||
say "$port/$proto: закрито" "$port/$proto: closed"
|
||||
done
|
||||
save "$tcp" "$udp"
|
||||
}
|
||||
|
||||
cmd_status() {
|
||||
local p state banned ports
|
||||
if firewall_on && nft list table inet das >/dev/null 2>&1; then
|
||||
state=$(say "увімкнений" "on")
|
||||
else
|
||||
state=$(say "ВИМКНЕНИЙ (dsv enable nftables)" "OFF (dsv enable nftables)")
|
||||
fi
|
||||
say "Брандмауер: $state" "Firewall: $state"
|
||||
say "Вхідні з'єднання закриті, крім:" "Incoming connections are closed except:"
|
||||
echo " 22/tcp (ssh)"
|
||||
for p in tcp udp; do
|
||||
while read -r ports; do
|
||||
[[ -n $ports ]] && echo " $ports/$p"
|
||||
done < <(saved_ports "$p")
|
||||
done
|
||||
|
||||
# Docker opens its published ports itself
|
||||
if command -v docker >/dev/null && ports=$(docker ps --format '{{.Names}}: {{.Ports}}' 2>/dev/null) &&
|
||||
grep -q -- '->' <<<"$ports"; then
|
||||
say "Опубліковані контейнерами Docker (відкриті незалежно від dfw):" \
|
||||
"Published by Docker containers (open regardless of dfw):"
|
||||
grep -- '->' <<<"$ports" | sed 's/^/ /'
|
||||
fi
|
||||
|
||||
if banned=$(fail2ban-client status sshd 2>/dev/null | sed -n 's/.*Currently banned:[[:space:]]*//p') &&
|
||||
[[ -n $banned && $banned != 0 ]]; then
|
||||
say "fail2ban: заблоковано адрес за невдалі входи SSH: $banned" \
|
||||
"fail2ban: addresses banned for failed SSH logins: $banned"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd=${1:-status}
|
||||
[[ $# -gt 0 ]] && shift
|
||||
|
||||
case $cmd in
|
||||
status | list | ls)
|
||||
need_root status
|
||||
check_config
|
||||
cmd_status
|
||||
;;
|
||||
allow | open)
|
||||
[[ $# -gt 0 ]] || die "вкажіть порт: dfw allow 80/tcp" "give a port: dfw allow 80/tcp"
|
||||
need_root allow "$@"
|
||||
check_config
|
||||
cmd_allow "$@"
|
||||
;;
|
||||
deny | close)
|
||||
[[ $# -gt 0 ]] || die "вкажіть порт: dfw deny 80/tcp" "give a port: dfw deny 80/tcp"
|
||||
need_root deny "$@"
|
||||
check_config
|
||||
cmd_deny "$@"
|
||||
;;
|
||||
reload)
|
||||
need_root reload
|
||||
nft -c -f "$conf"
|
||||
if firewall_on; then
|
||||
nft -f "$conf"
|
||||
say "правила завантажено" "rules loaded"
|
||||
else
|
||||
say "правила правильні, але брандмауер вимкнений: dsv enable nftables" \
|
||||
"the rules are valid, but the firewall is off: dsv enable nftables"
|
||||
fi
|
||||
;;
|
||||
-h | --help | help) usage ;;
|
||||
*)
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,22 @@
|
||||
# bash completion for dfw
|
||||
# shellcheck shell=bash
|
||||
_dfw() {
|
||||
local cur=${COMP_WORDS[COMP_CWORD]}
|
||||
if ((COMP_CWORD == 1)); then
|
||||
mapfile -t COMPREPLY < <(compgen -W "status allow deny reload help" -- "$cur")
|
||||
return
|
||||
fi
|
||||
case ${COMP_WORDS[1]} in
|
||||
deny)
|
||||
# The ports opened with dfw
|
||||
local ports
|
||||
ports=$(sed -n 's/^add element inet das \(tcp\|udp\)_ports { \(.*\) }$/\2 \1/p' /etc/nftables.d/dfw.nft 2>/dev/null |
|
||||
awk '{ for (i = 1; i < NF; i++) { p = $i; sub(/,$/, "", p); print p "/" $NF } }')
|
||||
mapfile -t COMPREPLY < <(compgen -W "$ports" -- "$cur")
|
||||
;;
|
||||
allow)
|
||||
mapfile -t COMPREPLY < <(compgen -W "http https 80/tcp 443/tcp 53/udp" -- "$cur")
|
||||
;;
|
||||
esac
|
||||
}
|
||||
complete -F _dfw dfw
|
||||
@@ -7,6 +7,9 @@
|
||||
# services that log to syslog, from socklog.
|
||||
set -euo pipefail
|
||||
|
||||
# shellcheck source=helpers.sh
|
||||
. "${DAS_HELPERS:-/usr/lib/das/helpers.sh}"
|
||||
|
||||
sv_dir=/etc/sv
|
||||
runlevel=/etc/runit/runsvdir/default
|
||||
# Outside a running system (a chroot, an image) /var/service may not exist
|
||||
@@ -14,22 +17,6 @@ service_dir=/var/service
|
||||
[[ -d $service_dir ]] || service_dir=$runlevel
|
||||
socklog=/var/log/socklog
|
||||
|
||||
if [[ ${LANG:-} == uk* || ${LC_ALL:-} == uk* || ${LC_MESSAGES:-} == uk* ]]; then
|
||||
uk=1
|
||||
else
|
||||
uk=""
|
||||
fi
|
||||
|
||||
# say UKRAINIAN ENGLISH: prints the message in the user's language
|
||||
say() {
|
||||
if [[ -n $uk ]]; then echo "$1"; else echo "$2"; fi
|
||||
}
|
||||
|
||||
die() {
|
||||
say "dsv: $1" "dsv: $2" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
if [[ -n $uk ]]; then
|
||||
cat <<'EOF'
|
||||
@@ -42,6 +29,16 @@ usage() {
|
||||
start|stop|restart СЛУЖБА...
|
||||
запустити, зупинити, перезапустити
|
||||
logs СЛУЖБА [-f] журнал служби (-f: стежити за новими рядками)
|
||||
new [СЛУЖБА] [ПАРАМЕТРИ]
|
||||
створити службу для своєї програми; чого не
|
||||
вказано, те запитає:
|
||||
--cmd КОМАНДА що запускати (програма не має йти у фон)
|
||||
--user КОРИСТУВАЧ від чийого імені
|
||||
--dir КАТАЛОГ робочий каталог
|
||||
--env ІМ'Я=ЗНАЧЕННЯ змінна середовища, можна кілька разів
|
||||
--no-log без власного журналу в /var/log/СЛУЖБА
|
||||
--enable одразу ввімкнути й запустити
|
||||
rm СЛУЖБА вимкнути й видалити службу, створену через new
|
||||
|
||||
Служби — каталоги в /etc/sv. Без команди виконується list.
|
||||
EOF
|
||||
@@ -56,21 +53,23 @@ Usage: dsv COMMAND [SERVICE...]
|
||||
start|stop|restart SERVICE...
|
||||
start, stop, restart
|
||||
logs SERVICE [-f] the service log (-f: follow new lines)
|
||||
new [SERVICE] [OPTIONS] create a service for your own program; asks for
|
||||
what is not given:
|
||||
--cmd COMMAND what to run (the program must stay in the foreground)
|
||||
--user USER who runs it
|
||||
--dir DIR working directory
|
||||
--env NAME=VALUE an environment variable, may be repeated
|
||||
--no-log no log of its own in /var/log/SERVICE
|
||||
--enable enable and start it right away
|
||||
rm SERVICE disable and delete a service made with new
|
||||
|
||||
Services are the directories in /etc/sv. Without a command, runs list.
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
# Service states live in root-only supervise directories
|
||||
need_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
exec sudo "$0" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
check_service() {
|
||||
[[ -d $sv_dir/$1 ]] || die "немає служби '$1' (див. ls $sv_dir)" "no service '$1' (see ls $sv_dir)"
|
||||
[[ -d $sv_dir/$1 ]] || die "немає служби '$1' (див. dsv list)" "no service '$1' (see dsv list)"
|
||||
}
|
||||
|
||||
is_enabled() {
|
||||
@@ -87,14 +86,12 @@ wait_supervised() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# row NAME ENABLED STATE PID UPTIME: one line of the list. printf pads by
|
||||
# bytes, ${#s} counts characters, so Cyrillic columns stay aligned.
|
||||
# row NAME ENABLED STATE PID UPTIME: one line of the list
|
||||
row() {
|
||||
local s widths=(24 9 6 8) i=0
|
||||
for s in "$1" "$2" "$3" "$4"; do
|
||||
printf '%s%*s ' "$s" $((widths[i] - ${#s})) ''
|
||||
i=$((i + 1))
|
||||
done
|
||||
pad "$1" 24
|
||||
pad "$2" 9
|
||||
pad "$3" 6
|
||||
pad "$4" 8
|
||||
printf '%s\n' "$5"
|
||||
}
|
||||
|
||||
@@ -240,6 +237,151 @@ cmd_logs() {
|
||||
fi
|
||||
}
|
||||
|
||||
# check_running NAME: after a start, tells whether the service stays up or
|
||||
# keeps restarting (runsv restarts a program that exits, once a second)
|
||||
check_running() {
|
||||
local line up='^run:[^;]*\) ([0-9]+)s'
|
||||
sleep 3
|
||||
line=$(sv status "$service_dir/$1" 2>/dev/null || true)
|
||||
if [[ $line =~ $up ]] && ((BASH_REMATCH[1] >= 2)); then
|
||||
say "$1: працює" "$1: running"
|
||||
else
|
||||
warn "$1 не працює стабільно (${line%%;*}); див. dsv logs $1" \
|
||||
"$1 does not stay up (${line%%;*}); see dsv logs $1"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_new() {
|
||||
local name="" run_cmd="" user="" dir="" log=1 enable="" envs=() e home bin
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
--cmd) run_cmd=${2:?}; shift ;;
|
||||
--user) user=${2:?}; shift ;;
|
||||
--dir) dir=${2:?}; shift ;;
|
||||
--env) envs+=("${2:?}"); shift ;;
|
||||
--no-log) log="" ;;
|
||||
--enable) enable=1 ;;
|
||||
-*) die "невідомий параметр $1 (dsv help)" "unknown option $1 (dsv help)" ;;
|
||||
*) [[ -z $name ]] || die "зайвий аргумент $1" "extra argument $1"; name=$1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ -z $name ]]; then
|
||||
interactive || die "вкажіть назву служби" "name the service"
|
||||
ask name "Назва служби" "Service name"
|
||||
fi
|
||||
[[ $name =~ ^[A-Za-z0-9][A-Za-z0-9_.@-]*$ ]] ||
|
||||
die "назва '$name': лише латинські літери, цифри, '.', '_', '-', '@'" \
|
||||
"name '$name': only Latin letters, digits, '.', '_', '-', '@'"
|
||||
[[ ! -e $sv_dir/$name ]] || die "служба $name уже є" "service $name already exists"
|
||||
|
||||
if [[ -z $run_cmd ]]; then
|
||||
interactive || die "вкажіть команду: --cmd" "give the command: --cmd"
|
||||
say "Команда запуску. Програма має працювати на передньому плані (без -d/--daemon)." \
|
||||
"The command to run. The program must stay in the foreground (no -d/--daemon)."
|
||||
ask run_cmd "Команда" "Command"
|
||||
[[ -n $run_cmd ]] || die "команду не вказано" "no command given"
|
||||
fi
|
||||
read -r bin _ <<<"$run_cmd"
|
||||
if [[ $bin == */* ]]; then
|
||||
[[ -x $bin ]] || die "$bin не існує або не виконуваний" "$bin does not exist or is not executable"
|
||||
elif ! command -v "$bin" >/dev/null; then
|
||||
die "команду $bin не знайдено; вкажіть повний шлях" "command $bin not found; give the full path"
|
||||
fi
|
||||
|
||||
if [[ -z $user ]] && interactive; then
|
||||
ask user "Від імені користувача" "Run as user" "${SUDO_USER:-root}"
|
||||
fi
|
||||
user=${user:-root}
|
||||
id -u "$user" >/dev/null 2>&1 || die "немає користувача $user" "no user $user"
|
||||
home=$(getent passwd "$user" | cut -d: -f6)
|
||||
|
||||
if [[ -z $dir ]] && interactive; then
|
||||
ask dir "Робочий каталог" "Working directory" "$home"
|
||||
fi
|
||||
dir=${dir:-$home}
|
||||
[[ -d $dir ]] || die "немає каталогу $dir" "no directory $dir"
|
||||
|
||||
if ((${#envs[@]} == 0)) && interactive; then
|
||||
ask e "Змінні середовища ІМ'Я=ЗНАЧЕННЯ через пробіл (Enter — без них)" \
|
||||
"Environment variables NAME=VALUE separated by spaces (Enter for none)"
|
||||
read -ra envs <<<"$e"
|
||||
fi
|
||||
for e in "${envs[@]}"; do
|
||||
[[ $e =~ ^[A-Za-z_][A-Za-z0-9_]*= ]] || die "'$e' не має вигляду ІМ'Я=ЗНАЧЕННЯ" "'$e' is not NAME=VALUE"
|
||||
done
|
||||
|
||||
# Build it aside, then move it in place in one step
|
||||
local tmp=$sv_dir/.$name.new
|
||||
rm -rf "$tmp"
|
||||
mkdir -p "$tmp/env"
|
||||
# chpst does not set these for the new user
|
||||
printf '%s\n' "$home" >"$tmp/env/HOME"
|
||||
printf '%s\n' "$user" >"$tmp/env/USER"
|
||||
for e in "${envs[@]}"; do
|
||||
printf '%s\n' "${e#*=}" >"$tmp/env/${e%%=*}"
|
||||
done
|
||||
cat >"$tmp/run" <<EOF
|
||||
#!/bin/sh
|
||||
# Created by dsv new. After editing: dsv restart $name
|
||||
# Environment variables: one file per variable in $sv_dir/$name/env
|
||||
exec 2>&1
|
||||
cd '$dir' || exit 1
|
||||
exec chpst -u $user -e $sv_dir/$name/env $run_cmd
|
||||
EOF
|
||||
chmod 755 "$tmp/run"
|
||||
if [[ -n $log ]]; then
|
||||
mkdir -p "$tmp/log" "/var/log/$name"
|
||||
cat >"$tmp/log/run" <<EOF
|
||||
#!/bin/sh
|
||||
# Log of $name, read with: dsv logs $name
|
||||
mkdir -p /var/log/$name
|
||||
exec svlogd -tt /var/log/$name
|
||||
EOF
|
||||
chmod 755 "$tmp/log/run"
|
||||
fi
|
||||
mv "$tmp" "$sv_dir/$name"
|
||||
say "$name: створена в $sv_dir/$name" "$name: created in $sv_dir/$name"
|
||||
|
||||
if [[ -z $enable ]] && interactive; then
|
||||
confirm "Увімкнути й запустити зараз?" "Enable and start it now?" && enable=1
|
||||
fi
|
||||
if [[ -n $enable ]]; then
|
||||
cmd_enable "$name"
|
||||
[[ $service_dir == /var/service ]] && check_running "$name"
|
||||
else
|
||||
say "Запустити: dsv enable $name" "To start it: dsv enable $name"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_rm() {
|
||||
local name=${1:-} i
|
||||
[[ -n $name ]] || die "вкажіть службу" "name a service"
|
||||
check_service "$name"
|
||||
# Services from packages come back with the next update; disable them instead
|
||||
# (xbps-query -o succeeds even when no package owns the file)
|
||||
if [[ -n $(xbps-query -o "$sv_dir/$name/run" 2>/dev/null) ]]; then
|
||||
die "$name встановлена пакетом, її можна лише вимкнути: dsv disable $name" \
|
||||
"$name comes from a package, it can only be disabled: dsv disable $name"
|
||||
fi
|
||||
if interactive && ! confirm "Видалити службу $name?" "Delete service $name?"; then
|
||||
return 1
|
||||
fi
|
||||
if is_enabled "$name"; then
|
||||
cmd_disable "$name"
|
||||
# runsvdir stops its runsv at the next scan; wait for it before deleting
|
||||
for ((i = 0; i < 20; i++)); do
|
||||
pgrep -x -f "runsv $name" >/dev/null || break
|
||||
sleep 0.5
|
||||
done
|
||||
fi
|
||||
rm -rf "${sv_dir:?}/$name"
|
||||
say "$name: видалена" "$name: deleted"
|
||||
[[ -d /var/log/$name ]] && say "Журнал залишився в /var/log/$name" "The log is kept in /var/log/$name"
|
||||
return 0
|
||||
}
|
||||
|
||||
cmd=${1:-list}
|
||||
[[ $# -gt 0 ]] && shift
|
||||
|
||||
@@ -269,6 +411,14 @@ case $cmd in
|
||||
[[ -r $socklog/messages/current ]] || need_root logs "$@"
|
||||
cmd_logs "$@"
|
||||
;;
|
||||
new | create)
|
||||
need_root new "$@"
|
||||
cmd_new "$@"
|
||||
;;
|
||||
rm | remove | delete)
|
||||
need_root rm "$@"
|
||||
cmd_rm "$@"
|
||||
;;
|
||||
-h | --help | help)
|
||||
usage
|
||||
;;
|
||||
+11
-2
@@ -3,7 +3,7 @@
|
||||
_dsv() {
|
||||
local cur=${COMP_WORDS[COMP_CWORD]} cmd=${COMP_WORDS[1]:-}
|
||||
if ((COMP_CWORD == 1)); then
|
||||
mapfile -t COMPREPLY < <(compgen -W "list status enable disable start stop restart logs help" -- "$cur")
|
||||
mapfile -t COMPREPLY < <(compgen -W "list status enable disable start stop restart logs new rm help" -- "$cur")
|
||||
return
|
||||
fi
|
||||
case $cmd in
|
||||
@@ -20,7 +20,16 @@ _dsv() {
|
||||
disable | start | stop | restart)
|
||||
mapfile -t COMPREPLY < <(compgen -W "$(ls /etc/runit/runsvdir/default 2>/dev/null)" -- "$cur")
|
||||
;;
|
||||
status | logs | log)
|
||||
new)
|
||||
case ${COMP_WORDS[COMP_CWORD - 1]} in
|
||||
--user) mapfile -t COMPREPLY < <(compgen -u -- "$cur") ;;
|
||||
--dir) mapfile -t COMPREPLY < <(compgen -d -- "$cur") ;;
|
||||
--cmd) mapfile -t COMPREPLY < <(compgen -c -- "$cur") ;;
|
||||
--env) ;;
|
||||
*) mapfile -t COMPREPLY < <(compgen -W "--cmd --user --dir --env --no-log --enable" -- "$cur") ;;
|
||||
esac
|
||||
;;
|
||||
status | logs | log | rm)
|
||||
if [[ $cmd == logs* ]] && ((COMP_CWORD == 3)); then
|
||||
mapfile -t COMPREPLY < <(compgen -W "-f" -- "$cur")
|
||||
else
|
||||
Executable
+247
@@ -0,0 +1,247 @@
|
||||
#!/bin/bash
|
||||
# dsys - system settings on Das OS: host name, time zone, language and
|
||||
# console keyboard, in place of hostnamectl, timedatectl and localectl.
|
||||
set -euo pipefail
|
||||
|
||||
# shellcheck source=helpers.sh
|
||||
. "${DAS_HELPERS:-/usr/lib/das/helpers.sh}"
|
||||
|
||||
zoneinfo=/usr/share/zoneinfo
|
||||
libc_locales=/etc/default/libc-locales
|
||||
keymaps=/usr/share/kbd/keymaps
|
||||
|
||||
usage() {
|
||||
if [[ -n $uk ]]; then
|
||||
cat <<'EOF'
|
||||
Використання: dsys [КОМАНДА] [ЗНАЧЕННЯ]
|
||||
|
||||
info огляд системи (типова команда)
|
||||
hostname [ІМ'Я] показати або змінити ім'я хоста
|
||||
timezone [ЗОНА] показати або змінити часовий пояс (Europe/Kyiv,
|
||||
достатньо й частини назви: Kyiv)
|
||||
timezone list [ФІЛЬТР] усі часові пояси
|
||||
locale [МОВА] показати або змінити мову системи (uk_UA, en_US)
|
||||
locale list [ФІЛЬТР] доступні мови; * — уже зібрані
|
||||
keymap [РОЗКЛАДКА] показати або змінити розкладку консолі (us, ua-utf)
|
||||
keymap list [ФІЛЬТР] доступні розкладки консолі
|
||||
|
||||
Мову й розкладку графічного сеансу кожен користувач задає в налаштуваннях
|
||||
Xfce і в das-welcome; dsys змінює типові для системи й консолі.
|
||||
EOF
|
||||
else
|
||||
cat <<'EOF'
|
||||
Usage: dsys [COMMAND] [VALUE]
|
||||
|
||||
info system overview (the default)
|
||||
hostname [NAME] show or change the host name
|
||||
timezone [ZONE] show or change the time zone (Europe/Kyiv,
|
||||
part of the name will do: Kyiv)
|
||||
timezone list [FILTER] all time zones
|
||||
locale [LANGUAGE] show or change the system language (uk_UA, en_US)
|
||||
locale list [FILTER] available languages; * marks the built ones
|
||||
keymap [MAP] show or change the console keyboard map (us, ua-utf)
|
||||
keymap list [FILTER] available console keyboard maps
|
||||
|
||||
In the graphical session every user picks the language and keyboard in the
|
||||
Xfce settings and in das-welcome; dsys changes the system and console defaults.
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
current_hostname() {
|
||||
cat /etc/hostname 2>/dev/null || hostname
|
||||
}
|
||||
|
||||
current_timezone() {
|
||||
local tz
|
||||
tz=$(readlink /etc/localtime 2>/dev/null || true)
|
||||
echo "${tz#*zoneinfo/}"
|
||||
}
|
||||
|
||||
current_locale() {
|
||||
sed -n 's/^LANG=//p' /etc/locale.conf 2>/dev/null | tr -d '"'
|
||||
}
|
||||
|
||||
rc_value() {
|
||||
sed -n "s/^$1=//p" /etc/rc.conf 2>/dev/null | tr -d '"' | tail -n 1
|
||||
}
|
||||
|
||||
# A graphical session keeps its X authority under the old host name
|
||||
session_warning() {
|
||||
if pgrep -x lightdm >/dev/null || [[ -n ${DISPLAY:-} ]]; then
|
||||
say "Перезавантажте комп'ютер: графічний сеанс не відкриватиме нових вікон зі старим іменем." \
|
||||
"Reboot: the graphical session will not open new windows under the old name."
|
||||
fi
|
||||
}
|
||||
|
||||
# 3 д 4 год 5 хв / 3 d 4 h 5 min
|
||||
uptime_text() {
|
||||
local s d h m
|
||||
read -r s _ </proc/uptime
|
||||
s=${s%.*}
|
||||
d=$((s / 86400))
|
||||
h=$((s % 86400 / 3600))
|
||||
m=$((s % 3600 / 60))
|
||||
if ((d > 0)); then
|
||||
say "$d д $h год $m хв" "$d d $h h $m min"
|
||||
elif ((h > 0)); then
|
||||
say "$h год $m хв" "$h h $m min"
|
||||
else
|
||||
say "$m хв" "$m min"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_info() {
|
||||
local ntp line
|
||||
pad "$(say "Ім'я хоста:" "Host name:")" 16; current_hostname
|
||||
pad "$(say "Система:" "System:")" 16; echo "$(uname -sr), $(uname -m)"
|
||||
pad "$(say "Працює:" "Up:")" 16; uptime_text
|
||||
pad "$(say "Час:" "Time:")" 16; date '+%Y-%m-%d %H:%M:%S %Z'
|
||||
pad "$(say "Часовий пояс:" "Time zone:")" 16; current_timezone
|
||||
if ntp=$(chronyc -n tracking 2>/dev/null); then
|
||||
if grep -q '^Leap status *: Normal' <<<"$ntp"; then
|
||||
ntp=$(say "синхронізовано (chrony)" "synchronized (chrony)")
|
||||
else
|
||||
ntp=$(say "не синхронізовано (chrony)" "not synchronized (chrony)")
|
||||
fi
|
||||
else
|
||||
ntp=$(say "не працює (dsv enable chronyd)" "not running (dsv enable chronyd)")
|
||||
fi
|
||||
pad "$(say "Синхронізація:" "Time sync:")" 16; echo "$ntp"
|
||||
pad "$(say "Мова:" "Language:")" 16; current_locale
|
||||
pad "$(say "Консоль:" "Console:")" 16
|
||||
echo "$(say розкладка keymap) $(rc_value KEYMAP || true), $(say шрифт font) $(rc_value FONT || true)"
|
||||
say "Мережа:" "Network:"
|
||||
ip -brief address 2>/dev/null | while read -r line; do
|
||||
[[ $line == lo\ * ]] || echo " $line"
|
||||
done
|
||||
}
|
||||
|
||||
cmd_hostname() {
|
||||
local new=${1:-} old
|
||||
if [[ -z $new ]]; then
|
||||
current_hostname
|
||||
return
|
||||
fi
|
||||
[[ $new =~ ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$ ]] ||
|
||||
die "'$new': малі латинські літери, цифри й дефіс (не на початку й не в кінці)" \
|
||||
"'$new': lowercase Latin letters, digits and hyphens (not first or last)"
|
||||
need_root hostname "$new"
|
||||
old=$(current_hostname)
|
||||
echo "$new" >/etc/hostname
|
||||
# The installers add "127.0.1.1 <name>"; keep the line in step
|
||||
if grep -qw -- "$old" /etc/hosts; then
|
||||
sed -i "/^[^#]/s/\([[:space:]]\)${old//./\\.}\([[:space:]]\|$\)/\1$new\2/g" /etc/hosts
|
||||
fi
|
||||
grep -qw -- "$new" /etc/hosts || printf '127.0.1.1\t%s\n' "$new" >>/etc/hosts
|
||||
hostname "$new"
|
||||
say "Ім'я хоста: $new" "Host name: $new"
|
||||
session_warning
|
||||
}
|
||||
|
||||
# All time zones, from tzdata's table plus UTC
|
||||
timezones() {
|
||||
{ awk '!/^#/ { print $3 }' "$zoneinfo/zone1970.tab"; echo UTC; } | sort -u
|
||||
}
|
||||
|
||||
cmd_timezone() {
|
||||
local zone=${1:-} matches line
|
||||
if [[ -z $zone ]]; then
|
||||
current_timezone
|
||||
return
|
||||
fi
|
||||
if [[ $zone == list ]]; then
|
||||
timezones | grep -i -- "${2:-}" || true
|
||||
return
|
||||
fi
|
||||
if ! timezones | grep -qx -- "$zone"; then
|
||||
# Kyiv -> Europe/Kyiv, when only one zone matches
|
||||
matches=$(timezones | grep -i -- "$zone" || true)
|
||||
if [[ $(grep -c . <<<"$matches") -eq 1 ]]; then
|
||||
zone=$matches
|
||||
elif [[ -n $matches ]]; then
|
||||
say "Кілька часових поясів підходять, уточніть:" "Several time zones match, be more specific:" >&2
|
||||
head -n 20 <<<"$matches" | while read -r line; do echo " $line"; done >&2
|
||||
(($(grep -c . <<<"$matches") <= 20)) || echo " ..." >&2
|
||||
exit 1
|
||||
else
|
||||
die "невідомий часовий пояс '$zone' (dsys timezone list)" "unknown time zone '$zone' (dsys timezone list)"
|
||||
fi
|
||||
fi
|
||||
need_root timezone "$zone"
|
||||
ln -sfn "$zoneinfo/$zone" /etc/localtime
|
||||
say "Часовий пояс: $zone, зараз $(date '+%H:%M %Z')" "Time zone: $zone, now $(date '+%H:%M %Z')"
|
||||
}
|
||||
|
||||
# Every UTF-8 locale glibc can build, with * for the enabled ones
|
||||
locales() {
|
||||
sed -n 's/^\(#\?\)\([^ ]*\.UTF-8\) UTF-8[[:space:]]*$/\2 \1/p' "$libc_locales" |
|
||||
awk '{ print ($2 == "#" ? " " : "* ") $1 }'
|
||||
}
|
||||
|
||||
cmd_locale() {
|
||||
local lang=${1:-} re
|
||||
if [[ -z $lang ]]; then
|
||||
current_locale
|
||||
return
|
||||
fi
|
||||
if [[ $lang == list ]]; then
|
||||
locales | grep -i -- "${2:-}" || true
|
||||
return
|
||||
fi
|
||||
[[ $lang == *.* ]] || lang+=.UTF-8
|
||||
re=${lang//./\\.}
|
||||
locales | grep -q "^. $re\$" ||
|
||||
die "невідома мова '$lang' (dsys locale list)" "unknown language '$lang' (dsys locale list)"
|
||||
need_root locale "$lang"
|
||||
if grep -q "^#$re " "$libc_locales"; then
|
||||
sed -i "s/^#\($re \)/\1/" "$libc_locales"
|
||||
say "Збираю $lang..." "Building $lang..."
|
||||
xbps-reconfigure -f glibc-locales >/dev/null
|
||||
fi
|
||||
# Keep the other settings, Void's default file also has LC_COLLATE=C
|
||||
if grep -q '^LANG=' /etc/locale.conf 2>/dev/null; then
|
||||
sed -i "s/^LANG=.*/LANG=$lang/" /etc/locale.conf
|
||||
else
|
||||
echo "LANG=$lang" >>/etc/locale.conf
|
||||
fi
|
||||
say "Мова системи: $lang (діє з наступного входу)" "System language: $lang (from the next login)"
|
||||
}
|
||||
|
||||
cmd_keymap() {
|
||||
local map=${1:-}
|
||||
if [[ -z $map ]]; then
|
||||
rc_value KEYMAP
|
||||
return
|
||||
fi
|
||||
if [[ $map == list ]]; then
|
||||
find "$keymaps" -name '*.map.gz' -printf '%f\n' | sed 's/\.map\.gz$//' | sort -u | grep -i -- "${2:-}" || true
|
||||
return
|
||||
fi
|
||||
[[ -n $(find "$keymaps" -name "$map.map.gz" -print -quit) ]] ||
|
||||
die "невідома розкладка '$map' (dsys keymap list)" "unknown keyboard map '$map' (dsys keymap list)"
|
||||
need_root keymap "$map"
|
||||
set_rc KEYMAP "$map"
|
||||
# The default console font has no Cyrillic letters
|
||||
if [[ $map == @(ua|ru|by|bg|mk|sr)* && -z $(rc_value FONT) ]]; then
|
||||
set_rc FONT cyr-sun16
|
||||
fi
|
||||
loadkeys "$map" >/dev/null 2>&1 || true
|
||||
say "Розкладка консолі: $map" "Console keyboard map: $map"
|
||||
}
|
||||
|
||||
cmd=${1:-info}
|
||||
[[ $# -gt 0 ]] && shift
|
||||
|
||||
case $cmd in
|
||||
info | status) cmd_info ;;
|
||||
hostname | host) cmd_hostname "$@" ;;
|
||||
timezone | tz) cmd_timezone "$@" ;;
|
||||
locale | lang) cmd_locale "$@" ;;
|
||||
keymap | keyboard) cmd_keymap "$@" ;;
|
||||
-h | --help | help) usage ;;
|
||||
*)
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,22 @@
|
||||
# bash completion for dsys
|
||||
# shellcheck shell=bash
|
||||
_dsys() {
|
||||
local cur=${COMP_WORDS[COMP_CWORD]}
|
||||
if ((COMP_CWORD == 1)); then
|
||||
mapfile -t COMPREPLY < <(compgen -W "info hostname timezone locale keymap help" -- "$cur")
|
||||
return
|
||||
fi
|
||||
((COMP_CWORD == 2)) || return
|
||||
case ${COMP_WORDS[1]} in
|
||||
timezone | tz)
|
||||
mapfile -t COMPREPLY < <(compgen -W "list UTC $(awk '!/^#/ { print $3 }' /usr/share/zoneinfo/zone1970.tab 2>/dev/null)" -- "$cur")
|
||||
;;
|
||||
locale | lang)
|
||||
mapfile -t COMPREPLY < <(compgen -W "list $(sed -n 's/^#\?\([^ ]*\)\.UTF-8 UTF-8[[:space:]]*$/\1/p' /etc/default/libc-locales 2>/dev/null)" -- "$cur")
|
||||
;;
|
||||
keymap | keyboard)
|
||||
mapfile -t COMPREPLY < <(compgen -W "list $(find /usr/share/kbd/keymaps -name '*.map.gz' -printf '%f\n' 2>/dev/null | sed 's/\.map\.gz$//')" -- "$cur")
|
||||
;;
|
||||
esac
|
||||
}
|
||||
complete -F _dsys dsys
|
||||
Executable
+396
@@ -0,0 +1,396 @@
|
||||
#!/bin/bash
|
||||
# duser - manage user accounts on Das OS: create them with the right groups,
|
||||
# set passwords, lock them, add SSH keys.
|
||||
set -euo pipefail
|
||||
|
||||
# shellcheck source=helpers.sh
|
||||
. "${DAS_HELPERS:-/usr/lib/das/helpers.sh}"
|
||||
|
||||
# Groups of every new user, as the desktop installer gives them; only the ones
|
||||
# that exist on this system are used
|
||||
user_groups="users audio video input storage network optical scanner lp kvm plugdev bluetooth"
|
||||
# Administrators also get sudo, Docker (root-equivalent) and the system logs
|
||||
admin_groups="wheel docker socklog"
|
||||
|
||||
uid_min=$(awk '$1 == "UID_MIN" { print $2 }' /etc/login.defs 2>/dev/null)
|
||||
uid_max=$(awk '$1 == "UID_MAX" { print $2 }' /etc/login.defs 2>/dev/null)
|
||||
uid_min=${uid_min:-1000}
|
||||
uid_max=${uid_max:-60000}
|
||||
|
||||
usage() {
|
||||
if [[ -n $uk ]]; then
|
||||
cat <<'EOF'
|
||||
Використання: duser КОМАНДА [КОРИСТУВАЧ] [...]
|
||||
|
||||
list користувачі: адміністратор, вхід, SSH-ключі, групи
|
||||
add [КОРИСТУВАЧ] створити; чого не вказано, те запитає:
|
||||
--admin адміністратор (sudo, docker, журнали)
|
||||
--key КЛЮЧ|ФАЙЛ публічний SSH-ключ
|
||||
--no-password без пароля, вхід лише за ключем
|
||||
--password-stdin пароль зі стандартного входу (для скриптів)
|
||||
del КОРИСТУВАЧ [--home] видалити (--home: разом із домашнім каталогом)
|
||||
passwd [КОРИСТУВАЧ] змінити пароль (без імені — свій)
|
||||
lock КОРИСТУВАЧ заборонити вхід (і за паролем, і за ключем)
|
||||
unlock КОРИСТУВАЧ дозволити вхід знову
|
||||
key КОРИСТУВАЧ [list] SSH-ключі користувача
|
||||
key КОРИСТУВАЧ add [КЛЮЧ|ФАЙЛ|-]
|
||||
додати ключ (рядок, файл .pub або stdin)
|
||||
key КОРИСТУВАЧ rm НОМЕР видалити ключ за номером зі списку
|
||||
groups КОРИСТУВАЧ [+ГРУПА|-ГРУПА...]
|
||||
показати групи, додати (+) чи прибрати (-);
|
||||
+wheel робить адміністратором
|
||||
|
||||
Зміни груп діють із наступного входу користувача.
|
||||
EOF
|
||||
else
|
||||
cat <<'EOF'
|
||||
Usage: duser COMMAND [USER] [...]
|
||||
|
||||
list users: administrator, login, SSH keys, groups
|
||||
add [USER] create one; asks for what is not given:
|
||||
--admin an administrator (sudo, docker, system logs)
|
||||
--key KEY|FILE a public SSH key
|
||||
--no-password no password, key login only
|
||||
--password-stdin read the password from stdin (for scripts)
|
||||
del USER [--home] delete (--home: with the home directory)
|
||||
passwd [USER] change the password (without a name: your own)
|
||||
lock USER forbid logins (by password and by key)
|
||||
unlock USER allow logins again
|
||||
key USER [list] the user's SSH keys
|
||||
key USER add [KEY|FILE|-]
|
||||
add a key (a line, a .pub file or stdin)
|
||||
key USER rm NUMBER remove a key by its number in the list
|
||||
groups USER [+GROUP|-GROUP...]
|
||||
show the groups, add (+) or remove (-) some;
|
||||
+wheel makes an administrator
|
||||
|
||||
Group changes apply from the user's next login.
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
|
||||
# A regular (human) account, not a system one
|
||||
check_user() {
|
||||
local uid
|
||||
uid=$(id -u "$1" 2>/dev/null) || die "немає користувача $1" "no user $1"
|
||||
((uid >= uid_min && uid <= uid_max)) ||
|
||||
die "$1 — системний обліковий запис, duser його не змінює" "$1 is a system account, duser leaves it alone"
|
||||
}
|
||||
|
||||
in_group() {
|
||||
id -nG "$1" | tr ' ' '\n' | grep -qx "$2"
|
||||
}
|
||||
|
||||
# existing GROUPS...: the given groups that exist, comma-separated
|
||||
existing() {
|
||||
local g out=()
|
||||
for g in "$@"; do
|
||||
getent group "$g" >/dev/null && out+=("$g")
|
||||
done
|
||||
local IFS=,
|
||||
echo "${out[*]}"
|
||||
}
|
||||
|
||||
# login_state USER: how the user can log in, from /etc/shadow
|
||||
login_state() {
|
||||
local hash expire today
|
||||
IFS=: read -r _ hash _ _ _ _ _ expire _ < <(getent shadow "$1")
|
||||
today=$(($(date +%s) / 86400))
|
||||
if [[ -n $expire ]] && ((expire <= today)); then
|
||||
say заблоковано locked
|
||||
elif [[ $hash == \$* ]]; then
|
||||
say пароль password
|
||||
else
|
||||
# "!", "*" or a "!"-locked hash: SSH keys still work
|
||||
say "лише ключ" "key only"
|
||||
fi
|
||||
}
|
||||
|
||||
keys_file() {
|
||||
echo "$(getent passwd "$1" | cut -d: -f6)/.ssh/authorized_keys"
|
||||
}
|
||||
|
||||
# The keys of a file, one per line, without comments and blank lines
|
||||
key_lines() {
|
||||
[[ -r $1 ]] || return 0
|
||||
grep -v '^[[:space:]]*\(#\|$\)' "$1" || true
|
||||
}
|
||||
|
||||
cmd_list() {
|
||||
local name uid home admin keys
|
||||
pad "$(say КОРИСТУВАЧ USER)" 16
|
||||
pad UID 6
|
||||
pad "$(say АДМІН ADMIN)" 6
|
||||
pad "$(say ВХІД LOGIN)" 12
|
||||
pad "$(say КЛЮЧІ KEYS)" 6
|
||||
say ГРУПИ GROUPS
|
||||
while IFS=: read -r name _ uid _ _ home _; do
|
||||
((uid >= uid_min && uid <= uid_max)) || continue
|
||||
admin=$(say ні no)
|
||||
in_group "$name" wheel && admin=$(say так yes)
|
||||
keys=$(key_lines "$home/.ssh/authorized_keys" | wc -l)
|
||||
pad "$name" 16
|
||||
pad "$uid" 6
|
||||
pad "$admin" 6
|
||||
pad "$(login_state "$name")" 12
|
||||
pad "$keys" 6
|
||||
id -nG "$name" | sed "s/^$name //; s/ /,/g"
|
||||
done < <(getent passwd | sort -t: -k3n)
|
||||
}
|
||||
|
||||
# add_key USER KEY|FILE|-: appends a validated key to the user's authorized_keys
|
||||
add_key() {
|
||||
local user=$1 src=$2 key file tmp group
|
||||
if [[ $src == - ]]; then
|
||||
key=$(cat)
|
||||
elif [[ -f $src ]]; then
|
||||
key=$(cat "$src")
|
||||
else
|
||||
key=$src
|
||||
fi
|
||||
key=$(printf '%s\n' "$key" | grep -v '^[[:space:]]*$' | head -n 1)
|
||||
tmp=$(mktemp)
|
||||
printf '%s\n' "$key" >"$tmp"
|
||||
if ! ssh-keygen -l -f "$tmp" >/dev/null 2>&1; then
|
||||
rm -f "$tmp"
|
||||
die "це не публічний SSH-ключ (потрібен рядок на кшталт 'ssh-ed25519 AAAA... ім'я')" \
|
||||
"this is not a public SSH key (expected a line like 'ssh-ed25519 AAAA... name')"
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
|
||||
file=$(keys_file "$user")
|
||||
# Same key type and body, whatever the comment
|
||||
if key_lines "$file" | awk -v k="$(awk '{ print $1, $2 }' <<<"$key")" '$1 " " $2 == k { found = 1 } END { exit !found }'; then
|
||||
say "$user: цей ключ уже є" "$user: this key is already there"
|
||||
return 0
|
||||
fi
|
||||
group=$(id -gn "$user")
|
||||
install -d -m 700 -o "$user" -g "$group" "$(dirname "$file")"
|
||||
printf '%s\n' "$key" >>"$file"
|
||||
chown "$user:$group" "$file"
|
||||
chmod 600 "$file"
|
||||
say "$user: ключ додано" "$user: key added"
|
||||
}
|
||||
|
||||
# set_password USER: asks on a terminal, reads one line from stdin otherwise
|
||||
set_password() {
|
||||
local password
|
||||
if interactive; then
|
||||
passwd "$1"
|
||||
else
|
||||
IFS= read -r password || die "пароль не передано" "no password given"
|
||||
[[ -n $password ]] || die "порожній пароль" "empty password"
|
||||
# -c: Void's PAM stack for chpasswd does not hash passwords itself
|
||||
printf '%s:%s\n' "$1" "$password" | chpasswd -c SHA512
|
||||
say "$1: пароль змінено" "$1: password changed"
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_add() {
|
||||
local name="" admin="" key="" password=ask groups
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
--admin) admin=1 ;;
|
||||
--key) key=${2:?}; shift ;;
|
||||
--no-password) password=none ;;
|
||||
--password-stdin) password=stdin ;;
|
||||
-*) die "невідомий параметр $1 (duser help)" "unknown option $1 (duser help)" ;;
|
||||
*) [[ -z $name ]] || die "зайвий аргумент $1" "extra argument $1"; name=$1 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ -z $name ]]; then
|
||||
interactive || die "вкажіть ім'я користувача" "name the user"
|
||||
ask name "Ім'я користувача (латиницею)" "User name"
|
||||
fi
|
||||
[[ $name =~ ^[a-z_][a-z0-9_-]{0,31}$ ]] ||
|
||||
die "ім'я '$name': малі латинські літери, цифри, '_', '-', до 32 символів" \
|
||||
"name '$name': lowercase Latin letters, digits, '_', '-', up to 32 characters"
|
||||
! id "$name" >/dev/null 2>&1 || die "користувач $name уже є" "user $name already exists"
|
||||
|
||||
if [[ -z $admin ]] && interactive && [[ $password != stdin ]]; then
|
||||
confirm "Адміністратор (sudo, docker, журнали)?" "Administrator (sudo, docker, system logs)?" && admin=1
|
||||
fi
|
||||
if [[ -z $key ]] && interactive && [[ $password != stdin ]]; then
|
||||
ask key "Публічний SSH-ключ: рядок або файл .pub (Enter — без ключа)" \
|
||||
"Public SSH key: a line or a .pub file (Enter for none)"
|
||||
fi
|
||||
[[ $password == ask ]] && ! interactive && password=none
|
||||
|
||||
# shellcheck disable=SC2086 # word lists
|
||||
groups=$(existing $user_groups ${admin:+$admin_groups})
|
||||
useradd -m -s /bin/bash -G "$groups" "$name"
|
||||
say "$name: створено, групи $groups" "$name: created, groups $groups"
|
||||
|
||||
# From here on the account exists: on a failure, say so instead of leaving
|
||||
# it half set up silently
|
||||
# shellcheck disable=SC2064 # $name is fixed by now
|
||||
trap "[[ \$? -eq 0 ]] || warn '$name створено не повністю; виправте через duser або видаліть: duser del $name --home' \
|
||||
'$name is only partly set up; fix it with duser or delete it: duser del $name --home'" EXIT
|
||||
[[ -n $key ]] && add_key "$name" "$key"
|
||||
case $password in
|
||||
ask | stdin) set_password "$name" ;;
|
||||
none)
|
||||
[[ -n $key ]] || warn "$name не має ні пароля, ні ключа і поки не зможе увійти: duser passwd $name" \
|
||||
"$name has neither a password nor a key and cannot log in yet: duser passwd $name"
|
||||
;;
|
||||
esac
|
||||
trap - EXIT
|
||||
}
|
||||
|
||||
# The current user (the one who ran sudo)
|
||||
me() {
|
||||
echo "${SUDO_USER:-$(id -un)}"
|
||||
}
|
||||
|
||||
cmd_del() {
|
||||
local name=${1:-} home="" others
|
||||
[[ -n $name ]] || die "вкажіть користувача" "name the user"
|
||||
[[ ${2:-} == --home ]] && home=1
|
||||
check_user "$name"
|
||||
[[ $name != "$(me)" ]] || die "не можна видалити себе" "you cannot delete yourself"
|
||||
if in_group "$name" wheel; then
|
||||
others=$(getent group wheel | cut -d: -f4 | tr ',' '\n' | grep -vx "$name" || true)
|
||||
[[ -n $others ]] || die "$name — єдиний адміністратор" "$name is the only administrator"
|
||||
fi
|
||||
if interactive; then
|
||||
confirm "Видалити користувача $name?" "Delete user $name?" || return 1
|
||||
if [[ -z $home ]] && confirm "Видалити й домашній каталог $(getent passwd "$name" | cut -d: -f6)?" \
|
||||
"Delete the home directory $(getent passwd "$name" | cut -d: -f6) too?"; then
|
||||
home=1
|
||||
fi
|
||||
fi
|
||||
# userdel refuses while the user has running processes, which is right.
|
||||
# Void creates no mail spools, so drop userdel's warning about it.
|
||||
local out
|
||||
if ! out=$(userdel ${home:+-r} "$name" 2>&1); then
|
||||
echo "$out" >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -v 'mail spool' <<<"$out" >&2 || true
|
||||
say "$name: видалено" "$name: deleted"
|
||||
}
|
||||
|
||||
cmd_passwd() {
|
||||
local name=${1:-}
|
||||
if [[ -z $name || $name == "$(id -un)" ]] && [[ $EUID -ne 0 ]]; then
|
||||
# Your own password: no sudo, passwd asks for the current one
|
||||
exec passwd
|
||||
fi
|
||||
need_root passwd "$@"
|
||||
name=${name:-$(me)}
|
||||
check_user "$name"
|
||||
set_password "$name"
|
||||
}
|
||||
|
||||
cmd_lock() {
|
||||
local name=${1:-}
|
||||
[[ -n $name ]] || die "вкажіть користувача" "name the user"
|
||||
check_user "$name"
|
||||
[[ $name != "$(me)" ]] || die "не можна заблокувати себе" "you cannot lock yourself out"
|
||||
# -L disables the password; an expired account also stops SSH key logins
|
||||
usermod -L -e 1 "$name"
|
||||
say "$name: вхід заборонено (поточні сеанси тривають; завершити: pkill -KILL -u $name)" \
|
||||
"$name: logins forbidden (open sessions go on; to end them: pkill -KILL -u $name)"
|
||||
}
|
||||
|
||||
cmd_unlock() {
|
||||
local name=${1:-} hash
|
||||
[[ -n $name ]] || die "вкажіть користувача" "name the user"
|
||||
check_user "$name"
|
||||
# (usermod -e '' complains "no changes" when the account never expired)
|
||||
chage -E -1 "$name"
|
||||
# Re-enable the password only when there is one under the "!"
|
||||
hash=$(getent shadow "$name" | cut -d: -f2)
|
||||
[[ $hash == \!\$* ]] && usermod -U "$name"
|
||||
say "$name: вхід дозволено" "$name: logins allowed"
|
||||
}
|
||||
|
||||
cmd_key() {
|
||||
local name=${1:-} action=${2:-list} file n i line
|
||||
[[ -n $name ]] || die "вкажіть користувача" "name the user"
|
||||
check_user "$name"
|
||||
file=$(keys_file "$name")
|
||||
case $action in
|
||||
list | ls)
|
||||
if [[ -z $(key_lines "$file") ]]; then
|
||||
say "$name: SSH-ключів немає" "$name: no SSH keys"
|
||||
return 0
|
||||
fi
|
||||
i=0
|
||||
while IFS= read -r line; do
|
||||
i=$((i + 1))
|
||||
printf '%2d %s\n' "$i" "$(ssh-keygen -l -f - <<<"$line" 2>/dev/null || echo "? $line")"
|
||||
done < <(key_lines "$file")
|
||||
;;
|
||||
add)
|
||||
local src=${3:-}
|
||||
if [[ -z $src ]]; then
|
||||
interactive || die "вкажіть ключ, файл або -" "give a key, a file or -"
|
||||
ask src "Публічний SSH-ключ: рядок або файл .pub" "Public SSH key: a line or a .pub file"
|
||||
fi
|
||||
add_key "$name" "$src"
|
||||
;;
|
||||
rm | del)
|
||||
n=${3:-}
|
||||
[[ $n =~ ^[1-9][0-9]*$ ]] || die "вкажіть номер ключа (duser key $name)" "give the key number (duser key $name)"
|
||||
line=$(key_lines "$file" | sed -n "${n}p")
|
||||
[[ -n $line ]] || die "немає ключа номер $n" "no key number $n"
|
||||
# Remove that exact line, keeping comments and the other keys
|
||||
awk -v k="$line" '$0 != k' "$file" >"$file.new"
|
||||
chown --reference="$file" "$file.new"
|
||||
chmod 600 "$file.new"
|
||||
mv "$file.new" "$file"
|
||||
say "$name: ключ $n видалено" "$name: key $n removed"
|
||||
;;
|
||||
*) die "невідома дія $action (list, add, rm)" "unknown action $action (list, add, rm)" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cmd_groups() {
|
||||
local name=${1:-} arg g
|
||||
[[ -n $name ]] || die "вкажіть користувача" "name the user"
|
||||
check_user "$name"
|
||||
shift
|
||||
for arg in "$@"; do
|
||||
g=${arg:1}
|
||||
case $arg in
|
||||
+?*)
|
||||
getent group "$g" >/dev/null || die "немає групи $g" "no group $g"
|
||||
gpasswd -a "$name" "$g" >/dev/null
|
||||
;;
|
||||
-?*)
|
||||
if [[ $g == wheel && $name == "$(me)" ]]; then
|
||||
die "не можна позбавити себе прав адміністратора" "you cannot drop your own administrator rights"
|
||||
fi
|
||||
gpasswd -d "$name" "$g" >/dev/null
|
||||
;;
|
||||
*) die "'$arg': потрібно +ГРУПА або -ГРУПА" "'$arg': expected +GROUP or -GROUP" ;;
|
||||
esac
|
||||
done
|
||||
echo "$name: $(id -nG "$name" | sed "s/^$name //; s/ /, /g")"
|
||||
}
|
||||
|
||||
cmd=${1:-list}
|
||||
[[ $# -gt 0 ]] && shift
|
||||
|
||||
case $cmd in
|
||||
list | ls)
|
||||
# /etc/shadow is root-only
|
||||
need_root list
|
||||
cmd_list
|
||||
;;
|
||||
add | new) need_root add "$@"; cmd_add "$@" ;;
|
||||
del | rm | remove) need_root del "$@"; cmd_del "$@" ;;
|
||||
passwd | password) cmd_passwd "$@" ;;
|
||||
lock) need_root lock "$@"; cmd_lock "$@" ;;
|
||||
unlock) need_root unlock "$@"; cmd_unlock "$@" ;;
|
||||
key | keys) need_root key "$@"; cmd_key "$@" ;;
|
||||
groups | group) need_root groups "$@"; cmd_groups "$@" ;;
|
||||
-h | --help | help) usage ;;
|
||||
*)
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,42 @@
|
||||
# bash completion for duser
|
||||
# shellcheck shell=bash
|
||||
_duser_users() {
|
||||
local min max
|
||||
min=$(awk '$1 == "UID_MIN" { print $2 }' /etc/login.defs 2>/dev/null)
|
||||
max=$(awk '$1 == "UID_MAX" { print $2 }' /etc/login.defs 2>/dev/null)
|
||||
getent passwd | awk -F: -v min="${min:-1000}" -v max="${max:-60000}" '$3 >= min && $3 <= max { print $1 }'
|
||||
}
|
||||
|
||||
_duser() {
|
||||
local cur=${COMP_WORDS[COMP_CWORD]} cmd=${COMP_WORDS[1]:-}
|
||||
if ((COMP_CWORD == 1)); then
|
||||
mapfile -t COMPREPLY < <(compgen -W "list add del passwd lock unlock key groups help" -- "$cur")
|
||||
return
|
||||
fi
|
||||
case $cmd in
|
||||
add)
|
||||
if [[ ${COMP_WORDS[COMP_CWORD - 1]} == --key ]]; then
|
||||
mapfile -t COMPREPLY < <(compgen -f -- "$cur")
|
||||
else
|
||||
mapfile -t COMPREPLY < <(compgen -W "--admin --key --no-password --password-stdin" -- "$cur")
|
||||
fi
|
||||
;;
|
||||
del | passwd | lock | unlock | key | groups)
|
||||
if ((COMP_CWORD == 2)); then
|
||||
mapfile -t COMPREPLY < <(compgen -W "$(_duser_users)" -- "$cur")
|
||||
elif [[ $cmd == del ]]; then
|
||||
mapfile -t COMPREPLY < <(compgen -W "--home" -- "$cur")
|
||||
elif [[ $cmd == key ]] && ((COMP_CWORD == 3)); then
|
||||
mapfile -t COMPREPLY < <(compgen -W "list add rm" -- "$cur")
|
||||
elif [[ $cmd == key && ${COMP_WORDS[3]} == add ]]; then
|
||||
mapfile -t COMPREPLY < <(compgen -f -- "$cur")
|
||||
elif [[ $cmd == groups ]]; then
|
||||
# +GROUP and -GROUP
|
||||
local sign=${cur:0:1}
|
||||
[[ $sign == [+-] ]] || sign=+
|
||||
mapfile -t COMPREPLY < <(compgen -P "$sign" -W "$(getent group | cut -d: -f1)" -- "${cur#[+-]}")
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
complete -F _duser duser
|
||||
@@ -0,0 +1,75 @@
|
||||
# Shared functions of the Das OS helpers (dsv, duser, dfw, dsys).
|
||||
# Sourced, not run: each helper sets "set -euo pipefail" itself.
|
||||
# shellcheck shell=bash
|
||||
|
||||
self=${0##*/}
|
||||
|
||||
if [[ ${LC_ALL:-} == uk* || ${LC_MESSAGES:-} == uk* || ${LANG:-} == uk* ]]; then
|
||||
uk=1
|
||||
else
|
||||
uk=""
|
||||
fi
|
||||
|
||||
# say UKRAINIAN ENGLISH: prints the message in the user's language
|
||||
say() {
|
||||
if [[ -n $uk ]]; then echo "$1"; else echo "$2"; fi
|
||||
}
|
||||
|
||||
warn() {
|
||||
say "$self: $1" "$self: $2" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
warn "$1" "$2"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# need_root ARGS...: runs the helper again through sudo with the same arguments
|
||||
need_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
exec sudo "$0" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Questions are only asked on a terminal; scripts must pass every argument
|
||||
interactive() {
|
||||
[[ -t 0 ]]
|
||||
}
|
||||
|
||||
# ask VAR UKRAINIAN ENGLISH [DEFAULT]: reads a line into VAR; the prompt shows
|
||||
# the default, which an empty answer keeps
|
||||
ask() {
|
||||
local -n _answer=$1
|
||||
local prompt
|
||||
prompt=$(say "$2" "$3")
|
||||
if [[ -n ${4:-} ]]; then
|
||||
prompt+=" [$4]"
|
||||
fi
|
||||
read -r -e -p "$prompt: " _answer || exit 1
|
||||
[[ -n $_answer ]] || _answer=${4:-}
|
||||
}
|
||||
|
||||
# confirm UKRAINIAN ENGLISH: a yes/no question, "no" by default
|
||||
confirm() {
|
||||
local reply
|
||||
read -r -p "$(say "$1 [т/Н]" "$2 [y/N]") " reply || return 1
|
||||
[[ ${reply,,} == @(y|yes|т|так) ]]
|
||||
}
|
||||
|
||||
# pad TEXT WIDTH: TEXT padded with spaces to WIDTH characters. printf pads by
|
||||
# bytes, ${#s} counts characters, so Cyrillic columns stay aligned.
|
||||
pad() {
|
||||
local width=$(($2 - ${#1}))
|
||||
((width > 0)) || width=0
|
||||
printf '%s%*s ' "$1" "$width" ''
|
||||
}
|
||||
|
||||
# set_rc VAR VALUE: sets VAR in /etc/rc.conf, uncommenting Void's example line
|
||||
set_rc() {
|
||||
local rc=${RC_CONF:-/etc/rc.conf}
|
||||
if grep -q "^#\?$1=" "$rc"; then
|
||||
sed -i "s|^#\?$1=.*|$1=\"$2\"|" "$rc"
|
||||
else
|
||||
echo "$1=\"$2\"" >>"$rc"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Template file for 'das-helpers'
|
||||
pkgname=das-helpers
|
||||
version=0.1.0
|
||||
revision=1
|
||||
depends="bash runit sudo shadow openssh nftables kbd"
|
||||
short_desc="Das OS helpers: services (dsv), users (duser), firewall (dfw), system (dsys)"
|
||||
maintainer="Das <anry.das@gmail.com>"
|
||||
license="MIT"
|
||||
homepage="https://git.das.org.ua/das/Das-OS"
|
||||
# dsv used to be a package of its own
|
||||
replaces="dsv>=0"
|
||||
provides="dsv-${version}_${revision}"
|
||||
|
||||
do_install() {
|
||||
vinstall ${FILESDIR}/helpers.sh 644 usr/lib/das
|
||||
for tool in dsv duser dfw dsys; do
|
||||
vbin ${FILESDIR}/${tool}
|
||||
vcompletion ${FILESDIR}/${tool}.bash-completion bash ${tool}
|
||||
done
|
||||
vlicense ${FILESDIR}/LICENSE
|
||||
}
|
||||
@@ -66,7 +66,7 @@ t() {
|
||||
s_config) echo "Налаштування системи..." ;;
|
||||
s_post) echo "Локалі, знімки, initramfs (кілька хвилин)..." ;;
|
||||
s_boot) echo "Встановлення завантажувача..." ;;
|
||||
finished) echo "Das OS Server встановлено. Вийміть інсталяційний носій і перезавантажте сервер.\n\nВхід: %s, далі sudo. Служби: dsv list. Оновлення: das-update." ;;
|
||||
finished) echo "Das OS Server встановлено. Вийміть інсталяційний носій і перезавантажте сервер.\n\nВхід: %s, далі sudo. Служби: dsv. Користувачі: duser. Порти: dfw. Налаштування: dsys. Оновлення: das-update." ;;
|
||||
reboot) echo "Перезавантажити зараз?" ;;
|
||||
failed) echo "Помилка встановлення. Журнал: %s" ;;
|
||||
root) echo "Інсталятор треба запускати від root (sudo das-server-install)." ;;
|
||||
@@ -105,7 +105,7 @@ t() {
|
||||
s_config) echo "Configuring the system..." ;;
|
||||
s_post) echo "Locales, snapshots, initramfs (a few minutes)..." ;;
|
||||
s_boot) echo "Installing the boot loader..." ;;
|
||||
finished) echo "Das OS Server is installed. Remove the installation medium and reboot.\n\nLog in as %s, then use sudo. Services: dsv list. Updates: das-update." ;;
|
||||
finished) echo "Das OS Server is installed. Remove the installation medium and reboot.\n\nLog in as %s, then use sudo. Services: dsv. Users: duser. Ports: dfw. Settings: dsys. Updates: das-update." ;;
|
||||
reboot) echo "Reboot now?" ;;
|
||||
failed) echo "Installation failed. Log: %s" ;;
|
||||
root) echo "The installer must run as root (sudo das-server-install)." ;;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Template file for 'das-server-installer'
|
||||
pkgname=das-server-installer
|
||||
version=0.1.0
|
||||
version=0.2.0
|
||||
revision=1
|
||||
depends="bash dialog das-install-common btrfs-progs dosfstools gptfdisk parted
|
||||
rsync util-linux grub grub-x86_64-efi efibootmgr"
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
#!/usr/sbin/nft -f
|
||||
# Das OS server firewall: incoming connections are closed except SSH.
|
||||
#
|
||||
# Only the "das" table is replaced, so loading this file again
|
||||
# (dsv restart nftables) keeps the rules of Docker and fail2ban.
|
||||
# Ports that Docker containers publish (-p 8080:80) pass through Docker's
|
||||
# own forwarding rules, not the input chain below.
|
||||
# Open ports with dfw (dfw allow 80/tcp), which keeps them in
|
||||
# /etc/nftables.d/dfw.nft. Ports that Docker containers publish (-p 8080:80)
|
||||
# pass through Docker's own forwarding rules, not the input chain below.
|
||||
#
|
||||
# After editing this file: dfw reload. Only the "das" table is replaced, so the
|
||||
# rules of Docker and fail2ban stay. Do not restart the nftables service for
|
||||
# that: its finish script flushes the whole ruleset, theirs too.
|
||||
|
||||
table inet das
|
||||
delete table inet das
|
||||
|
||||
table inet das {
|
||||
# Filled by dfw
|
||||
set tcp_ports {
|
||||
type inet_service; flags interval; auto-merge;
|
||||
}
|
||||
set udp_ports {
|
||||
type inet_service; flags interval; auto-merge;
|
||||
}
|
||||
|
||||
chain input {
|
||||
type filter hook input priority filter; policy drop;
|
||||
|
||||
@@ -24,8 +35,13 @@ table inet das {
|
||||
iifname "br-*" accept
|
||||
|
||||
tcp dport ssh accept
|
||||
tcp dport @tcp_ports accept
|
||||
udp dport @udp_ports accept
|
||||
|
||||
# Open more ports here, then: dsv restart nftables
|
||||
# tcp dport { http, https } accept
|
||||
# Rules of your own go here, e.g. a port for one network only:
|
||||
# ip saddr 192.168.1.0/24 tcp dport 5432 accept
|
||||
}
|
||||
}
|
||||
|
||||
# dfw's ports and drop-ins of your own
|
||||
include "/etc/nftables.d/*.nft"
|
||||
|
||||
@@ -4,7 +4,7 @@ version=0.2.0
|
||||
revision=1
|
||||
depends="openssh chrony dhcpcd NetworkManager docker docker-compose nftables
|
||||
fail2ban socklog-void python3 curl lazydocker lazygit mc tmux htop
|
||||
das-snapshots das-update dsv"
|
||||
das-snapshots das-update das-helpers xtools-minimal"
|
||||
short_desc="Das OS server: packages and secure defaults for SSH, firewall and Docker"
|
||||
maintainer="Das <anry.das@gmail.com>"
|
||||
license="MIT"
|
||||
@@ -18,6 +18,7 @@ conf_files="
|
||||
|
||||
do_install() {
|
||||
vinstall ${FILESDIR}/nftables.conf 644 etc
|
||||
vmkdir etc/nftables.d
|
||||
vinstall ${FILESDIR}/fail2ban-das.conf 644 etc/fail2ban/jail.d das.conf
|
||||
vinstall ${FILESDIR}/sshd-das.conf 644 etc/ssh/sshd_config.d 50-das.conf
|
||||
vinstall ${FILESDIR}/docker-daemon.json 644 etc/docker daemon.json
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
# Template file for 'dsv'
|
||||
pkgname=dsv
|
||||
version=0.1.0
|
||||
revision=1
|
||||
depends="bash runit sudo"
|
||||
short_desc="Das OS service manager: enable, start and read logs of runit services"
|
||||
maintainer="Das <anry.das@gmail.com>"
|
||||
license="MIT"
|
||||
homepage="https://git.das.org.ua/das/Das-OS"
|
||||
|
||||
do_install() {
|
||||
vbin ${FILESDIR}/dsv
|
||||
vcompletion ${FILESDIR}/dsv.bash-completion bash
|
||||
vlicense ${FILESDIR}/LICENSE
|
||||
}
|
||||
Reference in New Issue
Block a user