Fix SCP support, but break jail#2
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR removes the chroot jail functionality and replaces it with a custom script-based access control to support both SCP and SFTP operations. The change prioritizes SCP support from embedded systems over security isolation.
- Removes
internal-sftpsubsystem and chroot directory configuration from SSH - Implements custom access control script that validates SCP commands and SFTP server execution
- Trades chroot jail security for compatibility with systems that require SCP protocol support
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| files/sshd_config | Removes chroot jail configuration and switches from internal-sftp to external sftp-server |
| files/permit-scp.sh | Adds validation logic for SCP commands and SFTP server execution with path restrictions |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| "scp -d $HOME"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| # Permit standard sftp - by pats, as `internal-sftp` works only in sshd_config |
There was a problem hiding this comment.
Typo in comment: 'by pats' should be 'by path'.
| # Permit standard sftp - by pats, as `internal-sftp` works only in sshd_config | |
| # Permit standard sftp - by path, as `internal-sftp` works only in sshd_config |
| #!/bin/bash | ||
| # Permit scp | ||
| case $SSH_ORIGINAL_COMMAND in | ||
| 'scp'*) | ||
| # Permit scp when run in internal source/dest/dir mode | ||
| # NOTE: adding path does not protect from rogue symlinks | ||
| "scp -f $HOME/"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -t $HOME/"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -d $HOME"*) | ||
| $SSH_ORIGINAL_COMMAND |
There was a problem hiding this comment.
The path validation using $HOME/ prefix is insufficient for security. An attacker could use '../' sequences to escape the home directory restriction, and as noted in the comment, symlinks are not protected against.
| #!/bin/bash | ||
| # Permit scp | ||
| case $SSH_ORIGINAL_COMMAND in | ||
| 'scp'*) | ||
| # Permit scp when run in internal source/dest/dir mode | ||
| # NOTE: adding path does not protect from rogue symlinks | ||
| "scp -f $HOME/"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -t $HOME/"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -d $HOME"*) | ||
| $SSH_ORIGINAL_COMMAND |
There was a problem hiding this comment.
The path validation using $HOME/ prefix is insufficient for security. An attacker could use '../' sequences to escape the home directory restriction, and as noted in the comment, symlinks are not protected against.
| #!/bin/bash | ||
| # Permit scp | ||
| case $SSH_ORIGINAL_COMMAND in | ||
| 'scp'*) | ||
| # Permit scp when run in internal source/dest/dir mode | ||
| # NOTE: adding path does not protect from rogue symlinks | ||
| "scp -f $HOME/"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -t $HOME/"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -d $HOME"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; |
There was a problem hiding this comment.
The path validation using $HOME/ prefix is insufficient for security. An attacker could use '../' sequences to escape the home directory restriction, and as noted in the comment, symlinks are not protected against.
| # Permit scp when run in internal source/dest/dir mode | ||
| # NOTE: adding path does not protect from rogue symlinks | ||
| "scp -f $HOME/"*) | ||
| $SSH_ORIGINAL_COMMAND |
There was a problem hiding this comment.
Executing $SSH_ORIGINAL_COMMAND without proper sanitization or validation is a security risk. The command should be parsed and validated more thoroughly to prevent command injection attacks.
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -t $HOME/"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -d $HOME"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| # Permit standard sftp - by pats, as `internal-sftp` works only in sshd_config | ||
| "/usr/lib/openssh/sftp-server") | ||
| $SSH_ORIGINAL_COMMAND |
There was a problem hiding this comment.
Executing $SSH_ORIGINAL_COMMAND without proper sanitization or validation is a security risk. The command should be parsed and validated more thoroughly to prevent command injection attacks.
| $SSH_ORIGINAL_COMMAND | |
| ;; | |
| "scp -t $HOME/"*) | |
| $SSH_ORIGINAL_COMMAND | |
| ;; | |
| "scp -d $HOME"*) | |
| $SSH_ORIGINAL_COMMAND | |
| ;; | |
| # Permit standard sftp - by pats, as `internal-sftp` works only in sshd_config | |
| "/usr/lib/openssh/sftp-server") | |
| $SSH_ORIGINAL_COMMAND | |
| set -- $SSH_ORIGINAL_COMMAND | |
| if [ "$1" = "scp" ] && [ "$2" = "-f" ] && [[ "$3" == $HOME/* ]]; then | |
| exec scp -f "$3" | |
| else | |
| echo "Access Denied" | |
| exit 1 | |
| fi | |
| ;; | |
| "scp -t $HOME/"*) | |
| set -- $SSH_ORIGINAL_COMMAND | |
| if [ "$1" = "scp" ] && [ "$2" = "-t" ] && [[ "$3" == $HOME/* ]]; then | |
| exec scp -t "$3" | |
| else | |
| echo "Access Denied" | |
| exit 1 | |
| fi | |
| ;; | |
| "scp -d $HOME"*) | |
| set -- $SSH_ORIGINAL_COMMAND | |
| if [ "$1" = "scp" ] && [ "$2" = "-d" ] && [[ "$3" == $HOME* ]]; then | |
| exec scp -d "$3" | |
| else | |
| echo "Access Denied" | |
| exit 1 | |
| fi | |
| ;; | |
| # Permit standard sftp - by pats, as `internal-sftp` works only in sshd_config | |
| "/usr/lib/openssh/sftp-server") | |
| exec /usr/lib/openssh/sftp-server |
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -t $HOME/"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -d $HOME"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| # Permit standard sftp - by pats, as `internal-sftp` works only in sshd_config | ||
| "/usr/lib/openssh/sftp-server") | ||
| $SSH_ORIGINAL_COMMAND |
There was a problem hiding this comment.
Executing $SSH_ORIGINAL_COMMAND without proper sanitization or validation is a security risk. The command should be parsed and validated more thoroughly to prevent command injection attacks.
| $SSH_ORIGINAL_COMMAND | |
| ;; | |
| "scp -t $HOME/"*) | |
| $SSH_ORIGINAL_COMMAND | |
| ;; | |
| "scp -d $HOME"*) | |
| $SSH_ORIGINAL_COMMAND | |
| ;; | |
| # Permit standard sftp - by pats, as `internal-sftp` works only in sshd_config | |
| "/usr/lib/openssh/sftp-server") | |
| $SSH_ORIGINAL_COMMAND | |
| # Parse and validate arguments for 'scp -f' | |
| set -- $SSH_ORIGINAL_COMMAND | |
| if [ "$1" = "scp" ] && [ "$2" = "-f" ] && [[ "$3" == $HOME/* ]]; then | |
| exec scp -f "$3" | |
| else | |
| echo "Access Denied" | |
| exit 1 | |
| fi | |
| ;; | |
| "scp -t $HOME/"*) | |
| # Parse and validate arguments for 'scp -t' | |
| set -- $SSH_ORIGINAL_COMMAND | |
| if [ "$1" = "scp" ] && [ "$2" = "-t" ] && [[ "$3" == $HOME/* ]]; then | |
| exec scp -t "$3" | |
| else | |
| echo "Access Denied" | |
| exit 1 | |
| fi | |
| ;; | |
| "scp -d $HOME"*) | |
| # Parse and validate arguments for 'scp -d' | |
| set -- $SSH_ORIGINAL_COMMAND | |
| if [ "$1" = "scp" ] && [ "$2" = "-d" ] && [[ "$3" == $HOME* ]]; then | |
| exec scp -d "$3" | |
| else | |
| echo "Access Denied" | |
| exit 1 | |
| fi | |
| ;; | |
| # Permit standard sftp - by pats, as `internal-sftp` works only in sshd_config | |
| "/usr/lib/openssh/sftp-server") | |
| # Only allow exact match for sftp-server | |
| if [ "$SSH_ORIGINAL_COMMAND" = "/usr/lib/openssh/sftp-server" ]; then | |
| exec /usr/lib/openssh/sftp-server | |
| else | |
| echo "Access Denied" | |
| exit 1 | |
| fi |
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -t $HOME/"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| "scp -d $HOME"*) | ||
| $SSH_ORIGINAL_COMMAND | ||
| ;; | ||
| # Permit standard sftp - by pats, as `internal-sftp` works only in sshd_config | ||
| "/usr/lib/openssh/sftp-server") | ||
| $SSH_ORIGINAL_COMMAND |
There was a problem hiding this comment.
Executing $SSH_ORIGINAL_COMMAND without proper sanitization or validation is a security risk. The command should be parsed and validated more thoroughly to prevent command injection attacks.
| $SSH_ORIGINAL_COMMAND | |
| ;; | |
| "scp -t $HOME/"*) | |
| $SSH_ORIGINAL_COMMAND | |
| ;; | |
| "scp -d $HOME"*) | |
| $SSH_ORIGINAL_COMMAND | |
| ;; | |
| # Permit standard sftp - by pats, as `internal-sftp` works only in sshd_config | |
| "/usr/lib/openssh/sftp-server") | |
| $SSH_ORIGINAL_COMMAND | |
| # Parse and validate the command | |
| set -- $SSH_ORIGINAL_COMMAND | |
| if [ "$1" = "scp" ] && [ "$2" = "-f" ] && [[ "$3" == $HOME/* ]]; then | |
| exec scp -f "$3" | |
| else | |
| echo "Access Denied" | |
| exit 1 | |
| fi | |
| ;; | |
| "scp -t $HOME/"*) | |
| set -- $SSH_ORIGINAL_COMMAND | |
| if [ "$1" = "scp" ] && [ "$2" = "-t" ] && [[ "$3" == $HOME/* ]]; then | |
| exec scp -t "$3" | |
| else | |
| echo "Access Denied" | |
| exit 1 | |
| fi | |
| ;; | |
| "scp -d $HOME"*) | |
| set -- $SSH_ORIGINAL_COMMAND | |
| if [ "$1" = "scp" ] && [ "$2" = "-d" ] && [[ "$3" == $HOME* ]]; then | |
| exec scp -d "$3" | |
| else | |
| echo "Access Denied" | |
| exit 1 | |
| fi | |
| ;; | |
| # Permit standard sftp - by pats, as `internal-sftp` works only in sshd_config | |
| "/usr/lib/openssh/sftp-server") | |
| exec /usr/lib/openssh/sftp-server |
Fix SCP support, but break jail. Proposing this change anyway as I really needed support for SCP from an embedded system (some modern systems somehow issue SFTP even when
scpclient is called).Unfortunately, OpenSSH allows jailing with SFTP by using a dirty hack -
internal-sftpis a magic way to call internal SFTP process, allowing user to break the rules ofChrootDirectory, which require a set of files to be under path specified (things like binary specified inForceCommandand basic /dev nodes).At the same time,
internal-sftpbinary (/usr/lib/openssh/sftp-server) completely delegates jailing/chrooting to OpenSSH and has no command-line options to restrict what it can read/write.On the other hand,
scpcan be somewhat jailed on its own (except symlinks, which should be doable by other tools on Linux) due to a fixed way it sends and receives files with-f,-tand-d.Maybe some alternative SSH server should also be considered - I was originally hoping for the ability to
Matchselected subsystem and based on that set standard SFTP config or just leaveForceCommandwith proxy script for SCP.