You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

96 lines
3.2 KiB

  1. #!/bin/bash
  2. ## Note that the shebang is not used, but it's the login shell that
  3. ## will execute this command.
  4. exname=$(basename "$0")
  5. mkdir -p /var/log/rsync
  6. LOG="/var/log/rsync/$exname.log"
  7. ssh_connection=(${SSH_CONNECTION})
  8. SSH_SOURCE_IP="${ssh_connection[0]}:${ssh_connection[1]}"
  9. log() {
  10. printf "%s [%s] %s - %s\n" \
  11. "$(date --rfc-3339=seconds)" "$$" "$SSH_SOURCE_IP" "$*" \
  12. >> "$LOG"
  13. }
  14. log "NEW BACKUP CONNECTION"
  15. if [ -z "$1" ] || ! [[ "$1" =~ ^[a-zA-Z0-9._-]+$ ]]; then
  16. log "INVALID SETUP, ARG IS: '$1'"
  17. echo "Your command has been rejected. Contact administrator."
  18. exit 1
  19. fi
  20. ident="$1"
  21. log "IDENTIFIED AS $ident"
  22. reject() {
  23. log "REJECTED: $SSH_ORIGINAL_COMMAND"
  24. # echo "ORIG: $SSH_ORIGINAL_COMMAND" >&2
  25. echo "Your command has been rejected and reported to sys admin." >&2
  26. exit 1
  27. }
  28. sudo /usr/local/sbin/ssh-update-keys
  29. if [[ "$SSH_ORIGINAL_COMMAND" =~ [\&\(\{\;\<\>\`\$\}] ]]; then
  30. log "BAD CHARS DETECTED"
  31. # echo "Bad chars: $SSH_ORIGINAL_COMMAND" >&2
  32. reject
  33. fi
  34. if [[ "$SSH_ORIGINAL_COMMAND" =~ ^"rsync --server -"[vnloHgDtpArRzCeiLsfx\.]+(" --"[a-z=%-]+|" --partial-dir .rsync-partial")*" . /var/mirror/$ident"$ ]]; then
  35. log "ACCEPTED BACKUP COMMAND: $SSH_ORIGINAL_COMMAND"
  36. ## Interpret \ to allow passing spaces (want to avoid possible issue with \n)
  37. #read -a ssh_args <<< "${SSH_ORIGINAL_COMMAND}"
  38. ssh_args=(${SSH_ORIGINAL_COMMAND})
  39. exec sudo "${ssh_args[@]::3}" \
  40. "--log-file=/var/log/rsync/target_$1_rsync.log" \
  41. "--log-file-format=%i %o %f %l %b" \
  42. "${ssh_args[@]:3}"
  43. elif [[ "$SSH_ORIGINAL_COMMAND" =~ ^"rsync --server --sender -"[vnloHgDtpArRzCeiLsfx\.]+(" --"[a-z=%-]+|" --partial-dir .rsync-partial")*" . /var/mirror/$ident"(|/.*)$ ]]; then
  44. ## Interpret \ to allow passing spaces (want to avoid possible issue with \n)
  45. #read -a ssh_args <<< "${SSH_ORIGINAL_COMMAND}"
  46. ssh_args=(${SSH_ORIGINAL_COMMAND})
  47. last_arg="${ssh_args[@]: -1:1}"
  48. if ! new_path=$(realpath "$last_arg" 2>/dev/null); then
  49. log "FINAL PATH INVALID"
  50. reject
  51. fi
  52. if [[ "$new_path" != "$last_arg" ]] &&
  53. [[ "$new_path" != "/var/mirror/$ident/"* ]] &&
  54. [[ "$new_path" != "/var/mirror/$ident" ]]; then
  55. log "FINAL PATH SUSPICIOUS"
  56. reject
  57. fi
  58. log "ACCEPTED RECOVER COMMAND: $SSH_ORIGINAL_COMMAND"
  59. exec sudo "${ssh_args[@]}"
  60. elif [[ "$SSH_ORIGINAL_COMMAND" =~ ^"request-recovery-key"$ ]]; then
  61. log "ACCEPTED RECOVERY KEY REQUEST: $SSH_ORIGINAL_COMMAND"
  62. exec sudo /usr/local/sbin/request-recovery-key "" "$ident"
  63. else
  64. log "REFUSED COMMAND AS IT DOESN'T MATCH ANY EXPECTED COMMAND"
  65. reject
  66. fi
  67. ## For other commands, like `find` or `md5`, that could be used to
  68. ## challenge the backups and check that archive is actually
  69. ## functional, I would suggest to write a simple command that takes no
  70. ## arguments, so as to prevent allowing wildcards or suspicious
  71. ## contents. Letting `find` go through is dangerous for instance
  72. ## because of the `-exec`. And path traversal can be done also when
  73. ## allowing /my/path/* by using '..'. This is why a fixed purpose
  74. ## embedded executable will be much simpler to handle, and to be honest
  75. ## we don't need much more.