Skip to content

shell Git Command Guide

The git shell command provides a restricted shell environment that only allows Git operations, making it ideal for securing remote Git access. It restricts users to Git commands only, preventing general shell access while allowing full Git repository operations.

Terminal window
git shell [-c <command>]
OptionDescription
-c <command>Execute specific command in git shell

None - operates as interactive shell or executes single command

Git Shell Restrictions:
├── Only Git commands allowed
├── No general shell access
├── No file system browsing
├── No arbitrary command execution
├── Controlled Git operations only
└── Secure remote access
Git Shell Modes:
├── Interactive: Provides Git command prompt
├── Single Command: Execute one Git command
├── SSH Integration: Used with SSH forced commands
├── User Restriction: Limits user capabilities
└── Audit Trail: Logs all Git operations
Security Architecture:
├── SSH Key Authentication: Required for access
├── Command Whitelisting: Only Git commands permitted
├── Path Restriction: Limited to Git repositories
├── No Shell Escapes: Cannot break out to system shell
└── Access Logging: All operations are logged
Terminal window
# Start interactive git shell
git shell
# Git shell prompt (limited commands)
git> git --version
git> git status
git> exit
Terminal window
# Execute single git command
git shell -c "git --version"
# Run git status
git shell -c "git status"
# Execute complex git command
git shell -c "git log --oneline -5"
Terminal window
# SSH with git shell (server side)
# In ~/.ssh/authorized_keys:
command="git shell -c \"$SSH_ORIGINAL_COMMAND\"",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-rsa AAAAB3... user@host
# Client connects normally
ssh git@server
# Automatically enters git shell
Terminal window
# Configure git shell as user shell
sudo usermod -s /usr/bin/git-shell gituser
# Create git user with restricted shell
sudo useradd -m -s /usr/bin/git-shell gituser
# Setup SSH keys for git user
sudo -u gituser mkdir -p /home/gituser/.ssh
sudo -u gituser chmod 700 /home/gituser/.ssh
# Add authorized keys
sudo -u gituser tee /home/gituser/.ssh/authorized_keys << EOF
command="git shell -c \"$SSH_ORIGINAL_COMMAND\"",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-rsa AAAAB3... developer@company.com
EOF
Terminal window
# Setup repository with git shell access
sudo mkdir -p /srv/git/project.git
sudo chown gituser:gituser /srv/git/project.git
sudo -u gituser git init --bare /srv/git/project.git
# Configure repository permissions
sudo -u gituser chmod -R 755 /srv/git/project.git
# Client access
git clone git@server:/srv/git/project.git
Terminal window
# Setup multiple repositories
sudo mkdir -p /srv/git
sudo chown gituser:gituser /srv/git
# Create repositories
sudo -u gituser mkdir /srv/git/repo1.git /srv/git/repo2.git
sudo -u gituser git init --bare /srv/git/repo1.git
sudo -u gituser git init --bare /srv/git/repo2.git
# Client access to specific repos
git clone git@server:repo1.git
git clone git@server:repo2.git
Terminal window
# Create custom git shell commands
sudo mkdir -p /usr/local/lib/git-shell-commands
# Add custom command
sudo tee /usr/local/lib/git-shell-commands/help << 'EOF'
#!/bin/bash
echo "Available commands:"
echo " git-receive-pack <repo> - Push to repository"
echo " git-upload-pack <repo> - Pull from repository"
echo " git-upload-archive <repo> - Archive repository"
echo " help - Show this help"
EOF
sudo chmod +x /usr/local/lib/git-shell-commands/help
Terminal window
# Configure git shell behavior
export GIT_SHELL_LOG=/var/log/git-shell.log # Log file location
# Configure allowed commands
GIT_SHELL_COMMANDS="/usr/local/lib/git-shell-commands"
# Configure repository paths
GIT_REPOS="/srv/git"
# Configure SSH options
GIT_SHELL_SSH_OPTS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding"
Terminal window
# Use dedicated git user
sudo useradd -r -s /usr/bin/git-shell git
# Restrict SSH access
# Only allow git operations
command="git shell -c \"$SSH_ORIGINAL_COMMAND\"",no-port-forwarding,no-X11-forwarding,no-agent-forwarding
# Use SSH keys only
# Disable password authentication for git user
# Regular permission audits
find /srv/git -type d -exec chmod 755 {} \;
find /srv/git -type f -exec chmod 644 {} \;
Terminal window
# Per-user repository access
sudo -u gituser mkdir -p /srv/git/user-specific
sudo -u gituser chmod 700 /srv/git/user-specific
# Group-based access
sudo groupadd git-developers
sudo usermod -a -G git-developers gituser
# Repository permissions
sudo chown -R gituser:git-developers /srv/git/project.git
sudo chmod -R 770 /srv/git/project.git
#!/bin/bash
# Complete git shell server setup
setup_git_server() {
local git_user="git"
local git_home="/home/$git_user"
local repos_dir="/srv/git"
echo "Setting up Git server with git-shell..."
# Create git user
sudo useradd -m -s /usr/bin/git-shell "$git_user"
# Setup SSH directory
sudo -u "$git_user" mkdir -p "$git_home/.ssh"
sudo -u "$git_user" chmod 700 "$git_home/.ssh"
# Setup repositories directory
sudo mkdir -p "$repos_dir"
sudo chown "$git_user:$git_user" "$repos_dir"
sudo chmod 755 "$repos_dir"
# Configure SSH
sudo tee /etc/ssh/sshd_config.d/git-shell.conf << EOF
# Git shell configuration
Match User $git_user
ForceCommand git shell -c "\$SSH_ORIGINAL_COMMAND"
PermitTTY no
PermitTunnel no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
EOF
# Restart SSH
sudo systemctl restart sshd
echo "Git server setup complete"
echo "Add SSH keys to $git_home/.ssh/authorized_keys"
echo "Create repositories in $repos_dir"
}
setup_git_server
Terminal window
# Automated repository creation
create_git_repo() {
local repo_name="$1"
local repos_dir="/srv/git"
local git_user="git"
echo "Creating repository: $repo_name"
# Create bare repository
sudo -u "$git_user" mkdir -p "$repos_dir/$repo_name.git"
sudo -u "$git_user" git init --bare "$repos_dir/$repo_name.git"
# Set permissions
sudo chown -R "$git_user:$git_user" "$repos_dir/$repo_name.git"
sudo chmod -R 755 "$repos_dir/$repo_name.git"
# Initialize with post-receive hook for notifications
sudo -u "$git_user" tee "$repos_dir/$repo_name.git/hooks/post-receive" << 'EOF'
#!/bin/bash
echo "Push received in $PWD" >> /var/log/git-pushes.log
echo "User: $USER" >> /var/log/git-pushes.log
echo "Command: $SSH_ORIGINAL_COMMAND" >> /var/log/git-pushes.log
echo "---" >> /var/log/git-pushes.log
EOF
sudo chmod +x "$repos_dir/$repo_name.git/hooks/post-receive"
echo "Repository created: $repo_name"
echo "Clone URL: git@server:$repo_name.git"
}
create_git_repo "my-project"
Terminal window
# Git shell access logging
setup_logging() {
echo "Setting up git shell logging..."
# Create log directory
sudo mkdir -p /var/log/git-shell
sudo chown git:git /var/log/git-shell
# Configure rsyslog for git shell
sudo tee /etc/rsyslog.d/git-shell.conf << 'EOF'
# Git shell logging
:programname, isequal, "git-shell" /var/log/git-shell/auth.log
& stop
EOF
sudo systemctl restart rsyslog
# Custom git shell wrapper for logging
sudo tee /usr/local/bin/git-shell-logged << 'EOF'
#!/bin/bash
LOG_FILE="/var/log/git-shell/$(date +%Y%m%d).log"
echo "$(date): $USER@$SSH_CLIENT: $SSH_ORIGINAL_COMMAND" >> "$LOG_FILE"
/usr/bin/git-shell -c "$SSH_ORIGINAL_COMMAND"
EOF
sudo chmod +x /usr/local/bin/git-shell-logged
sudo ln -sf /usr/local/bin/git-shell-logged /usr/bin/git-shell
}
setup_logging
Terminal window
# Test SSH connection
ssh -T git@server
# Check SSH key setup
ssh-keygen -l -f ~/.ssh/id_rsa.pub
# Verify authorized_keys format
cat ~/.ssh/authorized_keys
# Check SSH server configuration
ssh -v git@server
Terminal window
# Check repository permissions
ls -la /srv/git/project.git/
# Fix ownership
sudo chown -R git:git /srv/git/project.git
# Check file permissions
find /srv/git/project.git -type f -exec ls -l {} \; | head -5
# Verify git user access
sudo -u git ls -la /srv/git/
Terminal window
# Test git shell directly
sudo -u git /usr/bin/git-shell -c "git --version"
# Check PATH in git shell
sudo -u git /usr/bin/git-shell -c "echo \$PATH"
# Verify git commands work
sudo -u git /usr/bin/git-shell -c "which git"
# Check for missing commands
sudo -u git /usr/bin/git-shell -c "git --help | head -5"
Terminal window
# Test repository access
sudo -u git git ls-remote /srv/git/project.git
# Check repository validity
sudo -u git git --git-dir=/srv/git/project.git fsck
# Verify remote setup
git remote -v
# Test push access
git push git@server:project.git main
Terminal window
# Check git shell logs
tail -f /var/log/git-shell/auth.log
# Enable verbose SSH logging
ssh -vv git@server
# Debug git operations
GIT_TRACE=1 git clone git@server:project.git
# Check system logs
journalctl -u sshd | grep git
#!/bin/bash
# Enterprise-grade git server with git-shell
enterprise_git_server() {
echo "=== Enterprise Git Server Setup ==="
# Server configuration
GIT_USER="git"
GIT_HOME="/home/$GIT_USER"
REPOS_BASE="/srv/git/repositories"
LOG_DIR="/var/log/git-server"
# Create git user
sudo useradd -r -m -d "$GIT_HOME" -s /usr/bin/git-shell "$GIT_USER"
# Setup directories
sudo mkdir -p "$REPOS_BASE" "$LOG_DIR"
sudo chown "$GIT_USER:$GIT_USER" "$REPOS_BASE" "$LOG_DIR"
# Configure SSH
sudo tee /etc/ssh/sshd_config.d/git-server.conf << EOF
Match User $GIT_USER
ForceCommand /usr/local/bin/git-shell-wrapper
PermitTTY no
PermitTunnel no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
PasswordAuthentication no
EOF
# Create git shell wrapper
sudo tee /usr/local/bin/git-shell-wrapper << 'EOF'
#!/bin/bash
LOG_FILE="/var/log/git-server/$(date +%Y%m%d).log"
# Log the access
echo "$(date '+%Y-%m-%d %H:%M:%S') $USER $SSH_CLIENT $SSH_ORIGINAL_COMMAND" >> "$LOG_FILE"
# Execute git command
exec /usr/bin/git-shell -c "$SSH_ORIGINAL_COMMAND"
EOF
sudo chmod +x /usr/local/bin/git-shell-wrapper
# Setup log rotation
sudo tee /etc/logrotate.d/git-server << EOF
/var/log/git-server/*.log {
daily
rotate 30
compress
missingok
notifempty
create 644 $GIT_USER $GIT_USER
}
EOF
# Restart services
sudo systemctl restart sshd
sudo systemctl restart logrotate
echo "Enterprise git server setup complete"
echo "Add developer SSH keys to $GIT_HOME/.ssh/authorized_keys"
}
enterprise_git_server
Terminal window
# Advanced access control with git-shell
access_control_system() {
echo "=== Git Repository Access Control ==="
# Configuration
CONTROL_FILE="/etc/git-access-control.conf"
ALLOWED_USERS="/etc/git-allowed-users"
# Create control script
sudo tee /usr/local/bin/git-access-control << 'EOF'
#!/bin/bash
REPO_PATH="$1"
USER="$2"
# Check if user is allowed
if ! grep -q "^$USER$" /etc/git-allowed-users; then
echo "Access denied: User $USER not authorized"
exit 1
fi
# Check repository permissions
REPO_NAME=$(basename "$REPO_PATH" .git)
USER_PERMS="/etc/git-repo-perms/$REPO_NAME"
if [ -f "$USER_PERMS" ]; then
if ! grep -q "^$USER:" "$USER_PERMS"; then
echo "Access denied: User $USER not authorized for $REPO_NAME"
exit 1
fi
# Check operation permissions
USER_LINE=$(grep "^$USER:" "$USER_PERMS")
PERMS=$(echo "$USER_LINE" | cut -d: -f2)
case "$SSH_ORIGINAL_COMMAND" in
*git-receive-pack*)
if [[ "$PERMS" != *"write"* ]]; then
echo "Access denied: No write permission for $REPO_NAME"
exit 1
fi
;;
*git-upload-pack*)
if [[ "$PERMS" != *"read"* ]]; then
echo "Access denied: No read permission for $REPO_NAME"
exit 1
fi
;;
esac
fi
# Log access
echo "$(date): $USER accessed $REPO_NAME ($SSH_ORIGINAL_COMMAND)" >> /var/log/git-access.log
# Allow access
exec /usr/bin/git-shell -c "$SSH_ORIGINAL_COMMAND"
EOF
sudo chmod +x /usr/local/bin/git-access-control
# Setup permission files
sudo mkdir -p /etc/git-repo-perms
sudo tee /etc/git-allowed-users << 'EOF'
alice
bob
charlie
EOF
# Example repo permissions
sudo tee /etc/git-repo-perms/secret-project << 'EOF'
alice:read,write
bob:read
EOF
echo "Access control system configured"
echo "Update /etc/git-allowed-users and /etc/git-repo-perms/*"
}
access_control_system
Terminal window
# Git shell for CI/CD systems
ci_cd_integration() {
echo "=== CI/CD Git Shell Integration ==="
# Create CI user
sudo useradd -r -m -d /home/ci -s /usr/bin/git-shell ci
# Setup SSH for CI
sudo -u ci mkdir -p /home/ci/.ssh
sudo -u ci chmod 700 /home/ci/.ssh
# CI-specific authorized keys
sudo -u ci tee /home/ci/.ssh/authorized_keys << 'EOF'
command="/usr/local/bin/ci-git-shell",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-rsa AAAAB3... ci-server@company.com
EOF
# Create CI git shell
sudo tee /usr/local/bin/ci-git-shell << 'EOF'
#!/bin/bash
LOG_FILE="/var/log/ci-git.log"
# Validate CI operations
case "$SSH_ORIGINAL_COMMAND" in
*git-upload-pack*)
# Allow pulls
echo "$(date): CI pull by $USER" >> "$LOG_FILE"
;;
*git-receive-pack*)
# Restrict pushes - only allow to specific branches
if [[ "$SSH_ORIGINAL_COMMAND" == *refs/heads/ci/* ]]; then
echo "$(date): CI push to ci/ branch by $USER" >> "$LOG_FILE"
else
echo "CI can only push to ci/* branches"
exit 1
fi
;;
*)
echo "Command not allowed for CI user"
exit 1
;;
esac
# Execute command
exec /usr/bin/git-shell -c "$SSH_ORIGINAL_COMMAND"
EOF
sudo chmod +x /usr/local/bin/ci-git-shell
echo "CI/CD git shell integration complete"
echo "CI can now safely access repositories"
}
ci_cd_integration
Terminal window
# Git shell for backup operations
backup_system() {
echo "=== Git Backup System ==="
# Create backup user
sudo useradd -r -m -d /home/backup -s /usr/bin/git-shell backup
# Setup backup repositories
BACKUP_ROOT="/srv/git-backups"
sudo mkdir -p "$BACKUP_ROOT"
sudo chown backup:backup "$BACKUP_ROOT"
# Backup script
sudo tee /usr/local/bin/git-backup << 'EOF'
#!/bin/bash
SOURCE_REPO="$1"
BACKUP_NAME="$(basename "$SOURCE_REPO" .git)-$(date +%Y%m%d-%H%M%S)"
echo "Creating backup: $BACKUP_NAME"
# Create backup directory
sudo -u backup mkdir -p "/srv/git-backups/$BACKUP_NAME"
# Mirror repository
sudo -u backup git clone --mirror "$SOURCE_REPO" "/srv/git-backups/$BACKUP_NAME"
# Compress backup
cd /srv/git-backups
tar czf "${BACKUP_NAME}.tar.gz" "$BACKUP_NAME"
rm -rf "$BACKUP_NAME"
echo "Backup created: /srv/git-backups/${BACKUP_NAME}.tar.gz"
# Cleanup old backups (keep last 7 days)
find /srv/git-backups -name "*.tar.gz" -mtime +7 -delete
EOF
sudo chmod +x /usr/local/bin/git-backup
# Scheduled backup
sudo tee /etc/cron.daily/git-backups << 'EOF'
#!/bin/bash
# Daily git repository backups
REPOS="/srv/git/*.git"
BACKUP_SCRIPT="/usr/local/bin/git-backup"
for repo in $REPOS; do
if [ -d "$repo" ]; then
$BACKUP_SCRIPT "$repo"
fi
done
EOF
sudo chmod +x /etc/cron.daily/git-backups
echo "Git backup system configured"
echo "Daily backups will be created in /srv/git-backups"
}
backup_system
Terminal window
# Git shell monitoring and alerting
monitoring_system() {
echo "=== Git Shell Monitoring ==="
# Setup monitoring script
sudo tee /usr/local/bin/git-monitor << 'EOF'
#!/bin/bash
LOG_FILE="/var/log/git-shell/monitor.log"
ALERT_EMAIL="admin@company.com"
# Monitor active connections
ACTIVE_CONNECTIONS=$(netstat -tn | grep :22 | grep ESTABLISHED | wc -l)
echo "$(date): Active SSH connections: $ACTIVE_CONNECTIONS" >> "$LOG_FILE"
# Check disk usage
DISK_USAGE=$(df /srv/git | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 90 ]; then
echo "ALERT: Disk usage high ($DISK_USAGE%)" | mail -s "Git Server Disk Alert" "$ALERT_EMAIL"
fi
# Check repository integrity
for repo in /srv/git/*.git; do
if ! sudo -u git git --git-dir="$repo" fsck --quiet >/dev/null 2>&1; then
echo "ALERT: Repository corruption in $(basename "$repo")" | mail -s "Git Repository Corruption" "$ALERT_EMAIL"
fi
done
# Monitor access patterns
TODAY=$(date +%Y%m%d)
ACCESS_COUNT=$(grep "$TODAY" /var/log/git-shell/auth.log | wc -l)
echo "$(date): Daily access count: $ACCESS_COUNT" >> "$LOG_FILE"
# Alert on unusual activity
if [ "$ACCESS_COUNT" -gt 1000 ]; then
echo "ALERT: High access volume ($ACCESS_COUNT requests today)" | mail -s "Git Server High Activity" "$ALERT_EMAIL"
fi
EOF
sudo chmod +x /usr/local/bin/git-monitor
# Setup cron monitoring
sudo tee /etc/cron.hourly/git-monitoring << 'EOF'
#!/bin/bash
/usr/local/bin/git-monitor
EOF
sudo chmod +x /etc/cron.hourly/git-monitoring
# Setup log directories
sudo mkdir -p /var/log/git-shell
sudo chown git:git /var/log/git-shell
echo "Git shell monitoring configured"
echo "Hourly monitoring reports in /var/log/git-shell/"
}
monitoring_system

What’s the difference between git shell and regular shell?

Section titled “What’s the difference between git shell and regular shell?”

git shell restricts users to Git commands only, preventing general system access. Regular shell allows full system access.

Set user’s shell to git-shell: sudo usermod -s /usr/bin/git-shell username

No, git shell only allows Git commands. Any other commands are rejected.

How do I setup git shell for multiple users?

Section titled “How do I setup git shell for multiple users?”

Create separate system users, each with git-shell as their shell, and manage SSH keys individually.

What’s the difference between git shell and gitolite?

Section titled “What’s the difference between git shell and gitolite?”

gitolite is a more advanced access control system built on git shell. git shell is the basic restricted shell.

Can I customize allowed commands in git shell?

Section titled “Can I customize allowed commands in git shell?”

Yes, place custom commands in /usr/local/lib/git-shell-commands/ directory.

Configure syslog or use wrapper scripts to log all git shell operations.

No, git shell is designed for SSH access only.

How do I troubleshoot git shell connection issues?

Section titled “How do I troubleshoot git shell connection issues?”

Check SSH configuration, verify user shell setting, and test with ssh -v git@server

Can git shell restrict access to specific repositories?

Section titled “Can git shell restrict access to specific repositories?”

Use SSH forced commands or custom git shell wrappers to implement repository-level access control.

What’s the performance impact of git shell?

Section titled “What’s the performance impact of git shell?”

Minimal - it’s just a command wrapper. Git operations themselves determine performance.

Yes, git shell allows all Git operations, including LFS commands.

How do I migrate from regular shell to git shell?

Section titled “How do I migrate from regular shell to git shell?”

Change user shell with usermod, ensure SSH keys are properly configured, test access.

  1. Secure Remote Access: Provide Git-only access to servers without full shell access
  2. Repository Hosting: Run Git servers with restricted user capabilities
  3. CI/CD Security: Allow automated systems Git access without shell privileges
  4. Access Control: Implement basic user isolation for Git operations
  5. Audit Trails: Log all Git operations for compliance and monitoring
  6. Backup Systems: Enable secure backup operations with limited access
  7. Development Environments: Provide controlled Git access in shared environments
  8. Enterprise Security: Meet security requirements for Git server deployments