Skip to content

Commit

Permalink
Moving up- and downrate to config;
Browse files Browse the repository at this point in the history
Tests added;
Typo fix in Readme.md
  • Loading branch information
dsevastianov committed Dec 6, 2022
1 parent 097076f commit 9b2e76a
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 35 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ RUN git clone https://github.com/balena-io-modules/connect-disconnect-script-ope

FROM plugin-builder as learn-address-plugin

ENV LEARN_ADDRESS_PLUGIN_COMMIT=9ee777205f54de62413fe5d0267eac426f6aae06
ENV LEARN_ADDRESS_PLUGIN_COMMIT=32c796f930a592a37f3d047dfdc3caffbde61ccd
RUN git clone https://github.com/balena-io-modules/learn-address-script-openvpn.git \
&& cd learn-address-script-openvpn \
&& git checkout ${LEARN_ADDRESS_PLUGIN_COMMIT} \
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Note that the dhcp pool size will also dictate the max clients per
process, with the max clients per server being
`max_clients_per_instance * VPN_INSTANCE_COUNT` and not the size of
the base subnet. A VLSM of `20` will allow for 4,094 clients per instance, and a
base subnet of size `/10` will allow for a total of a total of 4,194,302 clients.
base subnet of size `/10` will allow for a total of 4,194,302 clients.

Base ports are increments by the process instance ID (1-indexed) to calculate
the port for that instance.
Expand Down
6 changes: 5 additions & 1 deletion automation/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ cd "${WORKDIR}"
test_id=$(docker run --privileged -d \
--tmpfs /run \
--tmpfs /sys/fs/cgroup \
--cap-add=NET_ADMIN \
-e PRODUCTION_MODE=false \
-e API_HOST=api.balena.test \
-e VPN_PORT=443 \
Expand All @@ -37,6 +38,8 @@ test_id=$(docker run --privileged -d \
-e VPN_BASE_PORT=10000 \
-e VPN_BASE_MANAGEMENT_PORT=20000 \
-e VPN_API_PORT=30000 \
-e VPN_DOWNRATE=5mbit \
-e VPN_UPRATE=5mbit \
-e VPN_HOST=127.0.0.1 \
-e VPN_CONNECT_INSTANCE_COUNT=1 \
-e VPN_CONNECT_PROXY_PORT=3128 \
Expand All @@ -63,4 +66,5 @@ docker exec "${test_id}" /bin/sh -ec '
echo "127.0.0.1 deadbeef.vpn" >> /etc/hosts
npm install
npm run test-unit
npx mocha test/app.ts'
npx mocha test/app.ts
cat /tmp/learn-address/status.log'
3 changes: 3 additions & 0 deletions config/confd/templates/env.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ VPN_HAPROXY_SOCKET={{getenv "VPN_HAPROXY_SOCKET" "/run/haproxy.sock"}}

{{if getenv "TRUST_PROXY"}}TRUST_PROXY={{getenv "TRUST_PROXY"}}{{end}}
{{if getenv "VPN_AUTH_CACHE_TIMEOUT"}}VPN_AUTH_CACHE_TIMEOUT={{getenv "VPN_AUTH_CACHE_TIMEOUT"}}{{end}}

VPN_DOWNRATE=5mbit
VPN_UPRATE=5mbit
87 changes: 72 additions & 15 deletions openvpn/scripts/learn-address.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,62 @@

#shamelessly stolen from https://serverfault.com/questions/701194/limit-throttle-per-user-openvpn-bandwidth-using-tc

statedir=/tmp/
#$1 = downrate # from VPN server to the client, e.g. 5mbit
#$2 = uprate # from client to the VPN server, e.g. 5mbit
#$3 = action (add, update, delete)
#$4 = IP or MAC
#$5 = client_common name #Not used for rate limiting

#set -eu

DEBUG=0
statedir=/tmp/learn-address/
mkdir -p $statedir

if [[ $DEBUG -eq 1 ]]; then
log=$statedir/status.log
touch $log
echo "****************" &>> $log
echo "Starting $0: $# [$@]" &>> $log
fi

# downrate: from VPN server to the client
downrate=$1
# uprate: from client to the VPN server
downrate=5mbit
uprate=5mbit
uprate=$2

function trace() {
if [[ $DEBUG -eq 1 ]]; then
place=$1
dev=$2
echo "*** $place" &>> $log
if [[ $dev ]]; then
echo "tc -s qdisc show dev $dev" &>> $log
tc -s qdisc show dev $dev &>> $log
echo "tc -s class show dev $dev" &>> $log
c -s class show dev $detv &>> $log
echo "tc -s filter show dev $dev" &>> $log
tc -s class show dev $dev &>> $log
else
echo "No dev!" &>> $log
fi
echo "***" &>> $log
fi
}

function pre() {
trace "PRE" $1
}

function post() {
trace "POST" $1
}

function bwlimit-enable() {
ip=$1

pre $dev

# Disable if already enabled.
bwlimit-disable $ip

Expand All @@ -29,15 +76,20 @@ function bwlimit-enable() {
fi

# Limit traffic from VPN server to client
tc class add dev $dev parent 1: classid 1:$classid htb rate $downrate
tc filter add dev $dev protocol all parent 1:0 prio 1 u32 match ip dst $ip/32 flowid 1:$classid
echo "tc class add dev $dev parent 1: classid 1:$classid htb rate $downrate" &>> $log
tc class add dev $dev parent 1: classid 1:$classid htb rate $downrate &>> $log
echo "tc filter add dev $dev protocol all parent 1:0 prio 1 u32 match ip dst $ip/32 flowid 1:$classid" &>> $log
tc filter add dev $dev protocol all parent 1:0 prio 1 u32 match ip dst $ip/32 flowid 1:$classid &>> $log

# Limit traffic from client to VPN server
tc filter add dev $dev parent ffff: protocol all prio 1 u32 match ip src $ip/32 police rate $uprate burst 80k drop flowid :$classid

tc filter add dev $dev parent ffff: protocol all prio 1 u32 match ip src $ip/32 police rate $uprate burst 80k drop flowid :$classid &>> $log
echo "tc filter add dev $dev parent ffff: protocol all prio 1 u32 match ip src $ip/32 police rate $uprate burst 80k drop flowid :$classid" &>> $log

# Store classid and dev for further use.
echo $classid > $statedir/$ip.classid
echo $dev > $statedir/$ip.dev

post $dev
}

function bwlimit-disable() {
Expand All @@ -53,30 +105,35 @@ function bwlimit-disable() {
classid=`cat $statedir/$ip.classid`
dev=`cat $statedir/$ip.dev`

pre $dev

tc filter del dev $dev protocol all parent 1:0 prio 1 u32 match ip dst $ip/32
tc class del dev $dev classid 1:$classid

tc filter del dev $dev parent ffff: protocol all prio 1 u32 match ip src $ip/32

# Remove .dev but keep .classid so it can be reused.
rm $statedir/$ip.dev

post $dev
}

# Make sure queueing discipline is enabled.
tc qdisc add dev $dev root handle 1: htb 2>/dev/null || /bin/true
tc qdisc add dev $dev handle ffff: ingress 2>/dev/null || /bin/true
if [[ $dev ]]; then
# Make sure queueing discipline is enabled.
tc qdisc add dev $dev root handle 1: htb 2>/dev/null || /bin/true
tc qdisc add dev $dev handle ffff: ingress 2>/dev/null || /bin/true
fi

case "$1" in
case "$3" in
add|update)
bwlimit-enable $2
bwlimit-enable $4
;;
delete)
bwlimit-disable $2
bwlimit-disable $4
;;
*)
echo "$0: unknown operation [$1]" >&2
echo "$0: unknown operation [$3]" >&2
exit 1
;;
esac

exit 0
2 changes: 2 additions & 0 deletions src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ const getIPv4InterfaceInfo = (iface?: string): os.NetworkInterfaceInfo[] => {
export const TRUST_PROXY = trustProxyVar('TRUST_PROXY', false);

export const VPN_API_PORT = intVar('VPN_API_PORT');
export const VPN_DOWNRATE = optionalVar('VPN_DOWNRATE');
export const VPN_UPRATE = optionalVar('VPN_UPRATE');

// milliseconds
export const DEFAULT_SIGTERM_TIMEOUT =
Expand Down
4 changes: 3 additions & 1 deletion src/utils/openvpn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const Telnet =
// tslint:disable-next-line:no-var-requires
require('telnet-client') as typeof import('telnet-client').default;

import { VPN_API_PORT } from './config';
import { VPN_API_PORT, VPN_DOWNRATE, VPN_UPRATE } from './config';
import { Netmask } from './netmask';

export interface VpnClientUntrustedData {
Expand Down Expand Up @@ -193,6 +193,8 @@ export class VpnManager extends EventEmitter {
'--plugin',
'/etc/openvpn/plugins/openvpn-plugin-learn-address-script.so',
'/etc/openvpn/scripts/learn-address.sh',
`${VPN_DOWNRATE}`,
`${VPN_UPRATE}`,
'--plugin',
'/etc/openvpn/plugins/openvpn-plugin-auth-script.so',
'/etc/openvpn/scripts/auth',
Expand Down
50 changes: 34 additions & 16 deletions test/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,28 +115,46 @@ describe('VPN Events', function () {
.reply(200, 'OK');
});

it('should send a client-connect event', function () {
const connectEvent = getEvent('connect').then((body) => {
expect(body).to.have.property('serviceId').that.equals(instance.getId());
expect(body).to.have.property('uuids').that.deep.equals(['user2']);
expect(body).to.not.have.property('real_address');
expect(body).to.not.have.property('virtual_address');
});
async function verifyEvent(event: string) {
const body = await getEvent(event);
expect(body).to.have.property('serviceId').that.equals(instance.getId());
expect(body).to.have.property('uuids').that.deep.equals(['user2']);
expect(body).to.not.have.property('real_address');
expect(body).to.not.have.property('virtual_address');
}

it('should send a client-connect event', function () {
this.client = vpnClient.create(vpnDefaultOpts);
this.client.authenticate('user2', 'pass');
return this.client.connect().return(connectEvent);
return this.client.connect().return(verifyEvent('connect'));
});

it('should send a client-disconnect event', function () {
const disconnectEvent = getEvent('disconnect').then((body) => {
expect(body).to.have.property('serviceId').that.equals(instance.getId());
expect(body).to.have.property('uuids').that.deep.equals(['user2']);
expect(body).to.not.have.property('real_address');
expect(body).to.not.have.property('virtual_address');
});
return this.client.disconnect().return(verifyEvent('disconnect'));
});
});

describe('More than one client', function () {
this.timeout(30 * 1000);

return this.client.disconnect().return(disconnectEvent);
before(() => {
nock(BALENA_API_INTERNAL_HOST)
.get(/\/services\/vpn\/auth\/user[23]/)
.twice()
.reply(200, 'OK');
});
it('should connect two clients', async function () {
this.client = vpnClient.create(vpnDefaultOpts);
this.client.authenticate('user2', 'pass');

this.anotherClient = vpnClient.create(vpnDefaultOpts);
this.anotherClient.authenticate('user3', 'pass');
await this.client.connect();
return this.anotherClient.connect();
});
it('should disconnect two clients', async function () {
await this.client.disconnect();
return this.anotherClient.disconnect();
});
});

Expand Down Expand Up @@ -377,4 +395,4 @@ describe('VPN proxy', function () {
});
});
});
});
});

0 comments on commit 9b2e76a

Please sign in to comment.