riseup_vpn 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rubocop.yml +19 -0
- data/CHANGELOG.md +5 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +77 -0
- data/Rakefile +35 -0
- data/assets/vpns.png +0 -0
- data/lib/riseup_vpn/api.rb +50 -0
- data/lib/riseup_vpn/generator.rb +113 -0
- data/lib/riseup_vpn/template_ovpn.yml +30 -0
- data/lib/riseup_vpn/version.rb +5 -0
- data/lib/riseup_vpn.rb +13 -0
- data/sig/riseup_vpn.rbs +4 -0
- data/tmp/.placeholder +0 -0
- metadata +56 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 061471e29bcb71a8ad7d20c0757f445ed026913613deb3a289bde237aa40022e
|
4
|
+
data.tar.gz: 7a780713819d589fdf07e4abd684c6b341e61e5d0ffdf6ccc331693f35cc5301
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ae6548f93e8f3734c6578a0f593b979384735e34d07e84d6ffc6bf71ec741440086e0a480b2e19c7939072ee0876a33bf53b33224c3283e15ca7d913c98bd8d1
|
7
|
+
data.tar.gz: 6e5c748da5d135578357540b95d0ea97fb8e92287408443dac4acd655da6a8cd8ecd7dcb8bec4cc0944ad419a9eb4770c76447590da7d2927fb8efcab1a62652
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
---
|
2
|
+
plugins:
|
3
|
+
- rubocop-minitest
|
4
|
+
- rubocop-performance
|
5
|
+
- rubocop-rake
|
6
|
+
|
7
|
+
AllCops:
|
8
|
+
TargetRubyVersion: 3.1
|
9
|
+
NewCops: enable
|
10
|
+
|
11
|
+
Style/HashSyntax:
|
12
|
+
Enabled: false # yuk Ruby 3.1
|
13
|
+
|
14
|
+
Style/RegexpLiteral:
|
15
|
+
Exclude:
|
16
|
+
- 'Guardfile'
|
17
|
+
|
18
|
+
Minitest/TestMethodName:
|
19
|
+
Enabled: true
|
data/CHANGELOG.md
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# NOTE: rake runs threadsafe tests separately from others. Guard may show false positives when running all tests
|
4
|
+
guard :minitest, all_on_start: false do
|
5
|
+
watch(%r{^test/(.*)/?test_(.*)\.rb$})
|
6
|
+
watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
|
7
|
+
# watch(%r{^test/test_helper\.rb$}) { 'test' } # See note. Run bundle exec rake instead.
|
8
|
+
ignore %r{^/}, /Guardfile/ # See note. Run bundle exec rake instead.
|
9
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 MatzFan
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
[](https://badge.fury.io/rb/riseup_vpn)
|
2
|
+
[](https://github.com/rubocop/rubocop)
|
3
|
+
|
4
|
+
# RiseupVPN
|
5
|
+
|
6
|
+
A tool to create .ovpn ([OpenVPN](https://openvpn.net)) client config files for use with [RiseupVPN](https://riseup.net/vpn). Such files can be imported using, for example, the Debian [openvpn NetworkManager plugin](https://packages.debian.org/search?keywords=network-manager-openvpn). You will then be able to route your network connection through any of Riseup's 20+ VPN gateways! Screenshot below shows Linux Mint's Network Manager panel applet with an active VPN connection to RiseupVPN's Seattle gateway.
|
7
|
+
|
8
|
+
This project was inspired by [this](https://sizeof.cat/project/riseupvpn-to-openvpn) very excellent web tool.
|
9
|
+
|
10
|
+

|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Install the gem and add to the application's Gemfile by executing:
|
15
|
+
|
16
|
+
```bash
|
17
|
+
bundle add riseup_vpn
|
18
|
+
```
|
19
|
+
|
20
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
21
|
+
|
22
|
+
```bash
|
23
|
+
gem install riseup_vpn
|
24
|
+
```
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
require 'riseup_vpn'
|
30
|
+
|
31
|
+
generator = RiseupVpn::Generator.new
|
32
|
+
# => instance_of RiseupVpn::Generator
|
33
|
+
|
34
|
+
generator.ovpns
|
35
|
+
# => {"vpn01-sea.riseup.net" => "client\ntls-client\ndev tun\nproto udp\nremote 204.13.164.252 1194 ...
|
36
|
+
|
37
|
+
generator.ovpns.size
|
38
|
+
# => 21
|
39
|
+
|
40
|
+
RiseupVpn::Generator::OPTS # returns a list of valid options which maybe passed at initialization.
|
41
|
+
# => {proto: ["udp", "tcp"]} # e.g. RiseupVpn::Generator.new(proto: 'tcp') default is 'udp'
|
42
|
+
|
43
|
+
generator.write_files '~/.config/riseup_vpn' # dir must exist
|
44
|
+
```
|
45
|
+
The last command creates the following files in the chosen directory:
|
46
|
+
- an .ovpn file for each VPN gateway
|
47
|
+
- the Certificate Authority cert file (ca.crt)
|
48
|
+
- the client private key file (client.key)
|
49
|
+
- the client cert file (client.crt)
|
50
|
+
|
51
|
+
Note: OpenVPN requires that cert & key files shared across .ovpn client config files are present in the same directory.
|
52
|
+
|
53
|
+
## \_why?
|
54
|
+
|
55
|
+
I can use Riseup's white-labeled [desktop app](https://leap.se) to access Riseup's VPNs, so why the need?
|
56
|
+
|
57
|
+
- You can use this gem's Ruby interface to make Riseup's VPNs available to your apps!
|
58
|
+
- This approach saves you installing another app on your laptop/desktop (at least on Linux).
|
59
|
+
- Integrating Riseup's VPNs directly into your network manager means you can select individual gateways. The LEAP app currently allows selection by location only and there can be a wide disparity in connection speeds between them.
|
60
|
+
- You can configure your firewall to deny outgoing connections except on port 1194 (assuming UDP). LEAP's app makes DNS and HTTPS requests on startup to 1) get the gateway info and client key/cert and 2) determine the 'best' VPN for your location.
|
61
|
+
- This approach does mean you need to periodically update the client key/cert, as expiry is currently set for 30 days. A simple `cron` job can fix this and we are working into incorporating that functionality into this gem.
|
62
|
+
|
63
|
+
## Development
|
64
|
+
|
65
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
66
|
+
|
67
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
68
|
+
|
69
|
+
## Contributing
|
70
|
+
|
71
|
+
Known issues are recorded [here](https://gitlab.com/matzfan/riseup_vpn/-/issues).
|
72
|
+
|
73
|
+
Bug reports and pull requests are welcome on GitLab at https://gitlab.com/matzfan/riseup_vpn. For a pull request please checkout a suitably named feature branch before making and submitting changes.
|
74
|
+
|
75
|
+
## License
|
76
|
+
|
77
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'minitest/test_task'
|
5
|
+
require 'rake/testtask'
|
6
|
+
require 'rubocop/rake_task'
|
7
|
+
|
8
|
+
# Minitest::TestTask.create
|
9
|
+
|
10
|
+
namespace :test do
|
11
|
+
Rake::TestTask.new do |t|
|
12
|
+
t.name = 'api'
|
13
|
+
t.description = 'Run only API tests'
|
14
|
+
t.libs << 'lib'
|
15
|
+
t.test_files = ['test/riseup_vpn/test_api.rb']
|
16
|
+
end
|
17
|
+
|
18
|
+
Rake::TestTask.new do |t|
|
19
|
+
t.name = 'ex_api'
|
20
|
+
t.description = 'Run tests without API tests'
|
21
|
+
t.libs << 'lib'
|
22
|
+
t.test_files = FileList['test/**/test_*.rb']
|
23
|
+
t.test_files = FileList['test/**/test_*.rb'] - ['test/riseup_vpn/test_api.rb']
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Run the test suite'
|
28
|
+
task :test do
|
29
|
+
Rake::Task['test:api'].invoke
|
30
|
+
Rake::Task['test:ex_api'].invoke
|
31
|
+
end
|
32
|
+
|
33
|
+
RuboCop::RakeTask.new
|
34
|
+
|
35
|
+
task default: %i[test rubocop]
|
data/assets/vpns.png
ADDED
Binary file
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module RiseupVpn
|
6
|
+
# parses ca.cert, client cert/key & gateways data from riseup.net API
|
7
|
+
module Api
|
8
|
+
class ApiError < StandardError; end
|
9
|
+
|
10
|
+
PROVIDER = 'https://riseup.net/provider.json' # entry point for API
|
11
|
+
API_URI = 'https://api.black.riseup.net' # 443
|
12
|
+
API_VERSION = '3'
|
13
|
+
CA_CERT_URI = 'https://black.riseup.net/ca.crt'
|
14
|
+
CERT = 'cert' # not available from provider uri..
|
15
|
+
EIP = 'eip-service.json' # not available from provider uri..
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def ca_crt
|
19
|
+
OpenSSL::X509::Certificate.new get(CA_CERT_URI)
|
20
|
+
end
|
21
|
+
|
22
|
+
def client_key_and_crt
|
23
|
+
str = get(File.join(API_URI, API_VERSION, CERT))
|
24
|
+
[OpenSSL::PKey.read(str), OpenSSL::X509::Certificate.new(str)]
|
25
|
+
end
|
26
|
+
|
27
|
+
def client_key
|
28
|
+
OpenSSL::PKey.read get(File.join(API_URI, API_VERSION, CERT))
|
29
|
+
end
|
30
|
+
|
31
|
+
def eip
|
32
|
+
get_json File.join(API_URI, API_VERSION, EIP)
|
33
|
+
end
|
34
|
+
|
35
|
+
def get(url)
|
36
|
+
URI(url).read
|
37
|
+
rescue NoMethodError, URI::Error
|
38
|
+
raise ApiError, "Bad URI: '#{url}'"
|
39
|
+
rescue OpenURI::HTTPError
|
40
|
+
raise ApiError, "Failed to get url: '#{url}'"
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_json(url)
|
44
|
+
JSON.parse get(url)
|
45
|
+
rescue JSON::ParserError
|
46
|
+
raise ApiError, "Failed to parse JSON at: '#{url}'"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
require_relative 'api'
|
7
|
+
|
8
|
+
module RiseupVpn
|
9
|
+
# create a .ovpn config file for a gateway
|
10
|
+
class Generator
|
11
|
+
TEMPLATE = YAML.load_file File.join(__dir__, 'template_ovpn.yml')
|
12
|
+
OPTS = { proto: %w[udp tcp] }.freeze # default is 1st value
|
13
|
+
LINE_METHODS = %i[remote verify-x509-name].freeze
|
14
|
+
|
15
|
+
attr_reader :ovpns
|
16
|
+
|
17
|
+
def initialize(opts = {})
|
18
|
+
@opts = validate_options opts.transform_keys!(&:to_sym)
|
19
|
+
@server_config = server_config
|
20
|
+
@template = template
|
21
|
+
@ovpns = generate_ovpns # hash { gateway_name => string }
|
22
|
+
end
|
23
|
+
|
24
|
+
def write_files(dir)
|
25
|
+
dir = File.expand_path dir.to_s
|
26
|
+
write_ovpn_files dir
|
27
|
+
write_ca_file dir
|
28
|
+
write_client_key_and_crt_files dir
|
29
|
+
rescue Errno::ENOENT, Errno::EACCES
|
30
|
+
raise ArgumentError, "Dir #{dir} doesn't exist or isn't writable"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def write_ovpn_files(dir)
|
36
|
+
ovpns.each { |k, v| File.write(File.join(dir, "#{k}.ovpn"), v) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def write_ca_file(dir)
|
40
|
+
File.write File.join(dir, 'ca.crt'), Api.ca_crt.to_s.chomp
|
41
|
+
end
|
42
|
+
|
43
|
+
def write_client_key_and_crt_files(dir)
|
44
|
+
client_key, client_crt = Api.client_key_and_crt
|
45
|
+
File.write File.join(dir, 'client.key'), client_key.to_s.chomp
|
46
|
+
File.write File.join(dir, 'client.crt'), client_crt.to_s.chomp
|
47
|
+
end
|
48
|
+
|
49
|
+
def gateways
|
50
|
+
Api.eip['gateways']
|
51
|
+
end
|
52
|
+
|
53
|
+
def server_config
|
54
|
+
Api.eip['openvpn_configuration'].transform_keys(&:to_sym)
|
55
|
+
end
|
56
|
+
|
57
|
+
def validate_options(opts)
|
58
|
+
OPTS.each_key { |k| raise ArgumentError, "Valid #{k} opts: #{OPTS[k]}" if opts[k] && !OPTS[k].include?(opts[k]) }
|
59
|
+
|
60
|
+
OPTS.transform_values(&:first).merge opts
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_ovpns
|
64
|
+
gateways.inject({}) { |memo, gway| memo.merge(file_name(gway) => generate_ovpn(gway).join("\n")) }
|
65
|
+
end
|
66
|
+
|
67
|
+
def generate_ovpn(gateway)
|
68
|
+
@template.map { |k, v| generate_line(k.to_sym, v, gateway).to_s.strip }
|
69
|
+
end
|
70
|
+
|
71
|
+
def template
|
72
|
+
return TEMPLATE.except(*%i[up down]) if RiseupVpn.os == 'darwin'
|
73
|
+
return TEMPLATE if RiseupVpn.os == 'linux'
|
74
|
+
|
75
|
+
raise RiseupVpnError, "Unsupported OS: '#{RiseupVpn.os}'"
|
76
|
+
end
|
77
|
+
|
78
|
+
def generate_line(key, val, gateway)
|
79
|
+
return "#{key} #{val}" if val # hard coded key/value in template file
|
80
|
+
return "#{key} #{@opts[key]}" if @opts[key]
|
81
|
+
return server_config_line(key) if @server_config[key]
|
82
|
+
return send(regularize_method_name(key), gateway) if LINE_METHODS.include? key
|
83
|
+
|
84
|
+
key # hard coded key only in template file
|
85
|
+
end
|
86
|
+
|
87
|
+
def server_config_line(key)
|
88
|
+
return key if @server_config[key] == true # nobind & persist-key
|
89
|
+
|
90
|
+
"#{key} #{@server_config[key]}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def regularize_method_name(sym)
|
94
|
+
sym.to_s.tr('-', '_').to_sym
|
95
|
+
end
|
96
|
+
|
97
|
+
def remote(gateway)
|
98
|
+
"remote #{gateway['ip_address']} #{@opts[:proto] == 'udp' ? 1194 : 80}" # TODO: check port 80 in json
|
99
|
+
end
|
100
|
+
|
101
|
+
def verify_x509_name(gateway)
|
102
|
+
"verify-x509-name #{name(gateway)} name"
|
103
|
+
end
|
104
|
+
|
105
|
+
def name(gateway)
|
106
|
+
gateway['host'].split('.').first
|
107
|
+
end
|
108
|
+
|
109
|
+
def file_name(gateway)
|
110
|
+
"#{name(gateway)}.riseup.net"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
---
|
2
|
+
:client:
|
3
|
+
:tls-client:
|
4
|
+
:dev:
|
5
|
+
:proto:
|
6
|
+
:remote:
|
7
|
+
:auth:
|
8
|
+
:cipher:
|
9
|
+
:keepalive:
|
10
|
+
:tls-cipher:
|
11
|
+
:float:
|
12
|
+
:resolv-retry: infinite
|
13
|
+
:nobind:
|
14
|
+
:verb:
|
15
|
+
:persist-key:
|
16
|
+
:persist-tun:
|
17
|
+
:reneg-sec: 0
|
18
|
+
:pull:
|
19
|
+
:auth-nocache:
|
20
|
+
:script-security: 2
|
21
|
+
:up: /etc/openvpn/update-resolv-conf
|
22
|
+
:down: /etc/openvpn/update-resolv-conf
|
23
|
+
:tls-version-min:
|
24
|
+
:redirect-gateway: ipv6
|
25
|
+
:remote-cert-tls: server
|
26
|
+
:remote-cert-eku: "\"TLS Web Server Authentication\""
|
27
|
+
:verify-x509-name:
|
28
|
+
:ca: ca.crt
|
29
|
+
:cert: client.crt
|
30
|
+
:key: client.key
|
data/lib/riseup_vpn.rb
ADDED
data/sig/riseup_vpn.rbs
ADDED
data/tmp/.placeholder
ADDED
File without changes
|
metadata
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: riseup_vpn
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- MatzFan
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies: []
|
12
|
+
description: Gem to configure RiseupVPN
|
13
|
+
executables: []
|
14
|
+
extensions: []
|
15
|
+
extra_rdoc_files: []
|
16
|
+
files:
|
17
|
+
- ".rubocop.yml"
|
18
|
+
- CHANGELOG.md
|
19
|
+
- Guardfile
|
20
|
+
- LICENSE.txt
|
21
|
+
- README.md
|
22
|
+
- Rakefile
|
23
|
+
- assets/vpns.png
|
24
|
+
- lib/riseup_vpn.rb
|
25
|
+
- lib/riseup_vpn/api.rb
|
26
|
+
- lib/riseup_vpn/generator.rb
|
27
|
+
- lib/riseup_vpn/template_ovpn.yml
|
28
|
+
- lib/riseup_vpn/version.rb
|
29
|
+
- sig/riseup_vpn.rbs
|
30
|
+
- tmp/.placeholder
|
31
|
+
homepage: https://gitlab.com/matzfan/riseup_vpn
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata:
|
35
|
+
homepage_uri: https://gitlab.com/matzfan/riseup_vpn
|
36
|
+
source_code_uri: https://gitlab.com/matzfan/riseup_vpn
|
37
|
+
changelog_uri: https://gitlab.com/matzfan/riseup_vpn/CHANGELOG.md
|
38
|
+
rubygems_mfa_required: 'true'
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 3.1.0
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubygems_version: 3.6.7
|
54
|
+
specification_version: 4
|
55
|
+
summary: RiseupVPN configuration
|
56
|
+
test_files: []
|