1
0
mirror of https://github.com/cmur2/openvpn-status-web.git synced 2025-09-29 07:52:07 +02:00

115 Commits

Author SHA1 Message Date
cn
ea7b3a947c gem: release 3.2.1 2022-01-18 23:18:00 +01:00
cn
eb2bb4d632 gem: initialize new status attributes with default values
- prevents 'undefined method each_with_index for nil:NilClass' errors
2022-01-18 23:17:42 +01:00
cn
2a9bacf196 gem: release 3.2.0 2022-01-18 17:09:03 +01:00
cn
39adc74fbc gem: Date::Error is not in Ruby 2.6 and older, use ArgumentError 2022-01-18 17:07:33 +01:00
cn
9ad59194bd gem: support date format used by OpenVPN 2.5
- smooth fallback
2022-01-18 17:07:33 +01:00
cn
528709c895 gem: parse any number of list headers in status-version 2/3 2022-01-18 17:07:33 +01:00
cn
39d2b3ce94 gem: refactor to prepare supporting flexible list headers 2022-01-18 17:07:33 +01:00
406ed867f7 gem: add Ruby 3.1 support 2022-01-07 09:31:53 +01:00
depfu[bot]
22d9797f55 gems: update rubocop-rspec to version 2.7.0 2021-12-28 00:29:52 +01:00
depfu[bot]
55aecedef7 gems: update rubocop to version 1.24.0 2021-12-24 16:21:27 +01:00
depfu[bot]
d401074524 gems: update rubocop to version 1.23.0 2021-11-17 13:53:41 +01:00
depfu[bot]
ff6572bdac gems: update rubocop-rspec to version 2.6.0 2021-11-09 09:24:58 +01:00
depfu[bot]
b4bf74240b gems: update rubocop to version 1.22.0 2021-09-30 12:09:33 +02:00
depfu[bot]
fb4b3ca461 gems: update solargraph to version 0.44.0 2021-09-28 11:42:56 +02:00
depfu[bot]
8a6722f981 gems: update rubocop-rspec to version 2.5.0 2021-09-23 08:35:49 +02:00
depfu[bot]
8249b01c95 gems: update rubocop to version 1.21.0 2021-09-14 22:17:23 +02:00
depfu[bot]
5d04255a95 gems: update bundler-audit to version 0.9.0.1 2021-09-02 00:53:22 +02:00
depfu[bot]
846d41d35d gems: update rubocop to version 1.20.0 2021-08-27 12:50:41 +02:00
depfu[bot]
ce0e80f35f gems: update rubocop to version 1.19.0 2021-08-13 15:43:58 +02:00
depfu[bot]
0ec3faf5b4 gems: update solargraph to version 0.43.0 2021-07-27 00:54:44 +02:00
depfu[bot]
10a4e757f3 gems: update rubocop to version 1.18.1 2021-07-01 09:58:43 +02:00
depfu[bot]
f95134c315 gems: update rubocop-rake to version 0.6.0 2021-06-29 22:24:25 +02:00
depfu[bot]
7da4cb910f gems: update rubocop to version 1.17.0 2021-06-16 16:13:00 +02:00
depfu[bot]
382daa34c6 gems: update solargraph to version 0.42.1 2021-06-12 15:25:00 +02:00
depfu[bot]
5338b30e32 gems: update rubocop-rspec to version 2.4.0 2021-06-10 23:59:30 +02:00
depfu[bot]
47ee407af2 gems: update solargraph to version 0.41.1 2021-06-02 19:59:48 +02:00
depfu[bot]
281750d6ea gems: update rubocop to version 1.16.0 2021-06-02 19:55:34 +02:00
depfu[bot]
42743a1786 gems: update rubocop to version 1.15.0 2021-05-18 12:05:21 +02:00
depfu[bot]
7c94192765 gems: update rubocop to version 1.14.0 2021-05-06 12:44:25 +02:00
depfu[bot]
a3a65f05bc gems: update rubocop-rspec to version 2.3.0 2021-04-29 15:09:21 +02:00
depfu[bot]
e87a4d86f6 gems: update rubocop to version 1.13.0 2021-04-21 12:29:53 +02:00
depfu[bot]
2145e183e3 gems: update rubocop to version 1.12.0 2021-03-25 17:22:09 +01:00
depfu[bot]
9e5aa1ff71 gems: update bundler-audit to version 0.8.0 2021-03-11 17:18:44 +01:00
depfu[bot]
a99ba901c9 gems: update rubocop to version 1.11.0 2021-03-02 12:31:48 +01:00
depfu[bot]
cc21291a5c gems: update rubocop to version 1.10.0 2021-02-16 16:59:41 +01:00
depfu[bot]
ba00407d55 gems: update rubocop-rspec to version 2.2.0 2021-02-04 10:11:43 +01:00
depfu[bot]
80bd4f8584 gems: update rubocop to version 1.9.0 2021-01-29 14:29:43 +01:00
cn
e4034073a6 release: 3.1.0
- Ruby 3.0
- move from Travis CI to Github Actions
- dependency updates
2021-01-18 20:56:53 +01:00
depfu[bot]
f4d17809a2 gems: update rubocop to version 1.8.1 2021-01-18 20:51:39 +01:00
cn
08b50d5974 docs: update Dockerfile to Alpine 3.13 2021-01-18 15:00:41 +01:00
depfu[bot]
f287906240 gems: update rubocop to version 1.7.0 2020-12-28 21:42:19 +01:00
cn
27306b831f ci: use bundler cache to save resources
- see https://github.com/ruby/setup-ruby#caching-bundle-install-automatically
2020-12-26 15:17:06 +01:00
cn
1e12701fc3 gems: include rubocop-rspec and fix linting 2020-12-25 18:17:01 +01:00
cn
597b619b0b gems: include rubocop-rake and fix linting 2020-12-25 18:16:27 +01:00
cn
61f7cbd76c gem: add Ruby 3.0 support 2020-12-25 17:52:10 +01:00
cn
2628c08975 gems: revert back to upstream solargraph now with rubocop 1.0 compat
See https://github.com/castwide/solargraph/issues/380 and previous commit 0ffe369ef1
2020-12-25 17:51:25 +01:00
depfu[bot]
7343081406 gems: update rubocop to version 1.6.1 2020-12-11 12:15:59 +01:00
cn
ec4d028c03 ci: drop Travis CI
- end of an era
2020-12-10 11:03:44 +01:00
cn
12b5153604 gem: refactor Rakefile solargraph tasks, include in default task 2020-12-04 09:17:11 +01:00
cn
7c0722e875 ci: use Github Actions in parallel to Travis CI
- this adds a new workflow for Github Actions that mirrors what the existing Travis CI workflow tests
- Travis CI might become unfriendly to opensource soonish so migration might be necessary
2020-12-04 09:14:32 +01:00
depfu[bot]
afd7f4fa98 gems: update rubocop to version 1.5.1 2020-12-03 21:23:22 +01:00
depfu[bot]
63272da3e3 gems: update rubocop to version 1.4.1 2020-11-24 20:56:04 +01:00
depfu[bot]
ae7fc1137b gems: update rubocop to version 1.3.0 2020-11-13 13:09:57 +01:00
depfu[bot]
1f69dabf5f gems: update rubocop to version 1.2.0 2020-11-06 12:28:33 +01:00
cn
0e9c9fea68 docs: use travis-ci.com links 2020-10-31 00:05:28 +01:00
cn
0ffe369ef1 gems: use solargraph fork until gem metadata allows rubocop 1.0 compat
See https://github.com/castwide/solargraph/issues/380
2020-10-30 23:59:05 +01:00
depfu[bot]
c2cac319ae gems: update rubocop to version 1.1.0 2020-10-30 22:56:37 +01:00
depfu[bot]
5f343d7873 gems: update rubocop to version 1.0.0 2020-10-22 15:56:12 +02:00
depfu[bot]
0a45726857 gems: update rubocop to version 0.93.0 2020-10-10 16:46:18 +02:00
cn
109b27fecc release: 3.0.1
- use webrick gem which contains fixes against [CVE-2020-25613](https://www.ruby-lang.org/en/news/2020/09/29/http-request-smuggling-cve-2020-25613/)
2020-10-03 11:04:48 +02:00
cn
b44517ec21 gems: update webrick to version 1.6.1
- explicitly use webrick gem version with patch against CVE-2020-25613
- https://www.ruby-lang.org/en/news/2020/09/29/http-request-smuggling-cve-2020-25613/
2020-10-03 11:03:01 +02:00
depfu[bot]
520da15739 gems: update rubocop to version 0.92.0 2020-09-26 12:40:07 +02:00
depfu[bot]
9b3a13abef gems: update rubocop to version 0.91.0 2020-09-16 09:39:25 +02:00
depfu[bot]
b81d61b8d7 gems: update rubocop to version 0.90.0 (#16)
Update rubocop to version 0.90.0 (#16)

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
2020-09-02 10:35:06 +02:00
cn
147a905bfc gem: fix solargraph typecheck problem in CI + remove downpin 2020-08-14 17:39:02 +02:00
cn
eeed0fa089 gems: downpin solargraph
- releases newer than 0.39.12 fail on Travis CI although they work locally with RVM Ruby 2.7.1
2020-08-14 14:57:08 +02:00
cn
586ae8ae0f gem: add editorconfig 2020-08-14 14:07:47 +02:00
depfu[bot]
075ec1e548 gems: update rubocop to version 0.89.0
Update rubocop to version 0.89.0 (#15)
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
2020-08-07 08:32:29 +02:00
cn
64ec5e1e56 release: 3.0.0
- drop Ruby 2.3 and 2.4 compatibility
- add Dockerfile
2020-07-14 20:08:06 +02:00
depfu[bot]
7597c17e93 gem: upgrade rubocop to version 0.88.0
Update rubocop to version 0.88.0 (#13)

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Co-authored-by: Christian Nicolai <cn@mycrobase.de>
2020-07-14 20:06:06 +02:00
ac647a4f21 gem: bump minimum required Ruby version to Ruby 2.5 (#14) 2020-07-14 19:57:25 +02:00
cn
78f45f5080 docs: add example Dockerfile 2020-07-13 10:39:30 +02:00
9aeb1a9ad5 docs: fix typos 2020-07-13 10:33:54 +02:00
88c22a6504 docs: fix Travis CI build badge 2020-07-10 23:09:34 +02:00
depfu[bot]
289535d753 gems: update bundler-audit to version 0.7.0.1
(#9)
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
2020-06-14 12:42:05 +02:00
depfu[bot]
8d42bdafc1 gems: update rubocop to version 0.81.0
(#3)
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
2020-04-02 13:51:52 +02:00
cn
f8666fdfc2 release: 2.1.0 2020-03-07 01:32:22 +01:00
cn
41f5d4ed62 gem: adopt frozen string literals 2020-03-07 01:30:57 +01:00
cn
92ee01e5de gem: port fixes from Sorbet from dyndnsd 2020-03-07 01:29:12 +01:00
cn
bf3ba8f7cd gem: refactor gemspec, exclude tests from gem, move binaries to ./exe
- based on recommendations of https://piotrmurach.com/articles/writing-a-ruby-gem-specification/
2020-03-07 01:28:17 +01:00
cn
140c60c753 gem: add rubocop and fix style 2020-03-02 01:57:58 +01:00
cn
d959e7fe62 gem: fix gemspec using rubocop hints
- especially the wrong homepage URL
2020-03-01 22:13:23 +01:00
cn
4cde78fe96 gem: add solargraph support 2020-03-01 22:12:38 +01:00
23dd4aa63f docs: add depfu to README 2020-02-28 21:40:09 +01:00
1547db7bc6 travis: fix badge image URL 2020-02-28 13:31:36 +01:00
b1aa38059f travis: add Ruby 2.7 2020-02-28 13:30:49 +01:00
17054794da travis: fix build config validation problems
- https://docs.travis-ci.com/user/reference/overview/#deprecated-virtualization-environments
2020-02-28 13:25:02 +01:00
cn
ab81c8975e gem: use bundler-audit 2019-12-18 20:20:14 +01:00
cn
1804693c15 travis: add Ruby 2.6 2019-01-04 18:32:32 +01:00
cn
cdc20e8042 Bump version 2018-11-05 09:12:20 +01:00
cn
998f9e683c spec: use new rspec expect syntax 2018-11-02 10:16:28 +01:00
cn
cf69d6417d gem: upgrade to Rack 2.0, loosen version constraints by dropping old rubies 2018-11-02 10:16:18 +01:00
cn
cb1d029326 travis: update rubies 2018-11-02 10:11:26 +01:00
cn
563fb5743b Bump version 2013-10-08 13:23:22 +02:00
cn
a852aa4b4d React to SIGTERM 2013-10-08 13:23:02 +02:00
cn
d5f8d66422 Bump version 2013-10-07 22:27:18 +02:00
cn
f873d8176e Update README 2013-07-19 09:32:37 +02:00
cn
c14d59e0bf Require better_errors only in development 2013-07-18 23:39:59 +02:00
cn
a78a178150 Bump version 2013-05-03 22:26:53 +02:00
cn
e0c3073d82 Allow dropping privs 2013-05-03 22:26:07 +02:00
cn
c885e875ad Use date formatting in UI 2013-05-03 22:23:56 +02:00
cn
f2794ccea4 Add tests and bug fixed parser 2013-05-03 22:23:45 +02:00
cn
457aec64db Add some parser tests 2013-05-03 21:38:10 +02:00
cn
468e002162 Update README 2013-05-03 21:27:13 +02:00
cn
35b5be15a4 Don't use 1.8.7 2013-05-03 21:17:27 +02:00
cn
c51968618d Show multiple vpns 2013-05-03 21:12:06 +02:00
cn
a1a6b33902 Refactor V2 and V3 parser into one 2013-05-03 20:16:17 +02:00
cn
33013c56f3 Add support for multiple status-versions 2013-05-03 20:09:46 +02:00
cn
ed42fc9f30 Use better_errors 2013-05-03 19:49:09 +02:00
cn
635e562a3d First step towards multiple status files 2013-05-03 18:47:32 +02:00
cn
438931f8a6 Use a status object 2013-05-03 18:30:34 +02:00
cn
cd6e41fcfa Refactoring 2013-05-03 18:20:07 +02:00
cn
87d9ea302f Update README 2013-05-03 16:12:11 +02:00
cn
d6a7b8c0ee Use config file 2013-05-03 16:06:30 +02:00
cn
addc1cf45a Update desc 2013-05-03 15:39:50 +02:00
29 changed files with 867 additions and 189 deletions

9
.editorconfig Normal file
View File

@@ -0,0 +1,9 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

33
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
---
name: ci
on:
push:
branches: [master]
pull_request:
branches: [master]
workflow_dispatch:
schedule:
- cron: '35 4 * * 4' # weekly on thursday morning
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
ruby-version:
- '2.5'
- '2.6'
- '2.7'
- '3.0'
- '3.1'
steps:
- uses: actions/checkout@v2
- name: Set up Ruby ${{ matrix.ruby-version }}
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Lint and Test
run: |
bundle exec rake ci

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.DS_Store .DS_Store
*.lock *.lock
pkg/* pkg/*
.yardoc

105
.rubocop.yml Normal file
View File

@@ -0,0 +1,105 @@
require:
- rubocop-rake
- rubocop-rspec
AllCops:
TargetRubyVersion: '2.5'
NewCops: enable
Gemspec/RequireMFA:
Enabled: false
Layout/EmptyLineAfterGuardClause:
Enabled: false
# allows nicer usage of private_class_method
Layout/EmptyLinesAroundArguments:
Enabled: false
Layout/HashAlignment:
Enabled: false
Layout/LeadingEmptyLines:
Enabled: false
Layout/LineLength:
Max: 200
Layout/SpaceInsideHashLiteralBraces:
Enabled: false
Metrics/AbcSize:
Enabled: false
Metrics/BlockLength:
Enabled: false
Metrics/ClassLength:
Enabled: false
Metrics/CyclomaticComplexity:
Enabled: false
Metrics/MethodLength:
Enabled: false
Metrics/PerceivedComplexity:
Enabled: false
Naming/MethodParameterName:
Enabled: false
Naming/MemoizedInstanceVariableName:
Enabled: false
Style/AccessorGrouping:
Enabled: false
Style/ConditionalAssignment:
Enabled: false
Style/Documentation:
Enabled: false
Style/FormatStringToken:
Enabled: false
Style/GuardClause:
Enabled: false
Style/HashEachMethods:
Enabled: true
Style/HashTransformKeys:
Enabled: true
Style/HashTransformValues:
Enabled: true
Style/IdenticalConditionalBranches:
Enabled: false
Style/InverseMethods:
Enabled: false
Style/NegatedIf:
Enabled: false
Style/RescueModifier:
Enabled: false
Style/Semicolon:
AllowAsExpressionSeparator: true
Style/SymbolArray:
Enabled: false
RSpec/ExampleLength:
Max: 10
RSpec/FilePath:
CustomTransform:
OpenVPNStatusWeb: openvpn-status-web
RSpec/MultipleExpectations:
Max: 5

16
.solargraph.yml Normal file
View File

@@ -0,0 +1,16 @@
---
include:
- "**/*.rb"
- "bin/openvpn-status-web"
exclude:
- spec/**/*
- test/**/*
- vendor/**/*
- ".bundle/**/*"
require: []
domains: []
reporters:
- rubocop
- require_not_found
require_paths: []
max_files: 5000

View File

@@ -1,9 +0,0 @@
language: ruby
rvm:
- 2.0.0
- 1.9.3
- 1.8.7
gemfile:
- Gemfile

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
source 'https://rubygems.org' source 'https://rubygems.org'
gemspec gemspec

View File

@@ -1,17 +1,68 @@
# openvpn-status-web # openvpn-status-web
Small (another word for naive in this case, it's simple and serves my needs) [rack](http://rack.github.com/) app ![ci](https://github.com/cmur2/openvpn-status-web/workflows/ci/badge.svg) [![Depfu](https://badges.depfu.com/badges/c264e2f70f2a19c43f880ddcb4a12ba8/overview.svg)](https://depfu.com/github/cmur2/openvpn-status-web?project_id=6194)
providing the information the [OpenVPN](http://openvpn.net/index.php/open-source.html) server collects in it's status file
especially including a list of currently connected clients (common name, remote address, traffic, ...). ## Description
It comes with a Debian 6 compatible init.d file.
Small (another word for naive in this case, it's simple and serves my needs) [Rack](http://rack.github.com/) application providing the information an [OpenVPN](http://openvpn.net/index.php/open-source.html) server collects in it's status file especially including a list of currently connected clients (common name, remote address, traffic, ...).
It lacks: It lacks:
* authentication * caching (parses file on each request, page does auto-refresh every minute as OpenVPN updates the status file these often by default)
* caching (parses file on each request, page does auto-refresh every minute as OpenVPN updates the status file these often)
* management interface support * management interface support
* *possibly more...* * *possibly more...*
## Usage
Install the gem:
gem install openvpn-status-web
Create a configuration file in YAML format somewhere:
```yaml
# listen address and port
host: "0.0.0.0"
port: "8080"
# optional: drop priviliges in case you want to but you should give this user at least read access on the log files
user: "nobody"
group: "nogroup"
# logfile is optional, logs to STDOUT else
logfile: "openvpn-status-web.log"
# hash with each VPNs display name for humans as key and further config as value
vpns:
My Small VPN:
# the status file path and status file format version are required
version: 1
status_file: "/var/log/openvpn-status.log"
My Other VPN:
version: 3
status_file: "/var/log/other-openvpn-status.log"
```
Your OpenVPN configuration should contain something like this:
```
# ...snip...
status /var/log/openvpn-status.log
status-version 1
# ...snip...
```
For more information about OpenVPN status file and version, see their [man page](https://community.openvpn.net/openvpn/wiki/Openvpn23ManPage). openvpn-status-web is able to parse all versions from 1 to 3.
## Advanced topics
### Authentication
If the information exposed is important to you serve it via the VPN or use a webserver as a proxy to handle SSL and/or HTTP authentication.
### Startup
There is a [Dockerfile](docs/Dockerfile) that can be used to build a Docker image for running openvpn-status-web.
The [Debian 6 init script](docs/debian-init-openvpn-status-web) assumes that openvpn-status-web is installed into the system ruby (no RVM support) and the config.yaml is at `/opt/openvpn-status-web/config.yaml`. Modify to your needs.
## License ## License
openvpn-statsu-web is licensed under the Apache License, Version 2.0. See LICENSE for more information. openvpn-status-web is licensed under the Apache License, Version 2.0. See LICENSE for more information.

View File

@@ -1,6 +1,27 @@
# frozen_string_literal: true
require 'bundler/gem_tasks' require 'bundler/gem_tasks'
require 'rspec/core/rake_task' require 'rspec/core/rake_task'
require 'rubocop/rake_task'
require 'bundler/audit/task'
RSpec::Core::RakeTask.new(:spec) RSpec::Core::RakeTask.new(:spec)
RuboCop::RakeTask.new
Bundler::Audit::Task.new
task :default => :spec desc 'Run experimental solargraph type checker'
task :solargraph do
sh 'solargraph typecheck'
end
namespace :solargraph do
desc 'Should be run by developer once to prepare initial solargraph usage (fill caches etc.)'
task :init do
sh 'solargraph download-core'
end
end
task default: [:rubocop, :spec, 'bundle:audit', :solargraph]
desc 'Run all tasks desired for CI'
task ci: ['solargraph:init', :default]

15
docs/Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
FROM alpine:3.13
EXPOSE 8080
ENV VERSION=3.2.1
RUN apk --no-cache add openssl ca-certificates && \
apk --no-cache add ruby ruby-etc ruby-webrick && \
apk --no-cache add --virtual .build-deps ruby-dev build-base tzdata && \
gem install --no-document openvpn-status-web -v ${VERSION} && \
# set timezone to Berlin
cp /usr/share/zoneinfo/Europe/Berlin /etc/localtime && \
apk del .build-deps
ENTRYPOINT ["openvpn-status-web", "/etc/openvpn-status-web/config.yml"]

View File

@@ -0,0 +1,40 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: openvpn-status-web
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Handle openvpn-status-web gem
### END INIT INFO
# using the system ruby's gem binaries directory
DAEMON="/var/lib/gems/1.8/bin/openvpn-status-web"
CONFIG_FILE="/opt/openvpn-status-web/config.yaml"
DAEMON_OPTS="$CONFIG_FILE"
test -x $DAEMON || exit 0
. /lib/lsb/init-functions
case "$1" in
start)
log_daemon_msg "Starting openvpn-web-status" "openvpn-web-status"
start-stop-daemon --start --quiet --oknodo --make-pidfile --pidfile "/var/run/openvpn-web-status.pid" --background --exec $DAEMON -- $DAEMON_OPTS
;;
stop)
log_daemon_msg "Stopping openvpn-web-status" "openvpn-web-status"
start-stop-daemon --stop --quiet --oknodo --pidfile "/var/run/openvpn-web-status.pid"
;;
restart|force-reload)
log_daemon_msg "Restarting openvpn-web-status" "openvpn-web-status"
start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile "/var/run/openvpn-web-status.pid"
start-stop-daemon --start --quiet --oknodo --make-pidfile --pidfile "/var/run/openvpn-web-status.pid" --background --exec $DAEMON -- $DAEMON_OPTS
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload}" >&2
exit 1
;;
esac

View File

@@ -1,8 +1,8 @@
TITLE,OpenVPN 2.1_rc15 mipsel-unknown-linux-gnu [SSL] [LZO1] [EPOLL] built on Mar 27 2009 TITLE,OpenVPN 2.1_rc15 mipsel-unknown-linux-gnu [SSL] [LZO1] [EPOLL] built on Mar 27 2009
TIME,Sun Jan 1 23:42:00 2012,1238702330 TIME,Sun Jan 1 23:42:00 2012,1238702330
HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t) HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t)
CLIENT_LIST,foo,1.2.3.4:1234,11811160064,4194304,Sun Jan 1 23:42:00 2012,1238702330 CLIENT_LIST,foo,1.2.3.4:1234,192.168.66.2,11811160064,4194304,Sun Jan 1 23:42:00 2012,1238702330
CLIENT_LIST,bar,1.2.3.5:1235,512,2048,Sun Jan 1 23:42:00 2012,1238702330 CLIENT_LIST,bar,1.2.3.5:1235,2001:db8:0:0::1000,512,2048,Sun Jan 1 23:42:00 2012,1238702330
HEADER,ROUTING_TABLE,Virtual Address,Common Name,Real Address,Last Ref,Last Ref (time_t) HEADER,ROUTING_TABLE,Virtual Address,Common Name,Real Address,Last Ref,Last Ref (time_t)
ROUTING_TABLE,192.168.0.0/24,foo,1.2.3.4:1234,Sun Jan 1 23:42:00 2012,1238702330 ROUTING_TABLE,192.168.0.0/24,foo,1.2.3.4:1234,Sun Jan 1 23:42:00 2012,1238702330
ROUTING_TABLE,192.168.66.2,bar,1.2.3.5:1235,Sun Jan 1 23:42:00 2012,1238702330 ROUTING_TABLE,192.168.66.2,bar,1.2.3.5:1235,Sun Jan 1 23:42:00 2012,1238702330

View File

@@ -1,8 +1,8 @@
TITLE OpenVPN 2.1_rc15 mipsel-unknown-linux-gnu [SSL] [LZO1] [EPOLL] built on Mar 27 2009 TITLE OpenVPN 2.1_rc15 mipsel-unknown-linux-gnu [SSL] [LZO1] [EPOLL] built on Mar 27 2009
TIME Sun Jan 1 23:42:00 2012 1238702330 TIME Sun Jan 1 23:42:00 2012 1238702330
HEADER CLIENT_LIST Common Name Real Address Virtual Address Bytes Received Bytes Sent Connected Since Connected Since (time_t) HEADER CLIENT_LIST Common Name Real Address Virtual Address Bytes Received Bytes Sent Connected Since Connected Since (time_t)
CLIENT_LIST foo 1.2.3.4:1234 11811160064 4194304 Sun Jan 1 23:42:00 2012 1238702330 CLIENT_LIST foo 1.2.3.4:1234 192.168.66.2 11811160064 4194304 Sun Jan 1 23:42:00 2012 1238702330
CLIENT_LIST bar 1.2.3.5:1235 512 2048 Sun Jan 1 23:42:00 2012 1238702330 CLIENT_LIST bar 1.2.3.5:1235 2001:db8:0:0::1000 512 2048 Sun Jan 1 23:42:00 2012 1238702330
HEADER ROUTING_TABLE Virtual Address Common Name Real Address Last Ref Last Ref (time_t) HEADER ROUTING_TABLE Virtual Address Common Name Real Address Last Ref Last Ref (time_t)
ROUTING_TABLE 192.168.0.0/24 foo 1.2.3.4:1234 Sun Jan 1 23:42:00 2012 1238702330 ROUTING_TABLE 192.168.0.0/24 foo 1.2.3.4:1234 Sun Jan 1 23:42:00 2012 1238702330
ROUTING_TABLE 192.168.66.2 bar 1.2.3.5:1235 Sun Jan 1 23:42:00 2012 1238702330 ROUTING_TABLE 192.168.66.2 bar 1.2.3.5:1235 Sun Jan 1 23:42:00 2012 1238702330

8
examples/status2_5.v2 Normal file
View File

@@ -0,0 +1,8 @@
TITLE,OpenVPN 2.5.1 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on May 14 2021
TIME,2012-01-01 23:42:00,1238702330
HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Virtual IPv6 Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username,Client ID,Peer ID,Data Channel Cipher
CLIENT_LIST,foo,1.2.3.4:1234,192.168.66.2,,11811160064,4194304,2012-01-01 23:42:00,1238702330,UNDEF,1,0,AES-256-GCM
HEADER,ROUTING_TABLE,Virtual Address,Common Name,Real Address,Last Ref,Last Ref (time_t)
ROUTING_TABLE,192.168.66.2,foo,1.2.3.4:1234,2012-01-01 23:42:00,1238702330
GLOBAL_STATS,Max bcast/mcast queue length,42
END

View File

@@ -1,3 +1,5 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'openvpn-status-web' require 'openvpn-status-web'

View File

@@ -1,48 +0,0 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: openvpn-status-web
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Handle openvpn-status-web
### END INIT INFO
# your ruby interpreter
DAEMON="/usr/bin/ruby"
# some unique name identifying your VPN
VPN_NAME="vpn.example.org"
# path to the OpenVPN status log file
STATUS_PATH="/var/log/openvpn-status.log"
# host and port for this daemon to listen on
HOST="127.0.0.1"
PORT="3000"
DAEMON_OPTS="/opt/openvpn-status-web/status.rb $VPN_NAME $STATUS_PATH $HOST $PORT"
test -x $DAEMON || exit 0
. /lib/lsb/init-functions
case "$1" in
start)
log_daemon_msg "Starting openvpn-web-status for $VPN_NAME" "openvpn-web-status"
start-stop-daemon --start --quiet --oknodo --make-pidfile --pidfile "/var/run/$VPN_NAME.pid" --background --exec $DAEMON -- $DAEMON_OPTS
;;
stop)
log_daemon_msg "Stopping openvpn-web-status for $VPN_NAME" "openvpn-web-status"
start-stop-daemon --stop --quiet --oknodo --pidfile "/var/run/$VPN_NAME.pid"
;;
restart|force-reload)
log_daemon_msg "Restarting openvpn-web-status for $VPN_NAME" "openvpn-web-status"
start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile "/var/run/$VPN_NAME.pid"
start-stop-daemon --start --quiet --oknodo --make-pidfile --pidfile "/var/run/$VPN_NAME.pid" --background --exec $DAEMON -- $DAEMON_OPTS
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload}" >&2
exit 1
;;
esac

132
lib/openvpn-status-web.rb Normal file → Executable file
View File

@@ -1,96 +1,148 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true
require 'date'
require 'etc' require 'etc'
require 'logger' require 'logger'
require 'ipaddr' require 'ipaddr'
require 'yaml' require 'yaml'
require 'rack' require 'rack'
require 'erb'
require 'metriks' require 'metriks'
require 'better_errors' if ENV['RACK_ENV'] == 'development'
require 'openvpn-status-web/status'
require 'openvpn-status-web/parser/v1'
require 'openvpn-status-web/parser/v2'
require 'openvpn-status-web/parser/v3'
require 'openvpn-status-web/int_patch' require 'openvpn-status-web/int_patch'
require 'openvpn-status-web/version' require 'openvpn-status-web/version'
module OpenVPNStatusWeb module OpenVPNStatusWeb
# @return [Logger]
def self.logger def self.logger
@logger @logger
end end
# @param logger [Logger]
# @return [Logger]
def self.logger=(logger) def self.logger=(logger)
@logger = logger @logger = logger
end end
class LogFormatter class LogFormatter
def call(lvl, time, progname, msg) # @param lvl [Object]
"[%s] %-5s %s\n" % [Time.now.strftime('%Y-%m-%d %H:%M:%S'), lvl, msg.to_s] # @param _time [DateTime]
# @param _progname [String]
# @param msg [Object]
# @return [String]
def call(lvl, _time, _progname, msg)
format("[%s] %-5s %s\n", Time.now.strftime('%Y-%m-%d %H:%M:%S'), lvl, msg.to_s)
end end
end end
class Daemon class Daemon
def initialize(name, file) def initialize(vpns)
@name = name @vpns = vpns
@file = file
@main_tmpl = read_template(File.join(File.dirname(__FILE__), 'openvpn-status-web/main.html.erb'))
end end
def call(env) def call(env)
main_tmpl = read_template(File.join(File.dirname(__FILE__), 'openvpn-status-web/main.html.erb')) return [405, {'Content-Type' => 'text/plain'}, ['Method Not Allowed']] if env['REQUEST_METHOD'] != 'GET'
# variables for template return [404, {'Content-Type' => 'text/plain'}, ['Not Found']] if env['PATH_INFO'] != '/'
name = @name
client_list, routing_table, global_stats = read_status_log(@file)
html = main_tmpl.result(binding) # variables for template
[200, {"Content-Type" => "text/html"}, [html]] vpns = @vpns
stati = {}
@vpns.each do |name, config|
stati[name] = parse_status_log(config)
end
# eval
html = @main_tmpl.result(binding)
[200, {'Content-Type' => 'text/html'}, [html]]
end end
def read_template(file) def read_template(file)
text = File.open(file, 'rb') do |f| f.read end text = File.read(file, mode: 'rb')
ERB.new(text) ERB.new(text)
end end
def read_status_log(file) def parse_status_log(vpn)
text = File.open(file, 'rb') do |f| f.read end text = File.read(vpn['status_file'], mode: 'rb')
current_section = :none case vpn['version']
client_list = [] when 1
routing_table = [] OpenVPNStatusWeb::Parser::V1.new.parse_status_log(text)
global_stats = [] when 2
OpenVPNStatusWeb::Parser::V2.new.parse_status_log(text)
text.lines.each do |line| when 3
(current_section = :cl; next) if line == "OpenVPN CLIENT LIST\n" OpenVPNStatusWeb::Parser::V3.new.parse_status_log(text)
(current_section = :rt; next) if line == "ROUTING TABLE\n" else
(current_section = :gs; next) if line == "GLOBAL STATS\n" raise "No suitable parser for status-version #{vpn['version']}"
(current_section = :end; next) if line == "END\n"
case current_section
when :cl then client_list << line.strip.split(',')
when :rt then routing_table << line.strip.split(',')
when :gs then global_stats << line.strip.split(',')
end
end end
[client_list[2..-1], routing_table[1..-1], global_stats]
end end
# @return [void]
def self.run! def self.run!
if ARGV.length != 4 if ARGV.length != 1
puts "Usage: openvpn-status-web vpn-name status-log listen-host listen-port" puts 'Usage: openvpn-status-web config_file'
exit 1 exit 1
end end
OpenVPNStatusWeb.logger = Logger.new(STDOUT) config_file = ARGV[0]
OpenVPNStatusWeb.logger.progname = "openvpn-status-web" if !File.file?(config_file)
puts 'Config file not found!'
exit 1
end
puts "openvpn-status-web version #{OpenVPNStatusWeb::VERSION}"
puts "Using config file #{config_file}"
config = YAML.safe_load(File.read(config_file, mode: 'r'))
if config['logfile']
OpenVPNStatusWeb.logger = Logger.new(config['logfile'])
else
OpenVPNStatusWeb.logger = Logger.new($stdout)
end
OpenVPNStatusWeb.logger.progname = 'openvpn-status-web'
OpenVPNStatusWeb.logger.formatter = LogFormatter.new OpenVPNStatusWeb.logger.formatter = LogFormatter.new
OpenVPNStatusWeb.logger.info "Starting..." OpenVPNStatusWeb.logger.info 'Starting...'
# drop priviliges as soon as possible
# NOTE: first change group than user
if config['group']
group = Etc.getgrnam(config['group'])
Process::Sys.setgid(group.gid) if group
end
if config['user']
user = Etc.getpwnam(config['user'])
Process::Sys.setuid(user.uid) if user
end
# configure rack
app = Daemon.new(config['vpns'])
if ENV['RACK_ENV'] == 'development'
app = BetterErrors::Middleware.new(app)
BetterErrors.application_root = File.expand_path(__dir__)
end
Signal.trap('INT') do Signal.trap('INT') do
OpenVPNStatusWeb.logger.info "Quitting..." OpenVPNStatusWeb.logger.info 'Quitting...'
Rack::Handler::WEBrick.shutdown
end
Signal.trap('TERM') do
OpenVPNStatusWeb.logger.info 'Quitting...'
Rack::Handler::WEBrick.shutdown Rack::Handler::WEBrick.shutdown
end end
app = Daemon.new(ARGV[0], ARGV[1]) Rack::Handler::WEBrick.run app, Host: config['host'], Port: config['port']
Rack::Handler::WEBrick.run app, :Host => ARGV[2], :Port => ARGV[3]
end end
end end
end end

View File

@@ -1,13 +1,14 @@
# frozen_string_literal: true
class Integer class Integer
def as_bytes def as_bytes
return "1 Byte" if self == 1 return '1 Byte' if self == 1
label = ["Bytes", "KiB", "MiB", "GiB", "TiB"] label = %w[Bytes KiB MiB GiB TiB]
i = 0 i = 0
num = self.to_f num = to_f
while num >= 1024 do while num >= 1024
num = num / 1024 num /= 1024
i += 1 i += 1
end end

View File

@@ -42,67 +42,102 @@ thead {
</head> </head>
<body> <body>
<h1>OpenVPN Status for <%= name %></h1> <% vpns.each do |name,config| %>
<% status = stati[name] %>
<h1>OpenVPN Status for <%= name %></h1>
<h2>Client List</h2> <h2>Client List</h2>
<div> <div>
<table> <table>
<thead> <thead>
<td class="first">Common Name</td> <% status.client_list_headers.each_with_index do |header,i| %>
<td class="middle">Real Address</td> <% if i == 0 %>
<td class="middle">Data Received</td> <td class="first">
<td class="middle">Data Sent</td> <% elsif i == status.client_list_headers.size-1 %>
<td class="last">Connected Since</td> <td class="last">
</thead> <% else %>
<tbody> <td class="middle">
<% client_list.each do |client| %>
<tr>
<td class="first"><%= client[0] %></td>
<td class="middle"><%= client[1] %></td>
<td class="middle"><%= client[2].to_i.as_bytes %></td>
<td class="middle"><%= client[3].to_i.as_bytes %></td>
<td class="last"><%= client[4] %></td>
</tr>
<% end %> <% end %>
</tbody> <%= header %></td>
</table> <% end %>
</div> </thead>
<tbody>
<% status.client_list.each do |client| %>
<tr>
<% status.client_list_headers.each_with_index do |header,i| %>
<% if i == 0 %>
<td class="first">
<% elsif i == status.client_list_headers.size-1 %>
<td class="last">
<% else %>
<td class="middle">
<% end %>
<% if header =~ /(Received|Sent)/ %>
<%= client[i].as_bytes %></td>
<% elsif client[i].is_a? DateTime %>
<%= client[i].strftime('%-d.%-m.%Y %H:%M:%S') %></td>
<% else %>
<%= client[i] %></td>
<% end %>
<% end %>
</tr>
<% end %>
</tbody>
</table>
</div>
<h2>Routing Table</h2> <h2>Routing Table</h2>
<div> <div>
<table> <table>
<thead> <thead>
<td class="first">Virtual Address</td> <% status.routing_table_headers.each_with_index do |header,i| %>
<td class="middle">Common Name</td> <% if i == 0 %>
<td class="middle">Real Address</td> <td class="first">
<td class="last">Last Ref</td> <% elsif i == status.routing_table_headers.size-1 %>
</thead> <td class="last">
<tbody> <% else %>
<% routing_table.each do |e| %> <td class="middle">
<tr>
<td class="first"><%= e[0] %></td>
<td class="middle"><%= e[1] %></td>
<td class="middle"><%= e[2] %></td>
<td class="last"><%= e[3] %></td>
</tr>
<% end %> <% end %>
</tbody> <%= header %></td>
</table> <% end %>
</div> </thead>
<tbody>
<% status.routing_table.each do |route| %>
<tr>
<% status.routing_table_headers.each_with_index do |header,i| %>
<% if i == 0 %>
<td class="first">
<% elsif i == status.routing_table_headers.size-1 %>
<td class="last">
<% else %>
<td class="middle">
<% end %>
<% if route[i].is_a? DateTime %>
<%= route[i].strftime('%-d.%-m.%Y %H:%M:%S') %></td>
<% else %>
<%= route[i] %></td>
<% end %>
<% end %>
</tr>
<% end %>
</tbody>
</table>
</div>
<h2>Global Stats</h2> <h2>Global Stats</h2>
<div> <div>
<table> <table>
<tbody> <tbody>
<% global_stats.each do |e| %> <% status.global_stats.each do |global| %>
<tr> <tr>
<td><%= e[0] %>:</td> <td><%= global[0] %>:</td>
<td><%= e[1] %></td> <td><%= global[1] %></td>
</tr> </tr>
<% end %>
</tbody>
</table>
</div>
<% end %> <% end %>
</tbody>
</table>
</div>
</body> </body>
</html> </html>

View File

@@ -0,0 +1,55 @@
# frozen_string_literal: true
module OpenVPNStatusWeb
module Parser
class ModernStateless
def self.parse_status_log(text, sep)
status = Status.new
status.client_list_headers = []
status.client_list = []
status.routing_table_headers = []
status.routing_table = []
status.global_stats = []
text.lines.each do |line|
parts = line.strip.split(sep)
status.client_list_headers = parts[2..-1] if parts[0] == 'HEADER' && parts[1] == 'CLIENT_LIST'
status.client_list << parse_client(parts[1..-1], status.client_list_headers) if parts[0] == 'CLIENT_LIST'
status.routing_table_headers = parts[2..-1] if parts[0] == 'HEADER' && parts[1] == 'ROUTING_TABLE'
status.routing_table << parse_route(parts[1..-1], status.routing_table_headers) if parts[0] == 'ROUTING_TABLE'
status.global_stats << parse_global(parts[1..2]) if parts[0] == 'GLOBAL_STATS'
end
status
end
private_class_method def self.parse_client(client, headers)
headers.each_with_index do |header, i|
client[i] = parse_date(client[i]) if header.end_with?('Since')
client[i] = client[i].to_i if header.start_with?('Bytes')
end
client
end
private_class_method def self.parse_route(route, headers)
headers.each_with_index do |header, i|
route[i] = parse_date(route[i]) if header.end_with?('Last Ref')
end
route
end
private_class_method def self.parse_global(global)
global[1] = global[1].to_i
global
end
private_class_method def self.parse_date(date_string)
DateTime.strptime(date_string, '%a %b %d %k:%M:%S %Y')
rescue ArgumentError
DateTime.strptime(date_string, '%Y-%m-%d %k:%M:%S')
end
end
end
end

View File

@@ -0,0 +1,57 @@
# frozen_string_literal: true
module OpenVPNStatusWeb
module Parser
class V1
def parse_status_log(text)
current_section = :none
client_list = []
routing_table = []
global_stats = []
text.lines.each do |line|
(current_section = :cl; next) if line == "OpenVPN CLIENT LIST\n"
(current_section = :rt; next) if line == "ROUTING TABLE\n"
(current_section = :gs; next) if line == "GLOBAL STATS\n"
(current_section = :end; next) if line == "END\n"
case current_section
when :cl
client_list << line.strip.split(',')
when :rt
routing_table << line.strip.split(',')
when :gs
global_stats << line.strip.split(',')
end
end
status = Status.new
status.client_list_headers = ['Common Name', 'Real Address', 'Data Received', 'Data Sent', 'Connected Since']
status.client_list = client_list[2..-1].map { |client| parse_client(client) }
status.routing_table_headers = ['Virtual Address', 'Common Name', 'Real Address', 'Last Ref']
status.routing_table = routing_table[1..-1].map { |route| parse_route(route) }
status.global_stats = global_stats.map { |global| parse_global(global) }
status
end
private
def parse_client(client)
client[2] = client[2].to_i
client[3] = client[3].to_i
client[4] = DateTime.strptime(client[4], '%a %b %d %k:%M:%S %Y')
client
end
def parse_route(route)
route[3] = DateTime.strptime(route[3], '%a %b %d %k:%M:%S %Y')
route
end
def parse_global(global)
global[1] = global[1].to_i
global
end
end
end
end

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
require_relative 'modern_stateless'
module OpenVPNStatusWeb
module Parser
class V2
def parse_status_log(text)
OpenVPNStatusWeb::Parser::ModernStateless.parse_status_log(text, ',')
end
end
end
end

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
require_relative 'modern_stateless'
module OpenVPNStatusWeb
module Parser
class V3
def parse_status_log(text)
OpenVPNStatusWeb::Parser::ModernStateless.parse_status_log(text, "\t")
end
end
end
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
module OpenVPNStatusWeb
class Status
attr_accessor :client_list_headers
attr_accessor :client_list
attr_accessor :routing_table_headers
attr_accessor :routing_table
attr_accessor :global_stats
end
end

View File

@@ -1,4 +1,5 @@
# frozen_string_literal: true
module OpenVPNStatusWeb module OpenVPNStatusWeb
VERSION = "0.0.1" VERSION = '3.2.1'
end end

View File

@@ -1,31 +1,44 @@
# frozen_string_literal: true
$:.push File.expand_path("../lib", __FILE__) require_relative 'lib/openvpn-status-web/version'
require 'openvpn-status-web/version'
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = 'openvpn-status-web' s.name = 'openvpn-status-web'
s.version = OpenVPNStatusWeb::VERSION s.version = OpenVPNStatusWeb::VERSION
s.summary = 'openvpn-status-web' s.summary = 'openvpn-status-web'
s.description = 'Small Rack application that parses and serves the OpenVPN status file.' s.description = 'Small Rack (Ruby) application serving OpenVPN status file.'
s.author = 'Christian Nicolai' s.author = 'Christian Nicolai'
s.email = 'chrnicolai@gmail.com'
s.license = 'Apache License Version 2.0'
s.homepage = 'https://github.com/cmur2/openvpn-status-web'
s.files = `git ls-files`.split($/) s.homepage = 'https://github.com/cmur2/openvpn-status-web'
s.test_files = s.files.grep(%r{^(test|spec|features)/}) s.license = 'Apache-2.0'
s.metadata = {
'bug_tracker_uri' => "#{s.homepage}/issues",
'source_code_uri' => s.homepage
}
s.files = `git ls-files -z`.split("\x0").select do |f|
f.match(%r{^(init.d|lib)/})
end
s.require_paths = ['lib'] s.require_paths = ['lib']
s.bindir = 'exe'
s.executables = ['openvpn-status-web'] s.executables = ['openvpn-status-web']
s.extra_rdoc_files = Dir['README.md', 'LICENSE']
s.required_ruby_version = '>= 2.5'
s.add_runtime_dependency 'rack'
s.add_runtime_dependency 'json'
s.add_runtime_dependency 'metriks' s.add_runtime_dependency 'metriks'
s.add_runtime_dependency 'rack', '~> 2.0'
s.add_runtime_dependency 'webrick', '>= 1.6.1'
s.add_development_dependency 'bundler', '~> 1.3' s.add_development_dependency 'better_errors'
s.add_development_dependency 'binding_of_caller'
s.add_development_dependency 'bundler'
s.add_development_dependency 'bundler-audit', '~> 0.9.0'
s.add_development_dependency 'rack-test'
s.add_development_dependency 'rake' s.add_development_dependency 'rake'
s.add_development_dependency 'rspec' s.add_development_dependency 'rspec'
s.add_development_dependency 'rack-test' s.add_development_dependency 'rubocop', '~> 1.24.0'
s.add_development_dependency 'rubocop-rake', '~> 0.6.0'
s.add_development_dependency 'rubocop-rspec', '~> 2.7.0'
s.add_development_dependency 'solargraph', '~> 0.44.0'
end end

View File

@@ -0,0 +1,83 @@
# frozen_string_literal: true
require_relative '../../spec_helper'
describe OpenVPNStatusWeb::Parser::ModernStateless do
{
2 => status_v2,
3 => status_v3
}.each do |version, status|
context "for status-version #{version}" do
context 'with client list' do
it 'parses common names' do
expect(status.client_list.map { |client| client[0] }).to eq(%w[foo bar])
end
it 'parses real addresses' do
expect(status.client_list.map { |client| client[1] }).to eq(['1.2.3.4:1234', '1.2.3.5:1235'])
end
it 'parses virtual addresses' do
expect(status.client_list.map { |client| client[2] }).to eq(['192.168.66.2', '2001:db8:0:0::1000'])
end
it 'parses received bytes' do
expect(status.client_list.map { |client| client[3] }).to eq([11_811_160_064, 512])
end
it 'parses sent bytes' do
expect(status.client_list.map { |client| client[4] }).to eq([4_194_304, 2048])
end
it 'parses connected since date' do
expect(status.client_list.map { |client| client[5] }).to eq(
[
DateTime.new(2012, 1, 1, 23, 42, 0),
DateTime.new(2012, 1, 1, 23, 42, 0)
]
)
end
it 'has the same number of headers' do
expect(status.client_list[0].size).to eq(status.client_list_headers.size)
end
end
context 'with routing table' do
it 'parses virtual addresses' do
expect(status.routing_table.map { |route| route[0] }).to eq(['192.168.0.0/24', '192.168.66.2', '192.168.66.3', '2001:db8:0:0::1000'])
end
it 'parses common names' do
expect(status.routing_table.map { |route| route[1] }).to eq(%w[foo bar foo bar])
end
it 'parses real addresses' do
expect(status.routing_table.map { |route| route[2] }).to eq(['1.2.3.4:1234', '1.2.3.5:1235', '1.2.3.4:1234', '1.2.3.5:1235'])
end
it 'parses last ref date' do
expect(status.routing_table.map { |route| route[3] }).to eq(
[
DateTime.new(2012, 1, 1, 23, 42, 0), DateTime.new(2012, 1, 1, 23, 42, 0),
DateTime.new(2012, 1, 1, 23, 42, 0), DateTime.new(2012, 1, 1, 23, 42, 0)
]
)
end
it 'has the same number of headers' do
expect(status.routing_table[0].size).to eq(status.routing_table_headers.size)
end
end
it 'parses global stats' do
expect(status.global_stats.size).to eq(1)
expect(status.global_stats.first).to eq(['Max bcast/mcast queue length', 42])
end
end
end
it 'parses status-version 2 of OpenVPN 2.5' do
expect(status_2_5_v2).not_to be_nil
end
end

View File

@@ -0,0 +1,71 @@
# frozen_string_literal: true
require_relative '../../spec_helper'
describe OpenVPNStatusWeb::Parser::V1 do
def status
status_v1
end
context 'with client list' do
it 'parses common names' do
expect(status.client_list.map { |client| client[0] }).to eq(%w[foo bar])
end
it 'parses real addresses' do
expect(status.client_list.map { |client| client[1] }).to eq(['1.2.3.4:1234', '1.2.3.5:1235'])
end
it 'parses received bytes' do
expect(status.client_list.map { |client| client[2] }).to eq([11_811_160_064, 512])
end
it 'parses sent bytes' do
expect(status.client_list.map { |client| client[3] }).to eq([4_194_304, 2048])
end
it 'parses connected since date' do
expect(status.client_list.map { |client| client[4] }).to eq(
[
DateTime.new(2012, 1, 1, 23, 42, 0), DateTime.new(2012, 1, 1, 23, 42, 0)
]
)
end
it 'has the same number of headers' do
expect(status.client_list[0].length).to eq(status.client_list_headers.length)
end
end
context 'with routing table' do
it 'parses virtual addresses' do
expect(status.routing_table.map { |route| route[0] }).to eq(['192.168.0.0/24', '192.168.66.2', '192.168.66.3', '2001:db8:0:0::1000'])
end
it 'parses common names' do
expect(status.routing_table.map { |route| route[1] }).to eq(%w[foo bar foo bar])
end
it 'parses real addresses' do
expect(status.routing_table.map { |route| route[2] }).to eq(['1.2.3.4:1234', '1.2.3.5:1235', '1.2.3.4:1234', '1.2.3.5:1235'])
end
it 'parses last ref date' do
expect(status.routing_table.map { |route| route[3] }).to eq(
[
DateTime.new(2012, 1, 1, 23, 42, 0), DateTime.new(2012, 1, 1, 23, 42, 0),
DateTime.new(2012, 1, 1, 23, 42, 0), DateTime.new(2012, 1, 1, 23, 42, 0)
]
)
end
it 'has the same number of headers' do
expect(status.routing_table[0].length).to eq(status.routing_table_headers.length)
end
end
it 'parses global stats' do
expect(status.global_stats.size).to eq(1)
expect(status.global_stats.first).to eq(['Max bcast/mcast queue length', 42])
end
end

27
spec/spec_helper.rb Normal file
View File

@@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'rubygems'
require 'bundler/setup'
require 'rack/test'
require 'openvpn-status-web'
def status_v1
text = File.binread('examples/status.v1')
OpenVPNStatusWeb::Parser::V1.new.parse_status_log text
end
def status_v2
text = File.binread('examples/status.v2')
OpenVPNStatusWeb::Parser::V2.new.parse_status_log text
end
def status_2_5_v2
text = File.binread('examples/status2_5.v2')
OpenVPNStatusWeb::Parser::V2.new.parse_status_log text
end
def status_v3
text = File.binread('examples/status.v3')
OpenVPNStatusWeb::Parser::V3.new.parse_status_log text
end