Created
October 13, 2025 09:34
-
-
Save rezarajan/ebc40becaca69f805233243062dc775c to your computer and use it in GitHub Desktop.
A curl-based synology-cli tool for listing, locking and unlocking shares. Works with DSM 7.2.2 + and 2FA.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # synology-cli.sh | |
| # Works with DSM 7.2.2 and 2FA | |
| set -euo pipefail | |
| NAS_URL="" | |
| while getopts ":u:" opt; do | |
| case $opt in | |
| u) NAS_URL="$OPTARG" ;; | |
| *) echo "Usage: $0 [-u https://your-nas:5001]"; exit 1 ;; | |
| esac | |
| done | |
| : "${NAS_URL:="https://your-nas:5001"}" | |
| has_jq() { command -v jq >/dev/null 2>&1; } | |
| pretty() { if has_jq; then jq -C . 2>/dev/null || cat; else cat; fi; } | |
| # Secure cleanup | |
| SID="" | |
| cleanup() { | |
| if [[ -n "${SID:-}" ]]; then | |
| printf '' | curl -k -s --data-binary @- \ | |
| "${NAS_URL}/webapi/auth.cgi?api=SYNO.API.Auth&version=6&method=logout&session=FileStation&_sid=${SID}" >/dev/null 2>&1 || true | |
| fi | |
| unset SID DSM_PASS OTP_CODE | |
| } | |
| trap cleanup EXIT | |
| api_post() { local endpoint="$1"; curl -k -s --data-binary @- "${NAS_URL}/webapi/${endpoint}"; } | |
| urlencode() { | |
| local string="$1" encoded="" | |
| local length="${#string}" | |
| for (( i = 0; i < length; i++ )); do | |
| c="${string:i:1}" | |
| case "$c" in | |
| [a-zA-Z0-9.~_-]) encoded+="$c" ;; | |
| *) printf -v hex '%%%02X' "'$c"; encoded+="$hex" ;; | |
| esac | |
| done | |
| echo "$encoded" | |
| } | |
| login() { | |
| read -rp "DSM username: " DSM_USER | |
| printf "DSM password: " | |
| stty -echo; read DSM_PASS; stty echo; echo | |
| LOGIN_RESP=$(printf 'api=SYNO.API.Auth&version=6&method=login&account=%s&passwd=%s&session=FileStation&format=sid' \ | |
| "$DSM_USER" "$DSM_PASS" | api_post "auth.cgi") | |
| OTP_TOKEN=$(echo "$LOGIN_RESP" | jq -r '.error.errors.token // empty') | |
| if [[ -n "$OTP_TOKEN" ]]; then | |
| echo "๐ 2FA detected. Please enter OTP." | |
| read -rp "OTP code: " OTP_CODE | |
| LOGIN_RESP=$(printf 'api=SYNO.API.Auth&version=6&method=login&account=%s&passwd=%s&otp_code=%s&session=FileStation&format=sid' \ | |
| "$DSM_USER" "$DSM_PASS" "$OTP_CODE" | api_post "auth.cgi") | |
| fi | |
| SID=$(echo "$LOGIN_RESP" | jq -r '.data.sid // empty') | |
| if [[ -z "$SID" ]]; then | |
| echo "โ Login failed. Check credentials or OTP." | |
| exit 2 | |
| fi | |
| echo "โ Logged in. SID: $SID" | |
| DSM_PASS=""; unset DSM_PASS OTP_CODE | |
| } | |
| list_shares() { | |
| echo "๐ Listing shares (with encryption info)..." | |
| ALL_SHARES=$(curl -k -s -G "${NAS_URL}/webapi/entry.cgi" \ | |
| --data-urlencode "api=SYNO.Core.Share" \ | |
| --data-urlencode "version=1" \ | |
| --data-urlencode "method=list" \ | |
| --data-urlencode "additional=[\"hidden\",\"encryption\"]" \ | |
| --data-urlencode "shareType=\"all\"" \ | |
| --data-urlencode "_sid=${SID}" || true) | |
| if [[ -z "$ALL_SHARES" ]]; then | |
| echo "โ ๏ธ Failed to fetch shares." | |
| return 1 | |
| fi | |
| # Print name + encryption status | |
| echo "$ALL_SHARES" | jq -r '.data.shares[] | "\(.name) | Encrypted: \(.encryption != 0)"' | |
| } | |
| unlock_share() { | |
| SHARES=$(curl -k -s -G "${NAS_URL}/webapi/entry.cgi" \ | |
| --data-urlencode "api=SYNO.Core.Share" \ | |
| --data-urlencode "version=1" \ | |
| --data-urlencode "method=list" \ | |
| --data-urlencode "additional=[\"encryption\"]" \ | |
| --data-urlencode "shareType=\"all\"" \ | |
| --data-urlencode "_sid=${SID}" \ | |
| | jq -r '.data.shares[] | select(.encryption==1) | .name') | |
| if [[ -z "$SHARES" ]]; then | |
| echo "No encrypted (locked) shares available to unlock." | |
| return | |
| fi | |
| echo "Encrypted (locked) shares:" | |
| echo "$SHARES" | |
| read -rp "Share name to unlock: " share_name | |
| [[ -z "$share_name" ]] && { echo "Share name required"; return; } | |
| printf "Password for '%s': " "$share_name" | |
| stty -echo; read SHARE_PASS; stty echo; echo | |
| SHARE_NAME_ESC=$(urlencode "$share_name") | |
| SHARE_PASS_ESC=$(urlencode "$SHARE_PASS") | |
| RESPONSE=$(printf 'api=SYNO.Core.Share.Crypto&version=1&method=decrypt&name=%s&password=%s&_sid=%s' \ | |
| "$SHARE_NAME_ESC" "$SHARE_PASS_ESC" "$SID" | api_post "entry.cgi") | |
| pretty <<<"$RESPONSE" | |
| SHARE_PASS=""; unset SHARE_PASS | |
| } | |
| lock_share() { | |
| SHARES=$(curl -k -s -G "${NAS_URL}/webapi/entry.cgi" \ | |
| --data-urlencode "api=SYNO.Core.Share" \ | |
| --data-urlencode "version=1" \ | |
| --data-urlencode "method=list" \ | |
| --data-urlencode "additional=[\"encryption\"]" \ | |
| --data-urlencode "shareType=\"all\"" \ | |
| --data-urlencode "_sid=${SID}" \ | |
| | jq -r '.data.shares[] | select(.encryption==2) | .name') | |
| if [[ -z "$SHARES" ]]; then | |
| echo "No decrypted (unlocked) shares available to lock." | |
| return | |
| fi | |
| echo "Decrypted (unlocked) shares:" | |
| echo "$SHARES" | |
| read -rp "Share name to lock: " share_name | |
| [[ -z "$share_name" ]] && { echo "Share name required"; return; } | |
| SHARE_NAME_ESC=$(urlencode "$share_name") | |
| RESPONSE=$(printf 'api=SYNO.Core.Share.Crypto&version=1&method=encrypt&name=%s&_sid=%s' \ | |
| "$SHARE_NAME_ESC" "$SID" | api_post "entry.cgi") | |
| pretty <<<"$RESPONSE" | |
| } | |
| logout() { | |
| echo "Logging out..." | |
| SID="" # Will be cleaned up by trap | |
| echo "โ Logged out." | |
| } | |
| main_menu() { | |
| while true; do | |
| echo | |
| echo "Select an action:" | |
| echo " 1) List shares" | |
| echo " 2) Unlock (decrypt) encrypted share" | |
| echo " 3) Lock (encrypt) decrypted share" | |
| echo " 4) Logout and exit" | |
| read -rp "Choice [1-4]: " choice | |
| case "$choice" in | |
| 1) list_shares ;; | |
| 2) unlock_share || echo "โ ๏ธ Failed to unlock share" ;; | |
| 3) lock_share || echo "โ ๏ธ Failed to lock share" ;; | |
| 4) logout; break ;; | |
| *) echo "Invalid choice." ;; | |
| esac | |
| done | |
| } | |
| login | |
| main_menu | |
| # SID cleanup handled by trap |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage
The use-case for this script is very simple: list, unlock and lock shares.
The script prompts for login, asking for an OTP if required. All passwords are hidden from stdout. Once logged in, a menu will be presented which will guide you from there.
# Ensure executable permissions chmod +x syno-cli.sh