Skip to content
black-deskPublic

About

A transparent proxy RULE manager written in go inspired by cgproxy.

Topics

Resources

Contributing

Stars

40 stars

Watchers

1 watching

Forks

Repository files navigation

cgtproxy

checks commit activity contributors release date commits since release codecov go report card

en | zh_CN

Warning

This English README is translated from the Chinese version using LLM and may contain errors.

cgtproxy is a transparent proxy rule manager written in Go, inspired by the cgproxy project.

This project automatically updates nft rules based on configuration files to redirect network traffic from each cgroup to specific targets, enabling dynamic transparent proxy rules at the application level.

Currently supported targets include:

  • DIRECT (direct connection)
  • TPROXY

Usage

  1. Install cgtproxy

  2. Enable and start the systemd service:

    systemctl daemon-reload
    systemctl enable --now cgtproxy.service

    Check the generated nft rules with the default configuration:

    sudo nft list ruleset
  3. Create your own configuration:

    • Write configuration according to the configuration guide

    • Place the configuration file at /etc/cgtproxy/config.yaml

    • Restart the service:

      systemctl restart cgtproxy.service

Tips

You can create a bash function to easily manage processes:

function cgtproxy-exec() {
  local slice="cgtproxy-$1.slice"
  shift 1
  systemd-run --user --slice "$slice" -P "$@"
}

When you use this function like:

# Run without proxy
cgtproxy-exec direct /some/command

# Run with proxy
cgtproxy-exec proxy /some/command

The function uses systemd-run to run commands in the cgtproxy-direct and cgtproxy-proxy slices.

In the example configuration, we:

  • Leave traffic from cgroup cgtproxy-direct.slice unmodified
  • Transparently proxy traffic from cgroup cgtproxy-proxy.slice

How It Works

Netfilter can filter network traffic by cgroup and redirect traffic to TPROXY servers.

The systemd desktop environment documentation suggests that desktop environments and other launchers should start applications in systemd-managed units:

...

To ensure cross-desktop compatibility and encourage sharing of good practices, desktop environments should adhere to the following conventions:

  • Application unit names should be in the form app[-<launcher>]-<ApplicationID>[@<random>].service or app[-<launcher>]-<ApplicationID>-<random>.scope. For example:
    • app-gnome-org.gnome.Evince@12345.service
    • app-flatpak-org.telegram.desktop@12345.service
    • app-KDE-org.kde.okular@12345.service
    • app-org.kde.amarok.service
    • app-org.gnome.Evince-12345.scope

...

While this documentation doesn't directly specify what "Application ID" should be, currently all Linux desktop environments use "desktop file ID" when launching applications.

For example, Telegram from Flatpak launched by a desktop environment will run in a cgroup similar to:

/user.slice/user-1000.slice/user@1000.service/app.slice/app-flatpak-org.telegram.desktop@12345.service

This means each application instance's cgroup path follows a pattern that can be matched with regular expressions.

Based on this, cgtproxy monitors cgroupfs changes through inotify, and uses regex-based configuration files to update nft rules according to configuration when new cgroup hierarchies are created.

Advantages

Common methods for application-level proxy configuration on Linux have limitations:

  1. Environment variables:

    • No simple way to configure at the application level
    • Some applications ignore environment variables
  2. Filtering traffic by binary path (like Clash):

    Clash scans procfs when applications create new sockets to determine which binary actually initiated the connection, then decides how to route it.

    This approach has the following potential issues:

    • Possible performance issues when there are many processes
    • Programs written with scripts (like applications written with pyqt) cannot be correctly identified
    • Cannot correctly identify when applications connect to the network by running command-line tools
  3. Using cgproxy:

    This approach has serious security issues:

    • cgproxy moves processes out of their original cgroups
    • User-level processes can escape to system-level cgroups, bypassing resource limits configured by the system administrator

    Additionally, this approach breaks systemd's single-writer rule.

cgtproxy does not have these problems.

Disadvantages

cgtproxy updates nft rules according to the configuration after receiving a cgroup creation event. The application startup process does not wait for this update, so an application may send network requests before its rules take effect. This window is always present and becomes more noticeable when cgtproxy responds slowly.

To ensure an application follows the configuration from its first network request, the desktop environment or application launcher must cooperate: after creating the target cgroup, it must wait for cgtproxy to confirm that the corresponding nft rules have been successfully committed before allowing the application to run and establish network connections. The project does not currently implement this startup synchronization.

On a ThinkPad L14 Gen 2, the observed race window was approximately 0.3–43 ms.

TCP connections established directly during this window are reset once TPROXY rules take effect. After reconnecting, clients follow the new rules; whether they retry automatically depends on the application.

Differences from cgproxy

  1. cgproxy uses iptables, while cgtproxy uses nftables.

    You can check the differences between iptables and nftables.

  2. cgproxy predefines several [cgroups] and creates routing rules for them, while cgtproxy doesn't create [cgroups] but dynamically updates routing rules when [cgroups] appear.

  3. cgproxy uses eBPF to hook the execve system call, determining executable file paths when all applications execute and moving processes to predefined [cgroups], while cgtproxy keeps processes in their original [cgroups].

  4. cgproxy requires CAP_SYS_ADMIN, CAP_NETWORK_ADMIN, and CAP_BPF, while cgtproxy only requires CAP_NETWORK_ADMIN. For details, see the systemd service file.

Documentation

Project documentation:

Netfilter documentation:

Development Status

  • DNS hijacking for fake-ip:

    • IPv4

    • IPv6

      I don't have any IPv6-only devices, so I don't need and cannot validate this feature.

  • Built-in TPROXY server

    Clash Clash.Meta MetaCubeX/mihomo is good enough for me.

If you need any of the above features, pull requests are welcome.

Releases

Both stable releases and prereleases are driven by PROJECT_VERSION in Makefile. After a version change (for example, 0.3.4 or 0.3.4-rc.1) is committed to master, the .github/workflows/cd.yaml workflow automatically handles the release process.

License

Unless otherwise stated, the code in this project is open source under the GNU General Public License version 3 or any later version, while documentation, configuration files, and scripts used in the development and maintenance process are open source under the MIT License.

This project complies with the REUSE Specification.

You can use reuse-tool to generate an SPDX list for this project:

reuse spdx

Star History Chart

About

A transparent proxy RULE manager written in go inspired by cgproxy.

Topics

Resources

Contributing

Stars

40 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages