Introduction

Perhaps because I’ve always used environment variables to set proxies when installing packages with NPM, Composer, or Homebrew to improve download speeds, I subconsciously thought that the ALL_PROXY environment variable might apply to all protocols.

However, when I used git clone [email protected]:u-boot/u-boot.git to clone a project, I noticed there was no response for a long time. Looking at Surge’s traffic monitor, I saw no traffic passing through, which meant it wasn’t using the ALL_PROXY I had set.

After some search-engine-oriented learning, I discovered that Git supports three protocols:

  • HTTP
  • SSH
  • Git Protocol

Common Misconceptions

Many online tutorials suggest configuring http and https proxies in the ~/.gitconfig file:

[http "https://github.com/"]
    proxy = http://<host>:<port>/
[https "https://github.com/"]
    proxy = https://<host>:<port>/

Or setting proxies through commands like:

git config --global http.proxy http://username:[email protected]:8080
git config --global https.proxy http://username:[email protected]:8080

Or using temporary environment variables as I did before:

export https_proxy=http://127.0.0.1:8234;export http_proxy=http://127.0.0.1:8234;export all_proxy=socks5://127.0.0.1:8235

Note: This method only works for HTTP protocol! If you’re using SSH protocol for cloning, these settings won’t have any effect.

Configuring SSH Proxy

As in my previous article “Using ClashX to Accelerate SSH”, we just need to add the following configuration to the ~/.ssh/config file:

Host github.com
    User git
    ProxyCommand nc -x 127.0.0.1:8235 -X 5 %h %p

After setting this up, you can test cloning projects from Github using SSH.

For developers using gh 1, the same applies if you’re using the SSH protocol.

gh repo clone u-boot/u-boot

Github CLI clone iTerm 2 tasks

Git Protocol

I initially didn’t plan to write about the Git Protocol since it’s rarely used, but when learning about operating systems, I needed to install riscv-tools. Following the steps from 6.S081: Operating System Engineering, I found that cloning the glibc source code was stuck due to network issues. Looking at the logs, I saw that the repository address was git://sourceware.org/git/glibc.git.

At first, I didn’t recognize this as the Git Protocol. I set up a proxy for sourceware.org in the configuration file using the SSH method, but it didn’t work. After pondering for a while, I realized this wasn’t the SSH protocol.

Now that I had identified the problem, the solution was simple. First, I needed to create a git-proxy script:

#! /bin/bash

# connect to the Git repository through a SOCKS proxy


# default setting is to use port 1080 on the local host
proxy="localhost:1080"
from="default"

# check if there is a value in the git configuration
if git config --get socks.proxy >& /dev/null; then
    proxy=`git config --get socks.proxy`
    from="git's socks.proxy"
fi

# check if a generic proxy has been defined in the environment
if [ -n "$ALL_PROXY" ]; then
    proxy="$ALL_PROXY"
    from="\$ALL_PROXY"
fi
if [ -n "$all_proxy" ]; then
    proxy="$all_proxy"
    from="\$all_proxy"
fi

# check if a SOCKS proxy has been defined in the environment
if [ -n "$SOCKS_PROXY" ]; then
    proxy="$SOCKS_PROXY"
    from="\$SOCKS_PROXY"
fi
if [ -n "$socks_proxy" ]; then
    proxy="$socks_proxy"
    from="\$socks_proxy"
fi
if [ -n "$SOCKS5_PROXY" ]; then
    proxy="$SOCKS5_PROXY"
    from="\$SOCKS5_PROXY"
fi
if [ -n "$socks5_proxy" ]; then
    proxy="$socks5_proxy"
    from="\$socks5_proxy"
fi

# check if a git specific SOCKS proxy has been defined in the environment
if [ -n "$GIT_SOCKS_PROXY" ]; then
    proxy="$GIT_SOCKS_PROXY"
    from="\$GIT_SOCKS_PROXY"
fi

function usage() {
cat << @EOF
Usage:
  `basename $0` HOST PORT

Helper script to connect to a Git repository over the git:// protocol at host HOST and port PORT through a SOCKS proxy at $proxy ($from).

To use the proxy for all git:// traffic, set the core.gitproxy option to "git-proxy":

  git config core.gitproxy "git-proxy"


To use the proxy only for some reporitories, use the syntax explained in git-config(1).

To configure which proxy to use, set an appropriate environment variable (see below) or socks.proxy option to the proxy address, for example "localhost:1080":

  git config socks.proxy "localhost:1080"


The address of the proxy is read from (in order of priority):
  - the GIT_SOCKS_PROXY environment variable;
  - the SOCKS_PROXY or SOCKS5_PROXY environment variable;
  - the ALL_PROXY environment variable (see curl(1));
  - the socks.proxy git option;
  - the default value: localhost:1080 .
@EOF
}

if [ -z "$1" ] || [ -z "$2" ] || [ -n "$3" ]; then
  usage
  exit 1
fi

# connect through the specifid proxy

nc -x "$proxy" "$1" "$2"

Save the above code as a script in any directory in your PATH, such as /usr/local/bin/, and name it git-proxy. Then run the following command to verify it works:

git-proxy --help

Usage:
  git-proxy HOST PORT

Helper script to connect to a Git repository over the git:// protocol at host HOST and port PORT through a SOCKS proxy at 127.0.0.1:8235 (git's socks.proxy).

To use the proxy for all git:// traffic, set the core.gitproxy option to "git-proxy":

  git config core.gitproxy "git-proxy"


To use the proxy only for some reporitories, use the syntax explained in git-config(1).

To configure which proxy to use, set an appropriate environment variable (see below) or socks.proxy option to the proxy address, for example "localhost:1080":

  git config socks.proxy "localhost:1080"


The address of the proxy is read from (in order of priority):
  - the GIT_SOCKS_PROXY environment variable;
  - the SOCKS_PROXY or SOCKS5_PROXY environment variable;
  - the ALL_PROXY environment variable (see curl(1));
  - the socks.proxy git option;
  - the default value: localhost:1080 .

Then follow the instructions and run:

# It's recommended to use the absolute path for the script, otherwise you might get: error: cannot run git-proxy: No such file or directory.
git config core.gitproxy "/usr/local/bin/git-proxy"
git config socks.proxy "127.0.0.1:8235"

Also, since Surge’s Copy Shell Export Command generates commands that include export all_proxy=socks5://127.0.0.1:8235, according to the git-proxy script logic, if the all_proxy environment variable is set, it will override Git’s global configuration!!! This is what led me to discover the following issue.

Note: The proxy address here doesn’t need the protocol type; just use the HOST:PORT format. If you include the protocol, like socks5://127.0.0.1:8235, it will cause “nc: getaddrinfo: nodename nor servname provided, or not known” and fail to proxy the Git Protocol.

Now test if traffic through the Git Protocol is routed through our Surge proxy as expected:

brew install riscv-tools
==> Downloading https://ghcr.io/v2/homebrew/core/mpfr/manifests/4.1.0
Already downloaded: /Users/George/Library/Caches/Homebrew/downloads/e8b18e060649c00d9e1e351e3e05515c365e52dbd7bd392e2b99808d1f355b5b--mpfr-4.1.0.bottle_manifest.json
==> Downloading https://ghcr.io/v2/homebrew/core/mpfr/blobs/sha256:81ced499f237acfc2773711a3f8aa985572eaab2344a70485c06f72405e4a5e7
Already downloaded: /Users/George/Library/Caches/Homebrew/downloads/1816ae3e6ff09ea7ad42b8f5ff16142fd0a973c41c5633dfa3b1cf4fa2e1d51b--mpfr--4.1.0.arm64_monterey.bottle.tar.gz
==> Downloading https://ghcr.io/v2/homebrew/core/gawk/manifests/5.1.1
Already downloaded: /Users/George/Library/Caches/Homebrew/downloads/13f45768bf43ccad0a4fabeca4923e0a6836a412b1255585fba6260956e53b70--gawk-5.1.1.bottle_manifest.json
==> Downloading https://ghcr.io/v2/homebrew/core/gawk/blobs/sha256:093465f34b94ec8ddeb4ff8dab2a02dafbccf8ec05f6ef0391673b7c4fd0a91f
Already downloaded: /Users/George/Library/Caches/Homebrew/downloads/0e765b98a270520e3539fb021f555e78705e08b6f24da7e20a56d1f228be9ff1--gawk--5.1.1.arm64_monterey.bottle.tar.gz
==> Downloading https://ghcr.io/v2/homebrew/core/gnu-sed/manifests/4.8
Already downloaded: /Users/George/Library/Caches/Homebrew/downloads/71fd4a7118e6fb954728ce6a8f92871d721711578de17baa01337b3570581179--gnu-sed-4.8.bottle_manifest.json
==> Downloading https://ghcr.io/v2/homebrew/core/gnu-sed/blobs/sha256:78481cc3509f617328d3c361c21beef829f24f4b130cabfc08ed6e4ce83f2286
Already downloaded: /Users/George/Library/Caches/Homebrew/downloads/c0d5ea51ee74ae84aee90d77161075b8567d8c949e99a85582e2fa1570eb824b--gnu-sed--4.8.arm64_monterey.bottle.tar.gz
==> Downloading https://ghcr.io/v2/homebrew/core/isl/manifests/0.24
Already downloaded: /Users/George/Library/Caches/Homebrew/downloads/cf108f3cbdbad48a5ecbe0a148e97b47590045a58151ad768bcf7219ec23305a--isl-0.24.bottle_manifest.json
==> Downloading https://ghcr.io/v2/homebrew/core/isl/blobs/sha256:be08c3e9765655ad5bfd227f9b97acb0ef88ad2307dc214ea4064cc1f51db641
Already downloaded: /Users/George/Library/Caches/Homebrew/downloads/65e4670b48d6dda0e181978e35cf506256f350f33cf7e1371b9451856ad21d41--isl--0.24.arm64_monterey.bottle.tar.gz
==> Downloading https://ghcr.io/v2/homebrew/core/libmpc/manifests/1.2.1
Already downloaded: /Users/George/Library/Caches/Homebrew/downloads/3f193de9dd7f9e68742cf207cfadd3a63b695a8026d343b668e10a9b7bcf6e52--libmpc-1.2.1.bottle_manifest.json
==> Downloading https://ghcr.io/v2/homebrew/core/libmpc/blobs/sha256:658a1d260b6f77c431451a554ef8fa36ea2b6db19b38adc05c52821598ce2647
Already downloaded: /Users/George/Library/Caches/Homebrew/downloads/d18d439506c54ddbad35cc4dc0c77e5fc3fd442c9ec926ff212d6fed1a11c8b6--libmpc--1.2.1.arm64_monterey.bottle.tar.gz
==> Cloning https://github.com/riscv/riscv-gnu-toolchain.git
Updating /Users/George/Library/Caches/Homebrew/riscv-gnu-toolchain--git
==> Checking out branch master
Already on 'master'
Your branch is up to date with 'origin/master'.
HEAD is now at 1342cd7 Merge pull request #1073 from palmer-dabbelt/qemu-system
Entering 'qemu'
Entering 'riscv-binutils'
Entering 'riscv-dejagnu'
Entering 'riscv-gcc'
Entering 'riscv-gdb'
Cloning into '/Users/George/Library/Caches/Homebrew/riscv-gnu-toolchain--git/glibc'...
Cloning into '/Users/George/Library/Caches/Homebrew/riscv-gnu-toolchain--git/musl'...
Cloning into '/Users/George/Library/Caches/Homebrew/riscv-gnu-toolchain--git/newlib'..
ps -ef | grep "nc -x 127.0.0.1:8235" | grep -v grep
  501 53932 53462   0 11:58PM ttys002    0:00.00 nc -x 127.0.0.1:8235 sourceware.org 9418

As you can see, when executing the Homebrew install command, a netcat process was started, establishing a connection to port 9418 of sourceware.org through the proxy. 9418 is the port used by the Git Protocol!

Using HTTP Port for Project Cloning with SSH Protocol

Recently, due to the shutdown of Suwa Cloud, I had to switch to another proxy service. After switching, I found I couldn’t clone Github projects normally using the SSH protocol, getting the following error:

gh repo clone golang-module/carbon
Cloning into 'carbon'...
kex_exchange_identification: Connection closed by remote host
Connection closed by UNKNOWN port 65535
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
exit status 128

After inquiring, I learned that the proxy service administrators had blocked port 22 traffic for security reasons. They also provided a solution, which is documented in the Github official documentation: Using SSH over the HTTPS port.

Following the documentation, I modified my ~/.ssh/config configuration:

Host github.com
	User git
	HostName ssh.github.com
	Port 443
	ProxyCommand nc -x 127.0.0.1:8235 -X 5 %h %p

Now I can normally accelerate Git operations using the SSH protocol!

I hope this is helpful, Happy hacking…