From 20bde577afd539a1354f8232c4daab80b7ac758d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Palma?= Date: Mon, 4 Mar 2024 18:57:04 +0000 Subject: [PATCH 1/4] Refactor check_default_credentials to always check PalWorldSettings.ini file --- scripts/server/check_default_credentials.sh | 23 +++++++-------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/scripts/server/check_default_credentials.sh b/scripts/server/check_default_credentials.sh index 9d2c01a..de00faa 100644 --- a/scripts/server/check_default_credentials.sh +++ b/scripts/server/check_default_credentials.sh @@ -8,24 +8,15 @@ source "${SERVER_DIR}"/scripts/utils/logs.sh function check_default_credentials() { log_info "> Checking for existence of default credentials" - # If the 'ADMIN_PASSWORD' environment variable is set to 'adminPasswordHere', an error message is displayed and the function exits. - if [[ -n ${ADMIN_PASSWORD} ]] && [[ ${ADMIN_PASSWORD} == "adminPasswordHere" ]]; then - log_error ">>> Security thread detected: Please change the default admin password. Aborting server start ..." - exit 1 - fi - - # If the 'SERVER_PASSWORD' environment variable is set to 'serverPasswordHere', an error message is displayed and the function exits. - if [[ -n ${SERVER_PASSWORD} ]] && [[ ${SERVER_PASSWORD} == "serverPasswordHere" ]]; then - log_error ">>> Security thread detected: Please change the default server password. Aborting server start ..." - exit 1 - fi - - # If 'SERVER_SETTINGS_MODE' is set to 'manual' and the 'GAME_SETTINGS_FILE' exists and is not empty, the function extracts the admin password from the 'GAME_SETTINGS_FILE'. - # If the password is 'adminPasswordHere', an error message is displayed. - if [[ "${SERVER_SETTINGS_MODE,,}" == "manual" ]] && [[ -s "${GAME_SETTINGS_FILE}" ]]; then + if [[ -f "${GAME_SETTINGS_FILE}" ]]; then admin_password=$(awk -F'AdminPassword="' '{print $2}' "${GAME_SETTINGS_FILE}" | awk -F'"' '{print $1}' | tr -d '\n') if [[ "${admin_password}" == "adminPasswordHere" ]]; then - log_error ">>> Security thread detected: Please change the default server password. Aborting server start ..." + log_error ">>> Security thread detected: Please change the default admin password. Aborting server start..." + exit 1 + fi + server_password=$(awk -F'ServerPassword="' '{print $2}' "${GAME_SETTINGS_FILE}" | awk -F'"' '{print $1}' | tr -d '\n') + if [[ "${server_password}" == "serverPasswordHere" ]]; then + log_error ">>> Security thread detected: Please change the default server password. Aborting server start..." exit 1 fi fi From b6b57a267620114c4e68b90d93e8a156fed590b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Palma?= Date: Mon, 4 Mar 2024 18:58:17 +0000 Subject: [PATCH 2/4] Fix rconcli wrapper to output cleanly without ANSI codes --- scripts/rcon/rconcli.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/rcon/rconcli.sh b/scripts/rcon/rconcli.sh index 728b2c9..d5ed062 100644 --- a/scripts/rcon/rconcli.sh +++ b/scripts/rcon/rconcli.sh @@ -27,7 +27,7 @@ rconcli() { fi log_info -n "> RCON: " - log_base "$output" + echo "$output" } rconcli "$*" From a953ee3e54ff8cffe40f150cc21cb6f094f1731e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Palma?= Date: Mon, 4 Mar 2024 18:59:10 +0000 Subject: [PATCH 3/4] Redact RCON port setting in server start function --- scripts/server/start_server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/server/start_server.sh b/scripts/server/start_server.sh index 10ca079..218761e 100644 --- a/scripts/server/start_server.sh +++ b/scripts/server/start_server.sh @@ -23,7 +23,7 @@ function start_server() { fi if [[ -n $RCON_ENABLED ]] && [[ $RCON_ENABLED == "true" ]]; then - log_info "> Setting RCON port to ${RCON_PORT} on server start options" + log_info "> Setting RCON port on server start options" START_OPTIONS+=("-RCONPort=${RCON_PORT}") fi From 626c625d5b3aa45a0b02d3ee34fb665472eb9937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Palma?= Date: Mon, 4 Mar 2024 19:00:53 +0000 Subject: [PATCH 4/4] Enhance player monitor logic and respective logs and webhook messages to handle player uid and steam id placeholders --- README.md | 22 +++++++ docs/ENV_VARS.md | 61 ++++++++++++++------ scripts/utils/player_activity_monitor.sh | 55 ++++++++++++++++-- scripts/webhook/aliases.sh | 43 +++++++++++++- scripts/webhook/send_webhook_notification.sh | 58 ++++++++++++++----- 5 files changed, 201 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index f59ae97..63cbc4b 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,28 @@ Events monitored: Player events are logged in the container logs and sent to the webhook if enabled. +Logging format is `> Player [joined/left]: PLAYER_NAME | UID: PLAYER_UID | Steam ID: PLAYER_STEAM_UID`. + +If `PLAYER_STEAM_UID` is invalid (when players have spacial characters in their name, receiving 16 digits instead of 17 on the `RCON` `ShowPlayers` command), it will be logged but it will be missing the last digit. +When this happens, the server will give a warning message in the logs and list all possible steam profiles associated with the player that joined/left. +It does this by testing all 10 combos of the last digit from 0 to 9 (e.g. `"PLAYER_STEAM_UID" + "3"`). + +Log example: +```shell +> Player joined: PLAYER_NAME | UID: PLAYER_UID | Steam ID: XXXXXXXXXXXXXXXX +>> Invalid Steam ID - Should have 17 digits but has 16 digits! +>> Possible Steam IDs: +> Profile name is: profile1 | Profile link: https://steamcommunity.com/profiles/XXXXXXXXXXXXXXXX1 +> Profile name is: ___Profile2___ | Profile link: https://steamcommunity.com/profiles/XXXXXXXXXXXXXXXX3 +> Profile name is: pROFILe_$%4 | Profile link: https://steamcommunity.com/profiles/XXXXXXXXXXXXXXXX7 +``` + +> [!NOTE] +> +> You can use `PLAYER_NAME`, `PLAYER_UID` and `PLAYER_STEAM_UID` in the webhook messages. +> When Steam ID is invalid, the webhook message will always contain the possible Steam profiles associated with the player and change `PLAYER_STEAM_UID` to `###INVALID_STEAM_UID###`. +> See more info about this integration [here](/docs/ENV_VARS.md#--player-join-message). + ## Backup Manager > [!WARNING] diff --git a/docs/ENV_VARS.md b/docs/ENV_VARS.md index 30dc3e6..111da8d 100644 --- a/docs/ENV_VARS.md +++ b/docs/ENV_VARS.md @@ -17,14 +17,14 @@ Due to the extensive control options, the settings are split into categories: - [Webhook Settings](#webhook-settings) - [Webhook Configuration](#webhook-configuration) - [Webhook Messages](#webhook-messages) - - [**• Start server message**](#-start-server-message) - - [**• Stop server message**](#-stop-server-message) - - [**• Restart server message**](#-restart-server-message) - - [**• Install server message**](#-install-server-message) - - [**• Update server message**](#-update-server-message) - - [**• Update and validation server message**](#-update-and-validation-server-message) - - [**• Player join message**](#-player-join-message) - - [**• Player leave message**](#-player-leave-message) + - [**- Start server message**](#--start-server-message) + - [**- Stop server message**](#--stop-server-message) + - [**- Restart server message**](#--restart-server-message) + - [**- Install server message**](#--install-server-message) + - [**- Update server message**](#--update-server-message) + - [**- Update and validation server message**](#--update-and-validation-server-message) + - [**- Player join message**](#--player-join-message) + - [**- Player leave message**](#--player-leave-message) ## Container Settings @@ -298,7 +298,7 @@ Colors are represented in their decimal form. Below are the environment variables for each type of webhook message: -#### **• Start server message** +#### **- Start server message** | Variable | Description | Default Value | | --------------------------- | ------------------------------------- | ------------------------------------ | @@ -307,7 +307,7 @@ Below are the environment variables for each type of webhook message: | `WEBHOOK_START_COLOR` | The color for the start message | `65280` | -#### **• Stop server message** +#### **- Stop server message** | Variable | Description | Default Value | | -------------------------- | ------------------------------------ | --------------------------------- | @@ -316,7 +316,7 @@ Below are the environment variables for each type of webhook message: | `WEBHOOK_STOP_COLOR` | The color for the stop message | `16711680` | -#### **• Restart server message** +#### **- Restart server message** > [!NOTE] > @@ -329,7 +329,7 @@ Below are the environment variables for each type of webhook message: | `WEBHOOK_RESTART_NOW_DESCRIPTION` | The description for the restart now message | `Server is restarting in now! :alarm_clock:` | | `WEBHOOK_RESTART_COLOR` | The color for the restart message | `16750848` | -#### **• Install server message** +#### **- Install server message** | Variable | Description | Default Value | | ----------------------------- | --------------------------------------- | --------------------------- | @@ -337,7 +337,7 @@ Below are the environment variables for each type of webhook message: | `WEBHOOK_INSTALL_DESCRIPTION` | The description for the install message | `Server is being installed` | | `WEBHOOK_INSTALL_COLOR` | The color for the install message | `1644912` | -#### **• Update server message** +#### **- Update server message** | Variable | Description | Default Value | | ---------------------------- | -------------------------------------- | ------------------------- | @@ -345,7 +345,7 @@ Below are the environment variables for each type of webhook message: | `WEBHOOK_UPDATE_DESCRIPTION` | The description for the update message | `Server is being updated` | | `WEBHOOK_UPDATE_COLOR` | The color for the update message | `16776960` | -#### **• Update and validation server message** +#### **- Update and validation server message** | Variable | Description | Default Value | | ------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------- | @@ -353,12 +353,18 @@ Below are the environment variables for each type of webhook message: | `WEBHOOK_UPDATE_VALIDATE_DESCRIPTION` | The description for the update and validation message | `Server is being updated and validated` | | `WEBHOOK_UPDATE_VALIDATE_COLOR` | The color for the update and validation message | `16776960` | -#### **• Player join message** +#### **- Player join message** > [!NOTE] > > `PLAYER_NAME` is a variable that will be replaced with the name of the player that joined the server. -> Use it on the title and/or description to show the player's name. +> `PLAYER_UID` is a variable that will be replaced with the UID of the player that joined the server. +> `PLAYER_STEAM_UID` is a variable that will be replaced with the Steam UID of the player that joined the server. +> Use it on the title and/or description to show the player's name, UID and/or Steam UID. + +> [!WARNING] +> +> When **Steam ID is invalid** and **you have `PLAYER_STEAM_UID` in your title or description message**, the webhook message will always contain the possible Steam profiles associated with the player and change `PLAYER_STEAM_UID` to `###INVALID_STEAM_UID###`. | Variable | Description | Default Value | | --------------------------------- | ------------------------------------ | ---------------------- | @@ -366,20 +372,37 @@ Below are the environment variables for each type of webhook message: | `WEBHOOK_PLAYER_JOIN_DESCRIPTION` | The description for the join message | `### PLAYER_NAME` | | `WEBHOOK_PLAYER_JOIN_COLOR` | The color for the join message | `1728512` | -#### **• Player leave message** +> [!TIP] +> +> You can use the `PLAYER_STEAM_UID` variable to link the Steam profile of the player that joined the server on the **description** (doesn't work on the title). +> E.g.: +> - `### PLAYER_NAME (Steam: [PLAYER_STEAM_UID](https://steamcommunity.com/profiles/PLAYER_STEAM_UID))` will show the player's name and link to their Steam profile. +> - `### [PLAYER_NAME](https://steamcommunity.com/profiles/PLAYER_STEAM_UID)` will show the player's name and their UID. + +#### **- Player leave message** > [!NOTE] > > `PLAYER_NAME` is a variable that will be replaced with the name of the player that left the server. -> `PLAYER_NAME` is a variable that will be replaced with the name of the player that left the server. -> `PLAYER_NAME` is a variable that will be replaced with the name of the player that left the server. +> `PLAYER_UID` is a variable that will be replaced with the UID of the player that joined the server. +> `PLAYER_STEAM_UID` is a variable that will be replaced with the Steam UID of the player that joined the server. > Use it on the title and/or description to show the player's name. +> [!WARNING] +> +> When **Steam ID is invalid** and **you have `PLAYER_STEAM_UID` in your title or description message**, the webhook message will always contain the possible Steam profiles associated with the player and change `PLAYER_STEAM_UID` to `###INVALID_STEAM_UID###`. + | Variable | Description | Default Value | | ---------------------------------- | ------------------------------------- | -------------------- | | `WEBHOOK_PLAYER_LEAVE_TITLE` | The title for the leave message | `:dash: Player left` | | `WEBHOOK_PLAYER_LEAVE_DESCRIPTION` | The description for the leave message | `### PLAYER_NAME` | | `WEBHOOK_PLAYER_LEAVE_COLOR` | The color for the leave message | `6291482` | +> [!TIP] +> +> You can use the `PLAYER_STEAM_UID` variable to link the Steam profile of the player that joined the server on the **description** (doesn't work on the title). +> E.g.: +> - `### PLAYER_NAME (Steam: [PLAYER_STEAM_UID](https://steamcommunity.com/profiles/PLAYER_STEAM_UID))` will show the player's name and link to their Steam profile. +> - `### [PLAYER_NAME](https://steamcommunity.com/profiles/PLAYER_STEAM_UID)` will show the player's name and their UID. [Back to main](../README.md#environment-variables) diff --git a/scripts/utils/player_activity_monitor.sh b/scripts/utils/player_activity_monitor.sh index 2bbc3a4..50bb4f6 100755 --- a/scripts/utils/player_activity_monitor.sh +++ b/scripts/utils/player_activity_monitor.sh @@ -23,7 +23,11 @@ declare -a previous_players # and the player name (all fields before the last two). # This ensures that we always split on the last two commas. # This approach correctly handles player names that contain commas, spaces, and multibyte characters. - +# +# Special chars also break RCON usage when fecthing Steam UDIs +# We get a Steam UID with 16 characters, but we need 17 to be valid +# We can add a number at the end of the Steam UID to get a valid one +# but we don't have a way to know which one is the correct # Format: name,playeruid,steamid # Function to get the current player list @@ -51,6 +55,27 @@ get_current_players() { done <<< "$player_list" } +check_steam_profile() { + local clean_output=false + if [ "$1" = "clean" ]; then + clean_output=true + shift + fi + + link="https://steamcommunity.com/profiles/$1" + content=$(curl -sL "${link}") + if echo "$content" | grep -q 'This user has not yet set up their Steam Community profile.'; then + return + else + profile_name=$(echo "$content" | grep -oPm 1 '(?<=).*(?=)') + if [[ $clean_output = true ]]; then + echo "- [${profile_name}](<${link}>)" + else + log_info -n "> Profile name is: " && log_base -n "${profile_name}" && log_info -n " | Profile link: " && log_base "${link}" + fi + fi +} + # Function to compare the current player list with the previous one compare_players() { # Arrays to hold the players who have joined and left @@ -79,10 +104,32 @@ compare_players() { if [ ${#joined_players[@]} -ne 0 ]; then for player in "${joined_players[@]}"; do local player_name + local player_uid + local player_steam_uid + local possible_steam_ids + player_name=$(echo "$player" | awk 'BEGIN{FS=OFS=","} {NF-=2; print $0}' | sed 's/,*$//') - log_info -n "> Player joined: " && log_base "'$player_name'" + player_uid=$(echo "$player" | awk 'BEGIN{FS=OFS=","} {print $(NF-1)}') + player_steam_uid=$(echo "$player" | awk 'BEGIN{FS=OFS=","} {print $NF}') + + log_info -n "> Player joined: " && log_base -n "'$player_name' " && \ + log_info -n "| UID: " && log_base -n "$player_uid" && \ + log_info -n "| Steam ID: " && log_base "$player_steam_uid" + + if [ "${#player_steam_uid}" -ne 17 ]; then + log_warning ">> Invalid Steam ID (Player name has special characters!) - Should have 17 digits but has ${#player_steam_uid} digits!" + log_warning ">> Possible Steam IDs:" + for i in {0..9}; do + result=$(check_steam_profile "${player_steam_uid}${i}") + if [[ -n "$result" ]]; then + echo "$result" + possible_steam_ids+="$(check_steam_profile "clean" "${player_steam_uid}${i}")\n" + fi + done + player_steam_uid="###INVALID_STEAM_UID###" + fi player_name=$(echo "$player" | awk 'BEGIN{FS=OFS=","} {NF-=2; print $0}' | sed 's/,*$//' | tr '`' "'" | sed 's/\\\\/\\\\\\\\/g') - send_player_join_notification "\`$player_name\`" + send_player_join_notification "\`$player_name\`" "$player_uid" "$player_steam_uid" "$possible_steam_ids" done fi @@ -93,7 +140,7 @@ compare_players() { player_name=$(echo "$player" | awk 'BEGIN{FS=OFS=","} {NF-=2; print $0}' | sed 's/,*$//') log_info -n "> Player left: " && log_base "'$player_name'" player_name=$(echo "$player" | awk 'BEGIN{FS=OFS=","} {NF-=2; print $0}' | sed 's/,*$//' | tr '`' "'" | sed 's/\\\\/\\\\\\\\/g') - send_player_leave_notification "\`$player_name\`" + send_player_leave_notification "\`$player_name\`" "$player_uid" "$player_steam_uid" done fi diff --git a/scripts/webhook/aliases.sh b/scripts/webhook/aliases.sh index 086ea16..6f8036e 100644 --- a/scripts/webhook/aliases.sh +++ b/scripts/webhook/aliases.sh @@ -43,10 +43,49 @@ function send_restart_notification() { function send_player_join_notification() { local player_name="$1" - send_webhook_notification "${WEBHOOK_PLAYER_JOIN_TITLE/PLAYER_NAME/$player_name}" "${WEBHOOK_PLAYER_JOIN_DESCRIPTION/PLAYER_NAME/$player_name}" "${WEBHOOK_PLAYER_JOIN_COLOR}" + local player_uid="$2" + local player_steam_uid="$3" + local possible_steam_ids="$4" + + local title_message + local description_message + + title_message="${WEBHOOK_PLAYER_JOIN_TITLE/PLAYER_NAME/$player_name}" + title_message="${title_message/PLAYER_UID/$player_uid}" + title_message="${title_message/PLAYER_STEAM_UID/$player_steam_uid}" + + description_message="${WEBHOOK_PLAYER_JOIN_DESCRIPTION/PLAYER_NAME/$player_name}" + description_message="${description_message/PLAYER_UID/$player_uid}" + description_message="${description_message/PLAYER_STEAM_UID/$player_steam_uid}" + + if [[ -n "${possible_steam_ids}" ]] && { [[ "${WEBHOOK_PLAYER_JOIN_TITLE}" == *"PLAYER_STEAM_UID"* ]] || [[ "${WEBHOOK_PLAYER_JOIN_DESCRIPTION}" == *"PLAYER_STEAM_UID"* ]]; }; then + send_webhook_notification "${title_message}" "${description_message}" "${WEBHOOK_PLAYER_JOIN_COLOR}" "Invalid STEAM UID (Player name has special characters!).\nPossible Profiles:" "${possible_steam_ids}" + else + send_webhook_notification "${title_message}" "${description_message}" "${WEBHOOK_PLAYER_JOIN_COLOR}" + fi + } function send_player_leave_notification() { local player_name="$1" - send_webhook_notification "${WEBHOOK_PLAYER_LEAVE_TITLE/PLAYER_NAME/$player_name}" "${WEBHOOK_PLAYER_LEAVE_DESCRIPTION/PLAYER_NAME/$player_name}" "${WEBHOOK_PLAYER_LEAVE_COLOR}" + local player_uid="$2" + local player_steam_uid="$3" + local possible_steam_ids="$4" + + local title_message + local description_message + + title_message="${WEBHOOK_PLAYER_LEAVE_TITLE/PLAYER_NAME/$player_name}" + title_message="${title_message/PLAYER_UID/$player_uid}" + title_message="${title_message/PLAYER_STEAM_UID/$player_steam_uid}" + + description_message="${WEBHOOK_PLAYER_LEAVE_DESCRIPTION/PLAYER_NAME/$player_name}" + description_message="${description_message/PLAYER_UID/$player_uid}" + description_message="${description_message/PLAYER_STEAM_UID/$player_steam_uid}" + + if [[ -n "${possible_steam_ids}" ]] && { [[ "${WEBHOOK_PLAYER_LEAVE_TITLE}" == *"PLAYER_STEAM_UID"* ]] || [[ "${WEBHOOK_PLAYER_LEAVE_DESCRIPTION}" == *"PLAYER_STEAM_UID"* ]]; }; then + send_webhook_notification "${title_message}" "${description_message}" "${WEBHOOK_PLAYER_LEAVE_COLOR}" "Invalid STEAM UID (Player name has special characters!).\nPossible Profiles:" "${possible_steam_ids}" + else + send_webhook_notification "${title_message}" "${description_message}" "${WEBHOOK_PLAYER_LEAVE_COLOR}" + fi } diff --git a/scripts/webhook/send_webhook_notification.sh b/scripts/webhook/send_webhook_notification.sh index 765c208..828a9db 100644 --- a/scripts/webhook/send_webhook_notification.sh +++ b/scripts/webhook/send_webhook_notification.sh @@ -20,9 +20,11 @@ function send_webhook_notification() { local notification_title="$1" local notification_description="$2" local notification_color_code="$3" + local additional_info_field_name="$4" + local additional_info_field_value="$5" # Generate the JSON data - data=$(generate_post_data "$notification_title" "$notification_description" "$notification_color_code") + data=$(generate_post_data "$notification_title" "$notification_description" "$notification_color_code" "$additional_info_field_name" "$additional_info_field_value") # Debug Curl #curl --ssl-no-revoke -H "Content-Type: application/json" -X POST -d "$data" "$WEBHOOK_URL" @@ -32,19 +34,49 @@ function send_webhook_notification() { # Function to generate JSON data for the Discord message generate_post_data() { - jq -n \ - --arg title "$1" \ - --arg description "$2" \ - --argjson color "$3" \ - '{ - "content": "", - "embeds": [{ - "title": $title, - "description": $description, - "color": $color - }] - }' + if [ -z "$4" ] && [ -z "$5" ]; then + jq -n \ + --arg title "$1" \ + --arg description "$2" \ + --argjson color "$3" \ + '{ + "content": "", + "embeds": [{ + "title": $title, + "description": $description, + "color": $color + }] + }' +elif [ -n "$4" ] && [ -n "$5" ]; then + local additional_info_field_name_json + local additional_info_field_value_json + + additional_info_field_name_json=$(echo -e "$4") + additional_info_field_value_json=$(echo -e "$5") + + jq -n \ + --arg title "$1" \ + --arg description "$2" \ + --argjson color "$3" \ + --arg additional_info_field_name "$additional_info_field_name_json" \ + --arg additional_info_field_value "$additional_info_field_value_json" \ + '{ + "content": "", + "embeds": [{ + "title": $title, + "description": $description, + "color": $color, + "fields": [{ + "name": $additional_info_field_name, + "value": $additional_info_field_value, + }] + }] + }' + else + log_error ">>> Invalid arguments for 'generate_post_data'" + fi } + # generate_post_data() { # cat <