The old bbs
can be found here
bbs
is a router for SOCKS and HTTP proxies. It exposes SOCKS5, HTTP
CONNECT or port forwarding services and forwards incoming requests to proxies or chains of proxies
based on the request's target. Routing can be configured with a PAC script (if
built with PAC support), or through a JSON file.
go install github.com/synacktiv/bbs@master
To install bbs with PAC script support:
go install -tags pac github.com/synacktiv/bbs@master
Note: PAC relies on unaudited third-party libraries.
The bbscli.py
python CLI is provided to ease configuration of bbs and avoid writing JSON files manually.
It requires pyparsing
library, which is packaged by Debian:
apt install python3-pyparsing
If the library is not packaged by your distribution, it can be installed using pip:
pip install pyparsing
Configuration is performed in one JSON file composed of multiple sections:
- Proxies: defines all the upstream proxies used by bbs
- Chains: defines the differents chains of previously defined proxies, and their settings
- Routes: defines the different routing tables
- Servers: defines the listeners (SOCKS5, HTTP or port forwarding) opened by bbs
- Hosts: defines custom hosts resolution (in a /etc/hosts way)
The configuration file path is provided through argument -c <path>
(default to ./bbs.json
).
bbs
reloads configuration files on SIGHUP, use kill -HUP <pid>
to reload.
Here is an example of such configuration:
{
"proxies": {
"proxy1": {
"connstring": "socks5://127.0.0.1:1337",
"user": "user",
"pass": "s3cr3t"
},
"proxy2": {
"connstring": "http://127.0.0.1:1338"
}
},
"chains": {
"chain1": {
"proxyDns": true,
"tcpConnectTimeout": 1000,
"tcpReadTimeout": 2000,
"proxies": [
"proxy1",
"proxy2"
]
},
"direct": {
"proxies": []
}
},
"routes": {
"table1": {
"default": "direct",
"blocks": [
{
"comment": "Block1 comment",
"rules": {
"rule": "regexp",
"variable": "host",
"content": "me\\.gandi\\.net"
},
"route": "chain1"
},
{
"comment": "Route non web traffic towards 10.35.0.0/16 through proxy2",
"rules": {
"rule1": {
"rule": "subnet",
"content": "10.35.0.0/16"
},
"op": "AND",
"rule2": {
"rule": "regexp",
"variable": "port",
"content": "^(80|443)$",
"negate": "true"
}
},
"route": "proxy2"
},
{
"comment": "Drop traffic to 445",
"rules": {
"rule": "regexp",
"variable": "port",
"content": "^445$"
},
"route": "drop"
},
{
"comment": "Route *.corp.local through chain1",
"rules": {
"rule": "regexp",
"variable": "host",
"content": "(?i)^(.*\\.)?corp\\.local$"
},
"route": "chain1",
"disable": "true"
}
]
},
"table2": {
"default": "drop",
"blocks": [
{
"comment": "Route *.corp.local through chain2",
"rules": {
"rule": "regexp",
"variable": "host",
"content": "(?i)^(.*\\.)?corp\\.local$"
},
"route": "chain2"
}
]
}
},
"servers": [
"socks5://127.0.0.1:1081:table1",
"http://127.0.0.1:1080:table2",
"fwd://127.0.0.1:4445:chain1:10.0.0.1:445"
],
"hosts": {
"host1": "1.1.1.1",
"host2": "10.0.0.1",
"host3": "modified.host3",
"10.1.1.4": "10.1.1.5"
}
}
Upstream proxies must be declared in the proxies
section as a map of proxy
structures. Map keys are chosen freely but must match the ones used in chains
definition. Proxy structures are like this:
connstring
is required with formatprotocol://host:port
(protocol
can besocks5
orhttpconnect
/http
)user
andpass
are optional
For each proxy declared, an implicit chain (see next paragraph) is created with the same name. It has default parameters and is composed of the single associated proxy. If you want to use non-default parameters, you must explicitely create a chain.
Chains must be declared in the chains
section as a map of chain structures.
Map keys are chosen freely by must match with the ones used in routes definition, and
must be different than the proxies
section map keys.
Chain structures have proxychains-like parameters (cf. https://github.com/rofl0r/proxychains-ng):
proxyDns
: boolean, optional, defaults totrue
tcpConnectTimeout
: integer, optional, defaults to 1000 (used when connecting sockets, either to the first proxy of the chain or directly to the target)tcpReadTimeout
: integer, optional, defaults to 2000 (used when reading proxy handshakes responses on connected sockets)proxies
: string list, optional, defaults to empty list
The proxies
key of a chain
must contain an array of proxy names declared as keys in the proxies
section.
As mentionned in the previous paragraph, for each proxy declared in proxies
section, an implicit
chain (see next paragraph) is created with the same name. It has defaults parameters and is
composed of the single associated proxy.
The built-in configuration mode for routing is through the configuration file. It associates
addresses with chain names. The file must contain a map of routing tables. Map keys
are chosen freely but must match the ones used in the servers
section.
Each routing table contains a default
key representing the default route and a blocks
key
which is an array of rule blocks. Each
rule block contains a comment
, a set of rules
, and an associated chain
name. Rules are evaluated: given an address in the host:port
format, they can
be true
or false
. For a given address, blocks are evaluated in their
declaration order. Blocks can be disabled by setting the disable
field to true
.
This allows for a form of "commenting", which is not possible in JSON.
The evaluation stops at the first block that is true
and
the associated chain name is returned. Each opened server (from servers
section)
is associated with one routing table from the configuration. Requests received on
each server are routed according to the matching routing table. If all blocks evaluate to
false
the default route is used. If default
is not defined, connections are dropped by default.
Block fields:
comment
(string)rules
(Rule or RuleCombo)route
(string)disable
(bool)
Rule fields:
rule
(string): rule type,regexp
,subnet
.variable
(string): variable for regexp evaluation,host
,port
oraddr
(host:port).content
(string): content of the rule, depends on the rule type (see below).negate
(bool) [optional]: whether to negate the rule.
RuleCombo fields:
rule1
(Rule or RuleCombo): left operand.op
(string): operator,AND
,And
,and
,&
,&&
,OR
,Or
,or
,|
,||
.rule2
(Rule or RuleCombo): right operand.
Rule types:
regexp
: match the variable defined invariable
(host
,port
oraddr=host:port
) against the regexp incontent
.subnet
: checks if host is in the subnet defined incontent
. If host is a domain name and not a subnet address, the rule returns false.
The rule blocks from routes
section or the PAC function must return declared
chain names, not proxy names. If you want to use a single proxy, you must wrap
it in a chain. The drop
name is special and does not need to be declared in
this configuration. If the PAC function or a routing block returns drop
as a
chain name, then the connection is dropped.
If bbs is built with PAC support and -pac
arguments points to a PAC file, routes
defined in the configuration file will not be used. PAC file routing does not support
multiple routing tables. The same PAC file will be used for every opened server.
The listeners opened by bbs must be declared in the servers
section as a list of
connection strings of format protocol://bind_addr:bind_port:routing_table
or
protocol://bind_addr:bind_port:chain:dest_addr:dest_port
.
protocol
can behttp
orsocks5
if therouting_table
is providedprotocol
can befwd
ifchain
,dest_addr
anddest_port
are providedrouting_table
must match one of the tables defined inroutes
sectionchain
must match one of the chains defined inchains
section
Custom host resolution (similar to /etc/hosts
) can be configured in the
hosts
section as a map of strings. Map keys correspond to the hostname
and the values to the IP address the host should resolve to.
It should be noted that map keys may also be IP addresses. In this case the key IP address will be replaced by the value IP address. Similarly, map values may be hostnames and will replace the corresponding map key.
If defined, custom host resolutions occur at the beginning of the connection phase, after the routing decision is made and before any local DNS resolution (if the chain is configured with proxyDns=false) and before sending the destination address to the various proxies of the chain.
If bbs
is built with PAC support, routing can be configured with a PAC script
instead of a JSON configuration file. However, this requires using an untrusted
Go library. The PAC file path must be provided with -pac
.
The PAC script must define the FindProxyForURL(url, host)
function. The
values returned by this function must match the names of the chains (not the
proxies) declared in the JSON configuration.