Skip to content

lamg/pmproxy

Repository files navigation

 ____  __  __ ____                      
|  _ \|  \/  |  _ \ _ __ _____  ___   _ 
| |_) | |\/| | |_) | '__/ _ \ \/ / | | |
|  __/| |  | |  __/| | | (_) >  <| |_| |
|_|   |_|  |_|_|   |_|  \___/_/\_\\__, |
                                  |___/ 

License Badge Build Status Coverage Status Go Report Card

PMProxy is an HTTP proxy server which uses a predicate for allowing or forbidding requests, and assigning resources to them once they are allowed.

The predicate contains references as identifiers, which point to managers that analyzing:

  • the client IP address
  • the time the request was made
  • and the requested URL

return true or false when the predicate is evaluated with a specific request.

There are also managers that once they are reached by the predicate evaluation always return true, but also set themselves as handlers of the connections made by that client.

Configuration example

The sessionIPM manager only returns true when the client, identified by its IP address, authenticated against it, with valid credentials according a configured database. The dwnConsR manager always returns true, but every downloaded amount by a client (identified by the IP address from which it authenticated) is accumulated until it reaches a quota. Then the connections by that client are denied, until a reset occurs manually or at regular time intervals.

The following configuration will only allow connections from IPs authenticated by session:sessionIPM, and will have 1 GB for downloading each day:

rule = "sessions ∧ down"

[[sessionIPM]]
	name = "sessions"
	auth = "map"

[[dwnConsR]]
	name = "down"
	userDBN = "map"
	resetCycle = "24h"
	[dwnConsR.groupQuota]
		group0 = "1 GB"

[mapDB]
	name = "map"
	[mapDB.userPass]
		user0 = "pass0"
	[mapDB.userGroup]
		user0 = ["group0"]

The previous content must be placed at $HOME/.config/pmproxy/managers.toml.

Also there's a separate file for the servers (proxy and API) configuration, that must be placed at $HOME/.config/pmproxy/server.toml.An example content is:

[api]
	excludedRoutes=["/about"]
	httpsCert="cert.pem"
	httpsKey="key.pem"
	webStaticFilesDir="staticFiles"
	persistInterval="5m"
	[api.server]
		readTimeout="30s"
		writeTimeout="20s"
		addr=":4443"
		fastOrStd=false

[proxy]
	dialTimeout="10s"
	[proxy.server]
		readTimeout="30s"
		writeTimeout="20s"
		addr=":8080"
		fastOrStd=false

With the previous configuration the pmproxy command will start an HTTP proxy server at :8080, and an HTTPS API server at :4443. Then you can use your browser with pmproxy-server-address:8080 as your HTTP proxy, and https://pmproxy-server-address:4443 as argument to pmcl while discovering and querying assigned managers according the predicate. The excludedRoutes field is a list of routes that a web interface, served from the webStaticFilesDir, handles without requesting them to the server.

Client usage example

Running the proxy with the previous configuration at localhost, the command pmcl d https://localhost:4443 will return:

Match result: false
[❌] sessions:sessionIPM

With that, and knowing the credentials configured at the mapDB object in managers.toml, it's possible to log in with pmcl l -m sessions https://localhost:4443 user0 pass0. This will create a file login.secret at the current path with information for pmcl to work properly. Then the command pmcl s will return:

User: user0
Name: user0
Groups: [group0]
Quota: 1 GB Consumption: 0 B

and pmcl d:

Match result: true
[✅] down:DwnConsR
[✅] sessions:sessionIPM

Deployment

For a server with high traffic soon the amount of opened connections will increase up to the default limit for each process, therefore you need to configure a limit more suited for you needs. Using systemd units makes easier running, restarting or stopping the process, with a particular connection amount limit.

As example you can put the following content in /etc/systemd/system/pmproxy.service:

[Unit]
Description=PMProxy Service
After=network.target

[Service]
Type=simple
User=root
LimitNOFILE=49152
WorkingDirectory=/root/.config/pmproxy
ExecStart=/usr/local/bin/pmproxy
Restart=on-abort

[Install]
WantedBy=multi-user.target

which increases the limit number of opened files (LimitNOFILE) up to 49152, when usually it's 1024 (it can be found in /etc/security/limits.conf). Since opened connections count as opened files, this solves the previously mentioned problem.

Also having systemd a configuration allows to see the logs with journalctl -u pmproxy. Otherwise journalctl _PID=X, where X is the pmproxy process ID, will do.