diff --git a/docs/manual.desktop b/docs/manual.desktop new file mode 100644 index 00000000..53f7aaf7 --- /dev/null +++ b/docs/manual.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Hartman Lab Server Manual +Type=Link +URL=https://docs.google.com/document/d/1K_KwAlv8Zljmy-enwmhT6gMTFutlAFglixvpLGBx0VY +Icon=text-html \ No newline at end of file diff --git a/script-files-permissions-reset b/script-files-permissions-reset deleted file mode 100755 index 475b4c08..00000000 --- a/script-files-permissions-reset +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -# Smartly change permissions on selected directories -# Copyright 2021 Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 - -is_root - -[[ $# -eq 0 ]] && DIRS=("/mnt/data") || DIRS=("$@") - -ask_ok "Reset permissions on ${DIRS[*]}?" - -chgrp smbgrp -R "${DIRS[@]}" && \ -chmod 6775 -R "${DIRS[@]}" - -exit $? \ No newline at end of file diff --git a/script-functions b/script-functions deleted file mode 100644 index 102dcf71..00000000 --- a/script-functions +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -# Common functions for the lab scripts -# Copyright Bryan C. Roessler - -# Don't run this script directly -[[ "${BASH_SOURCE[0]}" == "${0}" ]] && exit 0 - - -### VARS ### -export INSTALL_DIR=/usr/local/bin - - -### FUNCTIONS ### -prompt() { read -r -p "Enter $1: " "$1"; } - -ask_ok() { - declare response - (( YES_SWITCH )) && return 0 - read -r -p "$* [y/N]: " response - [[ ${response,,} =~ ^(yes|y)$ ]] -} - -is_root() { - [[ $EUID -gt 0 ]] && echo "Script must be run with sudo" && exit 1 -} - -copy_manual() { - cat <<-EOF > "$1/manual.desktop" - [Desktop Entry] - Encoding=UTF-8 - Name=Hartman Lab Server Manual - Type=Link - URL=https://docs.google.com/document/d/1K_KwAlv8Zljmy-enwmhT6gMTFutlAFglixvpLGBx0VY - Icon=text-html -EOF -} \ No newline at end of file diff --git a/script-install-manual-scripts b/script-install-manual-scripts deleted file mode 100755 index 18ea4a57..00000000 --- a/script-install-manual-scripts +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash -# This script will add scripts-* to the PATH and the manual to each user's desktop -# Copyright 2021-2024 Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -reload=0 -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || reload=1 - -sourcedir="/home/roessler/shared/hartmanlab" -original_dir="$PWD" - -if [[ "$original_dir" != "$sourcedir" ]]; then - pushd "$sourcedir" || exit $? -fi - -((reload)) && [[ -f $parent/script-functions ]] && . "$parent"/script-functions - -target=/usr/local/bin -for script in script-*; do - echo "Linking $script to $target" - [[ $script == "script-functions" ]] && install -m 644 "$script" "$target" - sudo ln -s "$script" "$target/" -done - -# Install manual link -remove=("manual.pdf" "manual.odt" "Notes.pdf" "Notes.odt" - "README.html" "Link to Manual.desktop" "manual-images" - "manual.html" "manual-images" "Manual.desktop" "manual.desktop") -for homedir in /home/*; do - desktop="$homedir/Desktop" - [[ -d $desktop ]] || continue - echo "Scanning $desktop for old manuals" - for f in "${remove[@]}"; do - [[ -e $desktop/$f || -L $desktop/$f ]] && - echo "Removing $desktop/$f" && - rm -f "${desktop:?}/$f" - done - echo "Installing manual to $desktop/manual.desktop" - cat <<-EOF > "$desktop/manual.desktop" - [Desktop Entry] - Encoding=UTF-8 - Name=Hartman Lab Server Manual - Type=Link - URL=https://docs.google.com/document/d/1K_KwAlv8Zljmy-enwmhT6gMTFutlAFglixvpLGBx0VY - Icon=text-html - EOF -done - diff --git a/script-install-motd b/script-install-motd deleted file mode 100755 index 34fd0d4a..00000000 --- a/script-install-motd +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/env bash -# Install and generate motd -# Bryan C. Roessler - -main() { - if [[ " $1 " == " --motd " ]]; then - print_motd - else - parent="${BASH_SOURCE[0]}" - parent=${parent%/*} - - [[ -f "$parent"/script-functions ]] && . "$parent"/script-functions || exit 1 - - is_root - - script="/usr/local/bin/${0##*/}" - service="/usr/lib/systemd/system/motd.service" - timer="/usr/lib/systemd/system/motd.timer" - - [[ -f $script ]] || cp "$0" /usr/local/bin/ - - install_services - fi -} - -print_motd() { - # colors - default='\e[0m' - green='\e[32m' - red='\e[31m' - dim='\e[2m' - undim='\e[0m' - -# shellcheck disable=SC2016 - echo -e \ -' _ _ _ _ _ -| | | | | | | | | | -| |__| | __ _ _ __| |_ _ __ ___ __ _ _ __ | | __ _| |__ -| __ |/ _` | `__| __| `_ ` _ \ / _` | `_ \ | | / _` | `_ \ -| | | | (_| | | | |_| | | | | | (_| | | | | | |___| (_| | |_) | -|_| |_|\__,_|_| \__|_| |_| |_|\__,_|_| |_| |______\__,_|_.__/ ' - - # System info - # get load averages - IFS=" " read -r LOAD1 LOAD5 LOAD15 <<<"$(awk '{ print $1,$2,$3 }' /proc/loadavg)" - # get free memory - IFS=" " read -r USED AVAIL TOTAL <<<"$(free -htm | grep "Mem" | awk '{print $3,$7,$2}')" - # get processes - PROCESS=$(ps -eo user=|sort|uniq -c | awk '{ print $2 " " $1 }') - PROCESS_ALL=$(echo "$PROCESS"| awk '{print $2}' | awk '{ SUM += $1} END { print SUM }') - PROCESS_ROOT=$(echo "$PROCESS"| grep root | awk '{print $2}') - PROCESS_USER=$(echo "$PROCESS"| grep -v root | awk '{print $2}' | awk '{ SUM += $1} END { print SUM }') - # get processors - PROCESSOR_NAME=$(grep "model name" /proc/cpuinfo | cut -d ' ' -f3- | awk '{print $0}' | head -1) - PROCESSOR_COUNT=$(grep -ioP 'processor\t:' /proc/cpuinfo | wc -l) - - echo -e " - ${default}Distro......: $default$(cat /etc/*release | grep "PRETTY_NAME" | cut -d "=" -f 2- | sed 's/"//g') - ${default}Kernel......: $default$(uname -sr) - ${default}Uptime......: $default$(uptime -p) - ${default}Load........: $green$LOAD1$default (1m), $green$LOAD5$default (5m), $green$LOAD15$default (15m) - ${default}Processes...: $default$green$PROCESS_ROOT$default (root), $green$PROCESS_USER$default (user), $green$PROCESS_ALL$default (total) - ${default}CPU.........: $default$PROCESSOR_NAME ($green$PROCESSOR_COUNT$default vCPU) - ${default}Memory......: $green$USED$default used, $green$AVAIL$default avail, $green$TOTAL$default total$default" - - # Disk usage - # config - max_usage=90 - bar_width=50 - - # disk usage: ignore zfs, squashfs & tmpfs - while IFS= read -r line; do dfs+=("$line"); done < <(df -H -x zfs -x squashfs -x tmpfs -x devtmpfs -x overlay --output=target,pcent,size | tail -n+2) - printf "\nDisk usage\n" - - for line in "${dfs[@]}"; do - # get disk usage - usage=$(echo "$line" | awk '{print $2}' | sed 's/%//') - used_width=$(((usage*bar_width)/100)) - # color is green if usage < max_usage, else red - if [ "${usage}" -ge "${max_usage}" ]; then - color=$red - else - color=$green - fi - # print green/red bar until used_width - bar="[${color}" - for ((i=0; i> "$script" - # # config - # MAX_TEMP=40 - # # set column width - # COLUMNS=2 - # # colors - - # # disks to check - # disks=(sda sdb sdc sdd sde sdf sdg sdi) - # disknames=(sda sdb sdc sdd sde sdf sdg sdi) - - # # hddtemp - # hddtemp_host=localhost - # hddtemp_port=7634 - - # # logfiles to check - # logfiles='/var/log/syslog /var/log/syslog.1' - - # # get all lines with smartd entries from syslog - # lines=$(tac $logfiles | grep -hiP 'smartd\[[[:digit:]]+\]:' | grep -iP "previous self-test") - # # use nc to query temps from hddtemp daemon - # hddtemp=$(timeout 0.01 nc $hddtemp_host $hddtemp_port | sed 's/|//m' | sed 's/||/ \n/g') - - # out="" - # for i in "${!disks[@]}"; do - # disk=${disks[$i]} - # # use disknames if given - # diskname=${disknames[$i]} - # if [ -z "${diskname}" ]; then - # diskname=$disk - # fi - # uuid=$(blkid -s UUID -o value "/dev/${disk}") - # status=$( (grep "${uuid}" <<< "${lines}") | grep -m 1 -oP "previous self-test.*" | awk '{ print $4 " " $5 }') - # temp=$( (grep "${disk}" <<< "${hddtemp}") | awk -F'|' '{ print $3 }') - - # # color green if temp <= MAX_TEMP, else red - # if [[ "${temp}" -gt "${MAX_TEMP}" ]]; then - # color=$red - # else - # color=$green - # fi - # # add "C" if temp is numeric - # if [[ "$temp" =~ ^[0-9]+$ ]]; then - # temp="${temp}C" - # fi - # # color green if status is "without error", else red - # if [[ "${status}" == "without error" ]]; then - # status_color=$green - # else - # status_color=$red - # fi - - # # print temp & smartd error - # out+="${diskname}:,${color}${temp}${undim} | ${status_color}${status}${undim}," - # # insert \n every $COLUMNS column - # if [ $((($i+1) % $COLUMNS)) -eq 0 ]; then - # out+="\n" - # fi - # done - # out+="\n" - - # printf "\ndisk status:\n" - # printf "$out" | column -ts $',' | sed -e 's/^/ /' - # EOF - - - # Services - COLUMNS=2 - - services=( - btrfs-balance.timer btrfs-scrub.timer backup.timer btrbk.timer fstrim.timer - fail2ban firewalld smb nmb motion smartd cockpit.socket - dnf-automatic.timer motd.timer - ) - - service_status=() - # get status of all services - for service in "${services[@]}"; do - service_status+=("$(systemctl is-active "$service")") - done - - out="" - for i in "${!services[@]}"; do - # color green if service is active, else red - if [[ "${service_status[$i]}" == "active" ]]; then - out+="${services[$i]%.*}:,${green}${service_status[$i]}${undim}," - else - out+="${services[$i]%.*}:,${red}${service_status[$i]}${undim}," - fi - # insert \n every $COLUMNS column - if [[ $(((i+1) % COLUMNS)) -eq 0 ]]; then - out+="\n" - fi - done - - printf "\nServices\n" - printf "%b\n" "$out" | column -ts $',' | sed -e 's/^/ /' - - - # Fail2Ban - # fail2ban-client status to get all jails, takes about ~70ms - read -r -a jails <<< "$(fail2ban-client status | grep "Jail list:" | sed "s/ //g" | awk '{split($2,a,",");for(i in a) print a[i]}')" - - out="jail,failed,total,banned,total\n" - - for jail in "${jails[@]}"; do - # slow because fail2ban-client has to be called for every jail (~70ms per jail) - status=$(fail2ban-client status "$jail") - failed=$(echo "$status" | grep -ioP '(?<=Currently failed:\t)[[:digit:]]+') - totalfailed=$(echo "$status" | grep -ioP '(?<=Total failed:\t)[[:digit:]]+') - banned=$(echo "$status" | grep -ioP '(?<=Currently banned:\t)[[:digit:]]+') - totalbanned=$(echo "$status" | grep -ioP '(?<=Total banned:\t)[[:digit:]]+') - out+="$jail,$failed,$totalfailed,$banned,$totalbanned\n" - done - - printf "\nFail2ban\n" - printf "%b\n" "$out" | column -ts $',' | sed -e 's/^/ /' - - # Help links - echo -e ' -Links (ctrl+click to follow) - Server Manual.........: https://tinyurl.com/jjz9h6fr - Cockpit (for admins)..: http://localhost:9090 - Robot Camera..........: http://localhost:9999 - JupyterLab............: http://localhost:8888 - RStudio Server........: http://localhost:8787 - Robot Computer........: vnc://192.168.16.101:5900 - Windows 10 VM.........: vnc://localhost:5900 (pw: hartman) - ' - - # Scheduled reboot - if systemctl is-active scheduled-reboot.timer &>/dev/null; then - echo -n "Next scheduled reboot: " - time=$(systemctl cat scheduled-reboot.timer | grep OnCalendar=) - time=${time#*=} - echo "$time" - fi -} - -install_services() { - cat <<-EOF > "$service" - [Unit] - Description=Generate MoTD - - [Service] - Type=simple - ExecStart=/usr/bin/bash -c '$script --motd > /etc/motd' - - [Install] - WantedBy=default.target - EOF - cat <<-'EOF' > "$timer" - [Unit] - Description=Generate MoTD every minute on a timer - - [Timer] - OnCalendar=*:0/1 - OnBootSec=10s - - [Install] - WantedBy=timers.target - EOF - - chmod +x "$script" && - systemctl daemon-reload && - systemctl enable --now "${timer##*/}" -} - -main "$@" - -exit $? \ No newline at end of file diff --git a/script-install-reverse-proxy b/script-install-reverse-proxy deleted file mode 100644 index 66756e26..00000000 --- a/script-install-reverse-proxy +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash -# Adds a reverse proxy for local system services -# Copyright 2021 Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 - -is_root - -dnf install -y nginx || exit $? - -cat <<- 'EOF' > /etc/nginx/conf.d/hartmanlab.conf -server { - listen 80 default_server; - server_name localhost; - location /cockpit { - proxy_pass http://localhost:9090; - } -} -EOF - - # location /robot { - # proxy_pass http://127.0.0.1:8888; - # proxy_redirect http://127.0.0.1:8888/; - # } - -systemctl enable --now nginx - -exit $? diff --git a/script-system-update b/script-system-update deleted file mode 100755 index c108393c..00000000 --- a/script-system-update +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -# Update the system -# Copyright 2021 Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 - -is_root - -ask_ok "Security updates are automatically installed, perform a full system update?" || exit $? - -dnf update --refresh diff --git a/script-tree-to-markdown b/script-tree-to-markdown deleted file mode 100644 index 0f12b750..00000000 --- a/script-tree-to-markdown +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -# Make a nice markdown file from a dir tree -# Copyright 2021 Bryan C. Roessler - -tree=$(tree -f --noreport --charset ascii "$1" | -sed -e 's/| \+/ /g' -e 's/[|`]-\+/ */g' -e 's:\(* \)\(\(.*/\)\([^/]\+\)\):\1[\4](\2):g') -printf "# Code/Directory Structure:\n\n%s" "$tree" \ No newline at end of file diff --git a/script-user-add b/script-user-add deleted file mode 100755 index f2ec1261..00000000 --- a/script-user-add +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env bash -# Add a user to the Hartman Lab server -# Copyright Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 - -is_root - -echo "This script supports two optional arguments, a username and password" - -if [[ $# -eq 0 ]]; then - prompt user - prompt password -elif [[ $# -eq 1 ]]; then - user="$1" - prompt password -elif [[ $# -eq 2 ]]; then - user="$1" - password="$2" -elif [[ $# -gt 2 ]]; then - echo "Too many arguments provided" - exit 1 -fi - -useradd_cmd=(useradd -m -U) - -if id -u "$user" &>/dev/null; then - ask_ok "User $user exists. Run script-user-remove first?" || exit $? - "$parent"/script-user-remove "$user" || exit $? -fi - -ask_ok "Create user $user with password $password?" || exit $? - -restore=0 -if [[ -d /mnt/array/home-retired/$user ]]; then - ask_ok "Restore user $user's files from /mnt/array/home-retired/$user?" && restore=1 -fi - -samba=0 -ask_ok "Enable shared file access for user $user?" && group_str="smbgrp" && samba=1 - -ask_ok "Make $user an admin?" && \ - group_str+=",wheel" - -useradd_cmd+=("-G" "$group_str") -useradd_cmd+=("$user") - -if (( restore )); then - if rsync -av --progress=info2 /mnt/array/home-retired/"$user" /home/"$user"; then - ask_ok "User $user's files successfully restored, remove backup at /mnt/array/home-retired/$user?" && \ - rm -rf /mnt/array/home-retired/"$user" - fi -fi - -# echo "Running: ${useradd_cmd[*]}" -"${useradd_cmd[@]}" -echo "$user":"$password" | chpasswd - - -if (( samba )); then - (echo "$password"; echo "$password") | smbpasswd -a -s "$user" -fi - -ask_ok "Prompt user to reset password on next login?" && -passwd --expire "$user" && -echo "NOTE: The file sharing (smbpasswd) will not be changed" - -# TODO check if centos 9 does by default -# Add subuids & subgids for container namespace -# id_offset=100000 -# id_num=65536 -# last_uid=$(tail -1 /etc/subuid | cut -d':' -f2) -# last_gid=$(tail -1 /etc/subgid | cut -d':' -f2) -# start_uid=$(( last_uid + id_offset )) -# start_gid=$(( last_gid + id_offset )) -# echo "$user:$start_uid:$id_num" >> /etc/subuid -# echo "$user:$start_gid:$id_num" >> /etc/subgid - -# Copy manual to user desktop -desktop="/home/$user/Desktop" -[[ -d $desktop ]] || sudo -u "$user" mkdir -p "$desktop" -copy_manual "$desktop" - -exit 0 \ No newline at end of file diff --git a/script-user-remove b/script-user-remove deleted file mode 100755 index 19ef56eb..00000000 --- a/script-user-remove +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -# Remove a user from the server -# Copyright 2021-2023 Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 - -is_root - -echo "This script supports one optional argument, a username" - -if [[ $# -eq 1 ]]; then - user="$1" -else - prompt user -fi - -if ! id -u "$user" &>/dev/null; then - echo "User $user does not exist" - exit 1 -fi - -ask_ok "Remove user $user?" || exit 1 - -killall -u "$user" - -if ask_ok "Backup /home/$user?" && [[ -d /home/$user ]]; then - if [[ ! -d /mnt/array/home-retired/$user ]]; then - btrfs subvolume create /mnt/array/home-retired/"$user" || exit 1 - fi - rsync -av /home/"$user"/ /mnt/array/home-retired/"$user" || exit 1 -fi - -smbpasswd -x "$user" - -sed -i "/$user/d" /etc/subuid -sed -i "/$user/d" /etc/subgid - -if ! userdel -fr "$user"; then - ask_ok "Userdel failed, kill processes again and retry?" || exit 1 - killall -u "$user" -s SIGKILL - userdel -fr "$user" -fi - -exit $? diff --git a/script-user-reset-password b/script-user-reset-password deleted file mode 100755 index 8bd57154..00000000 --- a/script-user-reset-password +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash -# This script will reset a user password on the server -# Copyright 2021-24 Bryan C. Roessler - -unset user password - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 - -is_root - -echo "This script supports two optional arguments, a username and password" - -if [[ $# -eq 0 ]]; then - prompt user - prompt password -elif [[ $# -eq 1 ]]; then - user="$1" - prompt password -elif [[ $# -eq 2 ]]; then - user="$1" - password="$2" -elif [[ $# -gt 2 ]]; then - echo "Too many arguments provided" - exit 1 -fi - -if ! id -u "$user" &>/dev/null; then - echo "User $user does not exist" - exit 1 -fi - -if ask_ok "Change user $user's password to $password?"; then - echo "$user":"$password" | chpasswd - (echo "$password"; echo "$password") | smbpasswd -a -s "$user" -fi - -ask_ok "Prompt user to reset password on next login?" && -passwd --expire "$user" && -echo "NOTE: The file sharing (smbpasswd) will not be changed" - -exit 0 diff --git a/README.md b/scripts/README.md similarity index 100% rename from README.md rename to scripts/README.md diff --git a/scripts/script-deploy-manual b/scripts/script-deploy-manual new file mode 100644 index 00000000..7296b370 --- /dev/null +++ b/scripts/script-deploy-manual @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# This script installs the Hartman Lab Server Manual to each user's desktop +# Usage: script-deploy-manual USERNAME|--all +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 + +script-deploy-manual() { + local user_arg="$1" + local manual_url="https://docs.google.com/document/d/1K_KwAlv8Zljmy-enwmhT6gMTFutlAFglixvpLGBx0VY" + local remove=("manual.pdf" "manual.odt" "Notes.pdf" "Notes.odt" \ + "README.html" "Link to Manual.desktop" "manual-images" \ + "manual.html" "Manual.desktop" "manual.desktop") + local users=() + + if [[ "$user_arg" == "--all" ]]; then + for d in /home/*; do [[ -d $d ]] && users+=("${d##*/}"); done + else + users+=("$user_arg") + fi + + for user in "${users[@]}"; do + desktop="/home/$user/Desktop" + [[ -d $desktop ]] || continue + echo "Scanning $desktop for old manuals" + for f in "${remove[@]}"; do + if [[ -e $desktop/$f || -L $desktop/$f ]]; then + echo "Removing $desktop/$f" + rm -f "${desktop:?}/$f" + fi + done + echo "Installing manual to $desktop/manual.desktop" + cat <<-EOF > "$desktop/manual.desktop" + [Desktop Entry] + Encoding=UTF-8 + Name=Hartman Lab Server Manual + Type=Link + URL=$manual_url + Icon=text-html + EOF + done +} + +# Allow script to be safely sourced +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + script-deploy-manual "$@" + exit $? +fi \ No newline at end of file diff --git a/scripts/script-deploy-scripts b/scripts/script-deploy-scripts new file mode 100755 index 00000000..768be36a --- /dev/null +++ b/scripts/script-deploy-scripts @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Adds the scripts directory to the global PATH +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 + +script-deploy-scripts() { + local profile_file="/etc/profile.d/99-hartmanlab_server_scripts.sh" + echo "Adding $p to global PATH in $profile_file" + sudo bash -c "echo 'export PATH=\"\$PATH:$p\"' > '$profile_file'" +} + +# Allow script to be safely sourced +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + script-deploy-scripts + exit +fi diff --git a/script-drives-fix-btrfs-full b/scripts/script-drives-fix-btrfs-full similarity index 55% rename from script-drives-fix-btrfs-full rename to scripts/script-drives-fix-btrfs-full index 348833f6..a9a64368 100755 --- a/script-drives-fix-btrfs-full +++ b/scripts/script-drives-fix-btrfs-full @@ -1,11 +1,8 @@ #!/usr/bin/env bash # Fix the btrfs out of space error -# Copyright 2021 Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 is_root @@ -16,5 +13,3 @@ for f in "${fs[@]}"; do btrfs balance start -dusage=0 "$f" btrfs balance start -musage=0 "$f" done - -exit $? \ No newline at end of file diff --git a/scripts/script-files-permissions-reset b/scripts/script-files-permissions-reset new file mode 100755 index 00000000..21b0cf04 --- /dev/null +++ b/scripts/script-files-permissions-reset @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Smartly change permissions on selected directories +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 + +is_root + +[[ $# -eq 0 ]] && DIRS=("/mnt/data") || DIRS=("$@") + +ask_ok "Reset permissions on ${DIRS[*]}?" + +if ! chgrp smbgrp -R "${DIRS[@]}"; then + echo "Failed to change group ownership" >&2 + exit 1 +fi + +if ! chmod 6775 -R "${DIRS[@]}"; then + echo "Failed to change permissions" >&2 + exit 1 +fi + +exit 0 \ No newline at end of file diff --git a/script-files-permissions-set b/scripts/script-files-permissions-set similarity index 69% rename from script-files-permissions-set rename to scripts/script-files-permissions-set index 01565d38..27eb2418 100755 --- a/script-files-permissions-set +++ b/scripts/script-files-permissions-set @@ -1,11 +1,8 @@ #!/usr/bin/env bash -# Smartly change permissions on selected directories -# Copyright 2021 Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 +# Intelligently change permissions on selected directories +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 is_root @@ -36,7 +33,7 @@ for path in "${paths[@]}"; do fi og_user=$(stat -c "%U" "$path") og_group=$(stat -c "%G" "$path") - echo -e "PATH\tUSER\tGROUP" + printf "%-20s %-10s %-10s\n" "PATH" "USER" "GROUP" echo -e "$path\t$og_user\t$og_group" if [[ "$group" != "smbgrp" || "$og_group" != "smbgrp" ]]; then echo "$path is not world accessible by the smbgrp group" @@ -44,19 +41,19 @@ for path in "${paths[@]}"; do fi done - ask_ok "Apply user: $user and group: $group to ${paths[*]} and all subdirs?" && \ chown -R "$user":"$group" "${paths[@]}" +# Set mode based on group: +# - 6775: Enables read/write/execute for owner and group, with setgid bit for group inheritance. +# - 755: Enables read/write/execute for owner, and read/execute for group and others. [[ "$group" == "smbgrp" ]] && mode=6775 || mode=755 ask_ok "Apply chmod $mode to ${paths[*]} and all subdirs?" && \ - chmod -R $mode "${paths[@]}" + chmod -R "$mode" "${paths[@]}" # Let's do it in less steps (see above) for now unless it becomes a problem +# TODO: Implement setuid/setgid functionality to ensure files and directories inherit user/group permissions. # echo "Apply setuid/setgid bits to ${paths[*]} and all subdirs?" # ask_ok "Files/dirs will inherit their " && \ # chmod -R g+s,u+s "${paths[@]}" - - -exit $? \ No newline at end of file diff --git a/scripts/script-functions b/scripts/script-functions new file mode 100644 index 00000000..499b411b --- /dev/null +++ b/scripts/script-functions @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# Common functions for the lab scripts +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 + +export INSTALL_DIR=/usr/local/bin + +prompt() { + local user_input + read -r -p "Enter $1: " user_input + echo "$user_input" +} + +ask_ok() { + declare response + ((YES_SWITCH)) && return 0 + read -r -p "$* [y/N]: " response + [[ ${response,,} =~ ^(yes|y)$ ]] +} + +is_root() { + [[ $EUID -gt 0 ]] && echo "Script must be run with sudo" && exit 1 +} + diff --git a/script-install-btrfsmaintenance b/scripts/script-install-btrfsmaintenance similarity index 89% rename from script-install-btrfsmaintenance rename to scripts/script-install-btrfsmaintenance index a1d59b7c..4c95fb15 100755 --- a/script-install-btrfsmaintenance +++ b/scripts/script-install-btrfsmaintenance @@ -1,11 +1,8 @@ #!/usr/bin/env bash # Generic btrfsmaintenance install script -# Copyright 2021 Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 is_root @@ -57,5 +54,3 @@ systemctl daemon-reload # Optionally, start and enable the services # systemctl enable --now btrfs-scrub - -exit $? \ No newline at end of file diff --git a/scripts/script-install-motd b/scripts/script-install-motd new file mode 100755 index 00000000..439a967c --- /dev/null +++ b/scripts/script-install-motd @@ -0,0 +1,220 @@ +#!/usr/bin/env bash +# Install and generate motd +# Copyright 2019-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +set -euo pipefail + +PROG_NAME="$(basename "$0")" +INSTALL_PATH="/usr/local/bin/$PROG_NAME" +SERVICE_UNIT="/usr/lib/systemd/system/motd.service" +TIMER_UNIT="/usr/lib/systemd/system/motd.timer" + +usage() { + cat <<-EOF + Usage: $PROG_NAME [--motd | --install] [--help] + + Options: + --motd Output MOTD to stdout (used by systemd) + --install Instal motd systemd service and timer + -h, --help Show this help message +EOF +} + +print_motd() { + # ANSI color codes + local default='\033[0m' green='\033[0;32m' red='\033[0;31m' + local dim='\033[2m' undim='\033[22m' + + cat <<-'EOF' + _ _ _ _ _ + | | | | | | | | | | + | |__| | __ _ _ __| |_ _ __ ___ __ _ _ __ | | __ _| |__ + | __ |/ _` | `__| __| `_ ` _ \ / _` | `_ \ | | / _` | `_ \ + | | | | (_| | | | |_| | | | | | (_| | | | | | |___| (_| | |_) | + |_| |_|\__,_|_| \__|_| |_| |_|\__,_|_| |_| |______\__,_|_.__/ + EOF + + # System info + read -r LOAD1 LOAD5 LOAD15 < <(awk '{print $1, $2, $3}' /proc/loadavg) + read -r USED AVAIL TOTAL < <(free -htm | awk '/^Mem:/ {print $3, $7, $2}') + + local ROOT_PROCS NONROOT_PROCS TOTAL_PROCS + ROOT_PROCS=$(pgrep -u root | wc -l) + NONROOT_PROCS=$(( $(ps -eo user= | wc -l) - ROOT_PROCS )) + TOTAL_PROCS=$(( ROOT_PROCS + NONROOT_PROCS )) + + local CPU_MODEL CPU_COUNT + CPU_MODEL=$(awk -F': ' '/model name/ {print $2; exit}' /proc/cpuinfo) + CPU_COUNT=$(grep -c '^processor' /proc/cpuinfo) + + . /etc/os-release + local DISTRO="$PRETTY_NAME" + local KERNEL; KERNEL="$(uname -sr)" + local UPTIME; UPTIME="$(uptime -p)" + + # Load + printf "\n" + printf " Distro......: %s\n" "$DISTRO" + printf " Kernel......: %s\n" "$KERNEL" + printf " Uptime......: %s\n" "$UPTIME" + printf " Load........: %b%s%b (1m), %b%s%b (5m), %b%s%b (15m)\n" \ + "$green" "$LOAD1" "$default" \ + "$green" "$LOAD5" "$default" \ + "$green" "$LOAD15" "$default" + printf " Processes...: %b%d%b (root), %b%d%b (user), %b%d%b (total)\n" \ + "$green" "$ROOT_PROCS" "$default" \ + "$green" "$NONROOT_PROCS" "$default" \ + "$green" "$TOTAL_PROCS" "$default" + printf " CPU.........: %s (%b%d%b vCPU)\n" \ + "$CPU_MODEL" "$green" "$CPU_COUNT" "$default" + printf " Memory......: %b%s%b used, %b%s%b avail, %b%s%b total\n" \ + "$green" "$USED" "$default" \ + "$green" "$AVAIL" "$default" \ + "$green" "$TOTAL" "$default" + + # Disks + echo -e "\n Disk usage" + local max_usage=90 bar_width=50 + while read -r target pcent size; do + local use=${pcent%\%} + local filled=$(( use * bar_width / 100 )) + local bar="[" + + local col=$green + (( use >= max_usage )) && col=$red + + bar+="$col" + for ((i=0;i/dev/null || echo inactive) + st2=$(systemctl is-active "$s2" 2>/dev/null || echo inactive) + [[ $st1 == active ]] && c1="${green}$st1${default}" || c1="${red}$st1${default}" + [[ $st2 == active ]] && c2="${green}$st2${default}" || c2="${red}$st2${default}" + printf " %-15s : %b %-15s : %b\n" \ + "${s1%.*}" "$c1" "${s2%.*}" "$c2" + done + + # Fail2Ban + read -r -a jails <<< "$(fail2ban-client status | grep "Jail list:" | sed "s/ //g" | awk '{split($2,a,",");for(i in a) print a[i]}')" + + out="jail,failed,total,banned,total\n" + + for jail in "${jails[@]}"; do + # Slow because fail2ban-client has to be called for every jail (~70ms per jail) + status=$(fail2ban-client status "$jail") + failed=$(echo "$status" | grep -ioP '(?<=Currently failed:\t)[[:digit:]]+') + totalfailed=$(echo "$status" | grep -ioP '(?<=Total failed:\t)[[:digit:]]+') + banned=$(echo "$status" | grep -ioP '(?<=Currently banned:\t)[[:digit:]]+') + totalbanned=$(echo "$status" | grep -ioP '(?<=Total banned:\t)[[:digit:]]+') + out+="$jail,$failed,$totalfailed,$banned,$totalbanned\n" + done + + printf "\nFail2ban\n" + printf "%b\n" "$out" | column -ts $',' | sed -e 's/^/ /' + + # Help links + cat <<-EOF + + Links (ctrl+click to follow) + Server Manual.........: https://tinyurl.com/jjz9h6fr + Cockpit (for admins)..: http://localhost:9090 + Robot Camera..........: http://localhost:9999 + JupyterLab............: http://localhost:8888 + RStudio Server........: http://localhost:8787 + Robot Computer........: vnc://192.168.16.101:5900 + Windows 10 VM.........: vnc://localhost:5900 (pw: hartman) + EOF + + # Scheduled reboot + if systemctl is-active scheduled-reboot.timer &>/dev/null; then + echo -n "Next scheduled reboot: " + time=$(systemctl cat scheduled-reboot.timer | grep OnCalendar=) + time=${time#*=} + echo "$time" + fi + + if systemctl is-active scheduled-reboot.timer &>/dev/null; then + echo -n " Next reboot : " + systemctl show -p OnCalendar scheduled-reboot.timer | cut -d= -f2 + fi + + printf "\n" +} + +install_services() { + # Write the service unit + cat >"$SERVICE_UNIT" <<-EOF + [Unit] + Description=Generate MoTD + + [Service] + Type=oneshot + ExecStart=$INSTALL_PATH --motd > /etc/motd + + [Install] + WantedBy=multi-user.target + EOF + + # Write the timer unit + cat >"$TIMER_UNIT" <<-EOF + [Unit] + Description=Generate MoTD every minute + + [Timer] + OnCalendar=*:0/1 + OnBootSec=10s + + [Install] + WantedBy=timers.target + EOF + + chmod +x "$INSTALL_PATH" + systemctl daemon-reload + systemctl enable --now motd.timer + echo "Installed and started motd.timer → $SERVICE_UNIT, $TIMER_UNIT" +} + +main() { + if [[ ${1:-} == --motd ]]; then + print_motd + exit 0 + fi + + if [[ ${1:-} == --install ]]; then + if [[ $EUID -ne 0 ]]; then + echo "Error: Must run as root to install." >&2 + exit 1 + fi + cp -f "$0" "$INSTALL_PATH" + install_services + fi + + if [[ ${1:-} =~ ^(-h|--help)$ ]]; then + usage + exit 0 + fi + + +} + +main "$@" diff --git a/script-system-scheduled-restart b/scripts/script-system-scheduled-restart similarity index 61% rename from script-system-scheduled-restart rename to scripts/script-system-scheduled-restart index 97e0587d..9d7f9545 100755 --- a/script-system-scheduled-restart +++ b/scripts/script-system-scheduled-restart @@ -1,11 +1,8 @@ #!/usr/bin/env bash # Update and restart the system -# Copyright 2021 Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 is_root @@ -17,15 +14,15 @@ script-system-update ask_ok "Set a scheduled reboot for $time?" || exit 1 cat <<- EOF > "/usr/lib/systemd/system/scheduled-reboot.timer" -[Unit] -Description=Scheduled reboot + [Unit] + Description=Scheduled reboot -[Timer] -OnCalendar=$time -Unit=reboot.target + [Timer] + OnCalendar=$time + Unit=reboot.target -[Install] -WantedBy=timers.target + [Install] + WantedBy=timers.target EOF systemctl daemon-reload @@ -34,7 +31,10 @@ systemctl start scheduled-reboot.timer # Current date dt=$(date '+%d/%m/%Y %H:%M:%S'); -message="System restart scheduled for $time. The current time is $dt. Make sure all changes are saved." +msg_part1="System restart scheduled for $time." +msg_part2="The current time is $dt." +msg_part3="Make sure all changes are saved." +message="$msg_part1 $msg_part2 $msg_part3" # Graphical notification IFS=$'\n' @@ -47,5 +47,3 @@ done # Wall notification wall -n "$message" - -exit $? diff --git a/scripts/script-system-update b/scripts/script-system-update new file mode 100755 index 00000000..c2019dbd --- /dev/null +++ b/scripts/script-system-update @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# Update the system +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 + +is_root + +ask_ok "Security updates are automatically installed, perform a full system update?" || exit $? + +dnf update --refresh diff --git a/scripts/script-user-add b/scripts/script-user-add new file mode 100755 index 00000000..5ff7db77 --- /dev/null +++ b/scripts/script-user-add @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# Add a user to the Hartman Lab server +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 + +is_root + +echo "Usage: $0 [username] [password]" + +case $# in + 0) prompt user; prompt password ;; + 1) user="$1"; prompt password ;; + 2) user="$1"; password="$2" ;; + *) echo "Too many arguments provided"; exit 1 ;; +esac + +useradd_cmd=(useradd -m -U) +group_str="" + +if id -u "$user" &>/dev/null; then + ask_ok "User $user exists. Run script-user-remove first?" || exit $? + "$p/script-user-remove" "$user" || exit $? +fi + +ask_ok "Create user $user with password $password?" || exit $? + +restore=0 +if [[ -d /mnt/array/home-retired/$user ]]; then + ask_ok "Restore user $user's files from /mnt/array/home-retired/$user?" && restore=1 +fi + +samba=0 +ask_ok "Enable shared file access for user $user?" && group_str="smbgrp" && samba=1 +ask_ok "Make $user an admin?" && group_str+=",wheel" + +[[ -n $group_str ]] && useradd_cmd+=("-G" "$group_str") +useradd_cmd+=("$user") + +"${useradd_cmd[@]}" +echo "$user:$password" | chpasswd + +if (( restore )); then + if rsync -av --progress=info2 "/mnt/array/home-retired/$user/" "/home/$user/"; then + ask_ok "User $user's files successfully restored, remove backup at /mnt/array/home-retired/$user?" && \ + rm -rf "/mnt/array/home-retired/$user" + fi +fi + +if (( samba )); then + (echo "$password"; echo "$password") | smbpasswd -a -s "$user" +fi + +ask_ok "Prompt user to reset password on next login?" && + passwd --expire "$user" && + echo "NOTE: The file sharing (smbpasswd) will not be changed" + +# Copy manual to user desktop +desktop="/home/$user/Desktop" +mkdir -p "$desktop" +"$p/script-deploy-manual" "$user" + +exit 0 \ No newline at end of file diff --git a/scripts/script-user-remove b/scripts/script-user-remove new file mode 100755 index 00000000..c7b50c06 --- /dev/null +++ b/scripts/script-user-remove @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# Remove a user from the server +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 + +is_root + +echo "This script supports one optional argument, a username" + +if [[ $# -eq 1 ]]; then + user="$1" +else + prompt user +fi + +if ! id -u "$user" &>/dev/null; then + echo "User $user does not exist" + exit 1 +fi + +ask_ok "Remove user $user?" || exit 1 + +# Kill user processes +killall -u "$user" + +# Optional backup +if ask_ok "Backup /home/$user?" && [[ -d /home/$user ]]; then + backup_dir="/mnt/array/home-retired/$user" + if [[ ! -d $backup_dir ]]; then + btrfs subvolume create "$backup_dir" || exit 1 + fi + rsync -av "/home/$user/" "$backup_dir/" || exit 1 +fi + +# Remove from Samba and subuid/subgid +smbpasswd -x "$user" +sed -i "/^$user:/d" /etc/subuid +sed -i "/^$user:/d" /etc/subgid + +# Remove user and home directory +if ! userdel -fr "$user"; then + ask_ok "Userdel failed, kill processes again and retry?" || exit 1 + killall -u "$user" -s SIGKILL + userdel -fr "$user" +fi + diff --git a/script-user-reset-desktop b/scripts/script-user-reset-desktop similarity index 79% rename from script-user-reset-desktop rename to scripts/script-user-reset-desktop index cd5b1bdf..599541d3 100755 --- a/script-user-reset-desktop +++ b/scripts/script-user-reset-desktop @@ -1,11 +1,8 @@ #!/usr/bin/env bash # Reset default desktop preferences -# Copyright Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 echo "This script will only work for the current user" diff --git a/scripts/script-user-reset-password b/scripts/script-user-reset-password new file mode 100755 index 00000000..153a9c4b --- /dev/null +++ b/scripts/script-user-reset-password @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Reset a user password on the server +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 + +is_root + +echo "Usage: $0 [username] [password]" + +case $# in + 0) + prompt user + prompt password + ;; + 1) + user="$1" + prompt password + ;; + 2) + user="$1" + password="$2" + ;; + *) + echo "Too many arguments provided" + exit 1 + ;; +esac + +if ! id -u "$user" &>/dev/null; then + echo "User $user does not exist" + exit 1 +fi + +if ask_ok "Change user $user's password to $password?"; then + echo "$user:$password" | chpasswd + (echo "$password"; echo "$password") | smbpasswd -a -s "$user" +fi + +ask_ok "Prompt user to reset password on next login?" && + passwd --expire "$user" && + echo "NOTE: The file sharing (smbpasswd) will not be changed" + +exit 0 diff --git a/script-user-reset-x2go b/scripts/script-user-reset-x2go similarity index 88% rename from script-user-reset-x2go rename to scripts/script-user-reset-x2go index f8450bc4..7389013b 100755 --- a/script-user-reset-x2go +++ b/scripts/script-user-reset-x2go @@ -1,10 +1,9 @@ #!/usr/bin/env bash # This script will reset x2go sessions to a working state # Use --all to reset all user X2Go sessions -# Copyright Bryan C. Roessler - -parent="${BASH_SOURCE[0]%/*}" -[[ -f $parent/script-functions ]] && . "$parent/script-functions" || exit 1 +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 echo "Usage: $0 [username|--all]" diff --git a/script-user-unban b/scripts/script-user-unban similarity index 62% rename from script-user-unban rename to scripts/script-user-unban index 8849eb89..e9d31f85 100755 --- a/script-user-unban +++ b/scripts/script-user-unban @@ -1,11 +1,8 @@ #!/usr/bin/env bash # Unbans a fail2ban IP -# Copyright 2021 Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 +p="${BASH_SOURCE[0]%/*}"; [[ -r $p/script-functions ]] && . "$p"/script-functions || exit 1 is_root diff --git a/stow/etc/dconf/db/local.d/99-hartmanlab b/stow/etc/dconf/db/local.d/99-hartmanlab new file mode 100644 index 00000000..1bc2da0c --- /dev/null +++ b/stow/etc/dconf/db/local.d/99-hartmanlab @@ -0,0 +1,75 @@ +#/etc/dconf/db/local.d/99-hartmanlab + +[org/mate/desktop/background] +primary-color='rgb(59,110,165)' +color-shading-type='solid' +picture-filename='' + +[org/mate/desktop/font-rendering] +antialiasing='rgba' +hinting='slight' + +[org/mate/desktop/media-handling] +automount=false +automount-open=false +autorun-never=true + +[org/mate/desktop/interface] +gtk-decoration-layout='menu:minimize,maximize,close' +font-name='Liberation Sans 10' +monospace-font-name='Liberation Mono 10' +document-font-name='Liberation Sans 10' +enable-animations=false +gtk-enable-animations=false +gtk-theme='BlueMenta' + +[org/mate/screensaver] +lock-enabled=false + +[org/mate/panel/general] +default-layout='hartmanlab' +toplevel-id-list=['bottom'] + +[org/mate/panel/objects/clock/prefs] +format='12-hour' + +[org/mate/panel/toplevels/bottom] +orientation='bottom' +y-bottom=0 + +[org/mate/mate-menu] +applet-text='' +plugins-list=['places','system_management', 'newpane', 'applications', 'newpane', 'recent'] + +[org/mate/mate-menu/plugins/applications] +last-active-tab=1 + +[org/mate/mate-menu/plugins/places] +show-computer=false + +[org/mate/mate-menu/plugins/system_management] +show-control-center=false +show-lock-screen=true +show-package-manager=false +show-quit=false +show-terminal=true + +[org/mate/marco/general] +compositing-manager=false +action-double-click-titlebar='toggle_maximize' +button-layout='menu:minimize,maximize,close' +num-workspaces=4 +theme='BlueMenta' +titlebar-font='Liberation Sans Bold 11' + +[org/mate/caja/desktop] +font='Liberation Sans 10' + +[org/mate/caja/preferences] +always-use-location-entry=true +default-folder-viewer='list-view' +show-backup-files=true + +[org/mate/notification-daemon] +popup-location='bottom_right' +theme='slider' diff --git a/stow/etc/dconf/db/local.d/locks/99-hartmanlab b/stow/etc/dconf/db/local.d/locks/99-hartmanlab new file mode 100644 index 00000000..5a8501cc --- /dev/null +++ b/stow/etc/dconf/db/local.d/locks/99-hartmanlab @@ -0,0 +1,2 @@ +#/etc/dconf/db/local.db/locks/99-hartmanlab +/org/mate/mate-menu/plugins-list diff --git a/stow/etc/fail2ban/jail.local b/stow/etc/fail2ban/jail.local new file mode 100644 index 00000000..fe913fdc --- /dev/null +++ b/stow/etc/fail2ban/jail.local @@ -0,0 +1,11 @@ +[DEFAULT] +bantime = 30m +bantime.multipliers = 1 2 4 8 16 32 64 +findtime = 60m +maxretry = 3 +ignoreip = 127.0.0.0/8 10.0.0.0/8 138.26.0.0/16 +banaction = iptables-multiport + +[sshd] +enabled = true +port = ssh diff --git a/stow/etc/fstab b/stow/etc/fstab new file mode 100644 index 00000000..f3f0c47d --- /dev/null +++ b/stow/etc/fstab @@ -0,0 +1,18 @@ +# /etc/fstab +# +# Accessible filesystems, by reference, are maintained under '/dev/disk' +# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info +# +UUID=c6c096c8-d635-4890-a080-5de8c88b5441 / ext4 defaults 1 1 +UUID=1C8B-AF1E /boot/efi vfat umask=0077,shortname=winnt 0 0 +UUID=32e4f38a-8097-433b-878f-2096f9cad6d5 swap swap defaults 0 0 +UUID=d9f4c4c5-41d5-463d-abf9-b2070e5d3acc /mnt/array btrfs defaults,compress=zstd:2,x-gvfs-hide,nofail,x-systemd.device-timeout=180s,discard=async,X-fstrim.notrim 0 0 +UUID=d9f4c4c5-41d5-463d-abf9-b2070e5d3acc /mnt/data btrfs defaults,subvol=data,compress=zstd:2,x-gvfs-show,nofail,x-systemd.device-timeout=180s,discard=async,X-fstrim.notrim 0 0 +UUID=8d4bf94c-f307-40b1-8315-5b15f041c120 /mnt/backup btrfs defaults,compress=zstd:2,nofail,discard=async,X-fstrim.notrim 0 0 +#UUID=6E323E4F323E1C91 /media/ext1 ntfs-3g defaults,user,nofail,x-systemd.device-timeout=1,uid=root,gid=smbgrp,dmask=002,fmask=002 0 0 +#UUID=8433-7BB5 /media/ext2 vfat defaults,user,nofail,x-systemd.device-timeout=1,uid=root,gid=smbgrp,dmask=002,fmask=002 0 0 +#UUID=F474B7AA74B76DCC /media/ext3 ntfs-3g defaults,user,nofail,x-systemd.device-timeout=1,uid=root,gid=smbgrp,dmask=002,fmask=002 0 0 +#UUID=12C23AD8C23AC031 /media/ext4 ntfs-3g defaults,user,nofail,x-systemd.device-timeout=1,uid=root,gid=smbgrp,dmask=002,fmask=002 0 0 +#UUID=829AF4939AF484C7 /media/ext5 ntfs-3g defaults,user,nofail,x-systemd.device-timeout=1,uid=root,gid=smbgrp,dmask=002,fmask=002 0 0 +#UUID=0628B809375069C3 /media/ext6 ntfs-3g defaults,user,nofail,x-systemd.device-timeout=1,uid=root,gid=smbgrp,dmask=002,fmask=002 0 0 +#UUID=686A557F6A554B48 /media/ext7 ntfs-3g defaults,user,nofail,x-systemd.device-timeout=1,uid=root,gid=smbgrp,dmask=002,fmask=002 0 0 \ No newline at end of file diff --git a/stow/etc/samba/smb.conf b/stow/etc/samba/smb.conf new file mode 100644 index 00000000..1193dcdc --- /dev/null +++ b/stow/etc/samba/smb.conf @@ -0,0 +1,22 @@ +[global] +workgroup = WORKGROUP +server string = Samba Server %v +netbios name = hartmanlab +security = user +map to guest = bad user +dns proxy = no +#============================ Share Definitions ============================== +[data] +path = /mnt/data +valid users = @smbgrp +browseable = yes +writeable = yes +guest ok = no + + +# Special homes share (do not edit!) +[homes] +comment = Home Directories +browseable = yes +valid users = %S +writeable = yes \ No newline at end of file diff --git a/stow/etc/ssh/sshd_config b/stow/etc/ssh/sshd_config new file mode 100644 index 00000000..75f46448 --- /dev/null +++ b/stow/etc/ssh/sshd_config @@ -0,0 +1,137 @@ +# This is the sshd server system-wide configuration file. See +# sshd_config(5) for more information. + +# This sshd was compiled with PATH=/usr/local/bin:/usr/bin + +# The strategy used for options in the default sshd_config shipped with +# OpenSSH is to specify options with their default value where +# possible, but leave them commented. Uncommented options override the +# default value. + +# If you want to change the port on a SELinux system, you have to tell +# SELinux about this change. +# semanage port -a -t ssh_port_t -p tcp #PORTNUMBER +# +#Port 22 +#AddressFamily any +#ListenAddress 0.0.0.0 +#ListenAddress :: + +HostKey /etc/ssh/ssh_host_rsa_key +#HostKey /etc/ssh/ssh_host_dsa_key +HostKey /etc/ssh/ssh_host_ecdsa_key +HostKey /etc/ssh/ssh_host_ed25519_key + +# Ciphers and keying +#RekeyLimit default none + +# Logging +#SyslogFacility AUTH +SyslogFacility AUTHPRIV +#LogLevel INFO + +# Authentication: + +#LoginGraceTime 2m +PermitRootLogin no +#StrictModes yes +MaxAuthTries 4 +#MaxSessions 10 + +#PubkeyAuthentication yes + +# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 +# but this is overridden so installations will only check .ssh/authorized_keys +AuthorizedKeysFile .ssh/authorized_keys + +#AuthorizedPrincipalsFile none + +#AuthorizedKeysCommand none +#AuthorizedKeysCommandUser nobody + +# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts +#HostbasedAuthentication no +# Change to yes if you don't trust ~/.ssh/known_hosts for +# HostbasedAuthentication +#IgnoreUserKnownHosts no +# Don't read the user's ~/.rhosts and ~/.shosts files +#IgnoreRhosts yes + +# To disable tunneled clear text passwords, change to no here! +#PasswordAuthentication yes +#PermitEmptyPasswords no +PasswordAuthentication yes + +# Change to no to disable s/key passwords +#ChallengeResponseAuthentication yes +ChallengeResponseAuthentication no + +# Kerberos options +#KerberosAuthentication no +#KerberosOrLocalPasswd yes +#KerberosTicketCleanup yes +#KerberosGetAFSToken no +#KerberosUseKuserok yes + +# GSSAPI options +GSSAPIAuthentication yes +GSSAPICleanupCredentials no +#GSSAPIStrictAcceptorCheck yes +#GSSAPIKeyExchange no +#GSSAPIEnablek5users no + +# Set this to 'yes' to enable PAM authentication, account processing, +# and session processing. If this is enabled, PAM authentication will +# be allowed through the ChallengeResponseAuthentication and +# PasswordAuthentication. Depending on your PAM configuration, +# PAM authentication via ChallengeResponseAuthentication may bypass +# the setting of "PermitRootLogin without-password". +# If you just want the PAM account and session checks to run without +# PAM authentication, then enable this but set PasswordAuthentication +# and ChallengeResponseAuthentication to 'no'. +# WARNING: 'UsePAM no' is not supported in Red Hat Enterprise Linux and may cause several +# problems. +UsePAM yes + +#AllowAgentForwarding yes +#AllowTcpForwarding yes +GatewayPorts yes +X11Forwarding yes +#X11DisplayOffset 10 +#X11UseLocalhost yes +#PermitTTY yes +PrintMotd yes +#PrintLastLog yes +#TCPKeepAlive yes +#UseLogin no +#UsePrivilegeSeparation sandbox +#PermitUserEnvironment no +#Compression delayed +ClientAliveInterval 7200 +#ClientAliveCountMax 3 +#ShowPatchLevel no +#UseDNS yes +#PidFile /var/run/sshd.pid +#MaxStartups 10:30:100 +PermitTunnel yes +#ChrootDirectory none +#VersionAddendum none + +# no default banner path +#Banner none + +# Accept locale-related environment variables +AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES +AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT +AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE +AcceptEnv XMODIFIERS + +# override default of no subsystems +Subsystem sftp /usr/libexec/openssh/sftp-server + +# Example of overriding settings on a per-user basis +#Match User anoncvs +# X11Forwarding no +# AllowTcpForwarding no +# PermitTTY no +# ForceCommand cvs server \ No newline at end of file diff --git a/stow/usr/share/glib-2.0/schemas/50-tweaks.gschema.override b/stow/usr/share/glib-2.0/schemas/50-tweaks.gschema.override new file mode 100644 index 00000000..92cff7ff --- /dev/null +++ b/stow/usr/share/glib-2.0/schemas/50-tweaks.gschema.override @@ -0,0 +1,23 @@ +[org.mate.panel.general] +default-layout='redmond' + +[org.mate.desktop.font-rendering] +antialiasing='rgba' +hinting='slight' + +#[org.mate.desktop.interface] +#font-name='Liberation Sans 10' +#document-font-name='Liberation Sans 10' +#monospace-font-name='Liberation Mono 10' + +[org.mate.Marco.general] +side-by-side-tiling=true +num-workspaces=2 +compositing-manager=false + +[org.mate.media-handling] +automount=false +automount-open=false + +[org.mate.screensaver] +lock-enabled=false \ No newline at end of file diff --git a/stow/usr/share/mate-panel/layouts/hartmanlab.layout b/stow/usr/share/mate-panel/layouts/hartmanlab.layout new file mode 100644 index 00000000..fb767184 --- /dev/null +++ b/stow/usr/share/mate-panel/layouts/hartmanlab.layout @@ -0,0 +1,72 @@ +#/usr/share/mate-panel/layouts/hartmanlab.layout + +[Toplevel bottom] +expand=true +orientation=bottom +size=24 + +[Object mate-menu] +object-type=applet +toplevel-id=bottom +locked=true +position=0 +applet-iid=MateMenuAppletFactory::MateMenuApplet + +[Object separator] +object-type=separator +toplevel-id=bottom +locked=true +position=30 + +[Object firefox] +object-type=launcher +toplevel-id=bottom +locked=true +position=40 +launcher-location=/usr/share/applications/firefox.desktop + +[Object mate-terminal] +object-type=launcher +toplevel-id=bottom +locked=true +position=64 +launcher-location=/usr/share/applications/mate-terminal.desktop + +[Object caja] +object-type=launcher +toplevel-id=bottom +locked=true +position=88 +launcher-location=/usr/share/applications/caja-browser.desktop + +[Object window-list] +object-type=applet +toplevel-id=bottom +locked=true +position=112 +applet-iid=WnckletFactory::WindowListApplet + +[Object workspace-switcher] +object-type=applet +toplevel-id=bottom +locked=true +panel-right-stick=true +position=20 +applet-iid=WnckletFactory::WorkspaceSwitcherApplet + +[Object notification-area] +object-type=applet +toplevel-id=bottom +locked=true +panel-right-stick=true +position=10 +applet-iid=NotificationAreaAppletFactory::NotificationArea + +[Object clock] +object-type=applet +toplevel-id=bottom +locked=true +panel-right-stick=true +position=0 +applet-iid=ClockAppletFactory::ClockApplet + diff --git a/smartd-notify-all b/stow/usr/share/smartmontools/smartd_warning.d/99-smartd-notify-all similarity index 75% rename from smartd-notify-all rename to stow/usr/share/smartmontools/smartd_warning.d/99-smartd-notify-all index f4ac0f9c..0c8ff2ce 100755 --- a/smartd-notify-all +++ b/stow/usr/share/smartmontools/smartd_warning.d/99-smartd-notify-all @@ -1,15 +1,8 @@ #!/usr/bin/env bash # Notify all users on S.M.A.R.T errors # Place in /usr/share/smartmontools/smartd_warning.d/ or use "DEVICESCAN -m @smartd-notify-all" in /etc/smartd.conf -# Copyright 2021 Bryan C. Roessler - -parent="${BASH_SOURCE[0]}" -parent=${parent%/*} - -[[ -f $parent/script-functions ]] && . "$parent"/script-functions || exit 1 - -is_root - +# Copyright 2021-2025 Bryan C. Roessler +# Licensed under the Apache License, Version 2.0 IFS=$'\n' for LINE in $(w -hs); do USER=$(echo "$LINE" | awk '{print $1}')