riseup_vpn 1.1.0 → 1.2.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 +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +11 -0
- data/README.md +30 -7
- data/lib/riseup_vpn/api.rb +10 -9
- data/lib/riseup_vpn/{errors.rb → error.rb} +1 -0
- data/lib/riseup_vpn/generator.rb +3 -7
- data/lib/riseup_vpn/manager.rb +45 -12
- data/lib/riseup_vpn/nmcli.rb +4 -0
- data/lib/riseup_vpn/utils.rb +0 -8
- data/lib/riseup_vpn/version.rb +1 -1
- data/lib/riseup_vpn.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6aadf2b162813b7cc25700ac2352eda76e899498d25f8566ef6bb51b7c7aadbd
|
|
4
|
+
data.tar.gz: fbb7f9bb058abcf6c48e4a1d993ac62bd9bfcefacf67673821222abbc0a398bb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 628be1b80961627205b0888eb827f074bf1b2227fd06b41e1103e8871560cfafd9a04ba9077ea0cd7582f819577e68e9341a4d8eb713d4ab1b794afdf2cb83ab
|
|
7
|
+
data.tar.gz: 14d196bb02bcf7a457c4ee4d7954b356c3fa3ae9d3d8d78225286c56fd4eaaf0f75d3a17b4d8ab9f1580d6448557bb1389943f7f98ab6bc583c911932a8d28c8
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.2.0] - 2026-02-15
|
|
4
|
+
|
|
5
|
+
### New features
|
|
6
|
+
|
|
7
|
+
- config dir is created if it does not exist
|
|
8
|
+
- add abilty to generate ufw rules for the gateway IP addresses
|
|
9
|
+
|
|
10
|
+
### Bug fixes
|
|
11
|
+
|
|
12
|
+
- drop unecessary tls-version-min upgrade
|
|
13
|
+
|
|
3
14
|
## [1.1.0] - 2025-04-22
|
|
4
15
|
|
|
5
16
|
### New features
|
data/README.md
CHANGED
|
@@ -28,7 +28,7 @@ gem install riseup_vpn
|
|
|
28
28
|
```ruby
|
|
29
29
|
require 'riseup_vpn'
|
|
30
30
|
|
|
31
|
-
@manager = RiseupVPN::Manager.new # takes an optional positional arg for the dir path. Default is '~/.config/riseup_vpn'
|
|
31
|
+
@manager = RiseupVPN::Manager.new # takes an optional positional arg for the config files dir path. Default is '~/.config/riseup_vpn'
|
|
32
32
|
# => instance_of RiseupVPN::Manager
|
|
33
33
|
|
|
34
34
|
@manager.ovpns
|
|
@@ -40,7 +40,6 @@ require 'riseup_vpn'
|
|
|
40
40
|
RiseupVPN::Manager::OPTS # returns a list of valid options which maybe passed as an optional hash at initialization.
|
|
41
41
|
# => {proto: ["udp", "tcp"]} defaults are the first values e.g. 'udp' for :proto
|
|
42
42
|
# For example; RiseupVPN::Manager.new('/some/path', proto: 'tcp')
|
|
43
|
-
|
|
44
43
|
@manager.write_files # (over)writes the VPN client config files to the dir path
|
|
45
44
|
```
|
|
46
45
|
The last command creates (or overwrites) the following files:
|
|
@@ -49,9 +48,17 @@ The last command creates (or overwrites) the following files:
|
|
|
49
48
|
- the client private key file (client.key)
|
|
50
49
|
- the client cert file (client.crt)
|
|
51
50
|
|
|
52
|
-
Note
|
|
51
|
+
**Note**: The client certificate expires after 30 days and therefore needs to be updated regularly. This can be done manually by simply running `RiseupVPN::Manager.new.write_files` again. This will overwrite all of the above files and you'll be good for another 30 days. We'd recommend running this task once a week to ensure the client certificate does not expire. If you want to automate the process by running a ruby script (a good guide on how to do this [here](https://cronitor.io/guides/ruby-cron-jobs)) you'll need an executable which looks something like this:
|
|
52
|
+
```ruby
|
|
53
|
+
# in executable file called update_riseup_vpn.rb or similar:
|
|
54
|
+
require 'riseup_vpn'
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
RiseupVPN::Manager.new.write_files # assumes default config dir location
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Please also note: OpenVPN requires that cert & key files shared across .ovpn client config files are present in the same directory.
|
|
60
|
+
|
|
61
|
+
### Importing OpenVPN configuration files on Linux
|
|
55
62
|
|
|
56
63
|
On Linux, you can do this to make Riseup's VPNs available in Network Manager. It uses `nmcli` under the hood:
|
|
57
64
|
```Ruby
|
|
@@ -59,15 +66,31 @@ On Linux, you can do this to make Riseup's VPNs available in Network Manager. It
|
|
|
59
66
|
# the client key and cert files are updated too
|
|
60
67
|
```
|
|
61
68
|
|
|
69
|
+
### Firewall rules
|
|
70
|
+
|
|
71
|
+
If you use `ufw` firewall and deny/reject outgoing traffic not going through your VPN, you will need rule(s) allowing OpenVPN itself to connect. The normal way to do the latter is to allow outgoing traffic to anywhere on the relevant port (e.g. 1194/udp). The rule set might look something like [this](https://askubuntu.com/a/796281). RiseupVPN can instead generate a set of rules for outbound connections for OpenVPN's TCP/UDP ports *to the specific gateway IP addresses*. Specifying IP addresses is safer than allowing the relevant port(s) direct access everywhere. E.g:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
sudo ufw allow out 1194/udp # OK
|
|
75
|
+
|
|
76
|
+
# better
|
|
77
|
+
sudo ufw allow out from any to 204.13.164.252 port 1194 proto udp
|
|
78
|
+
sudo ufw allow out from any to 51.159.197.108 port 1194 proto udp
|
|
79
|
+
...
|
|
80
|
+
```
|
|
81
|
+
```ruby
|
|
82
|
+
@manager.ufw_rules # writes file "ufw_rules.txt" to the config files dir. Copy and paste its contents into terminal and hit return to generate the ufw rules.
|
|
83
|
+
```
|
|
84
|
+
|
|
62
85
|
## \_why?
|
|
63
86
|
|
|
64
87
|
I can use Riseup's white-labeled [desktop app](https://leap.se) to access Riseup's VPNs, so why the need?
|
|
65
88
|
|
|
66
|
-
- You can use this gem's Ruby interface to make Riseup's
|
|
89
|
+
- You can use this gem's Ruby interface to make Riseup's VPN configurations available to your apps
|
|
67
90
|
- This approach saves you installing another app on your laptop/desktop (at least on Linux).
|
|
68
91
|
- 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.
|
|
69
|
-
- You can configure your firewall to deny outgoing connections except on port 1194
|
|
70
|
-
- 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
|
|
92
|
+
- You can configure your firewall to deny outgoing connections except on OpenVPN's port (1194). 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.
|
|
93
|
+
- 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.
|
|
71
94
|
|
|
72
95
|
## Development
|
|
73
96
|
|
data/lib/riseup_vpn/api.rb
CHANGED
|
@@ -5,20 +5,21 @@ require 'openssl'
|
|
|
5
5
|
module RiseupVPN
|
|
6
6
|
# parses ca.cert, client cert/key & gateways data from riseup.net API
|
|
7
7
|
module API
|
|
8
|
-
PROVIDER
|
|
9
|
-
|
|
10
|
-
API_VERSION
|
|
11
|
-
|
|
12
|
-
CERT
|
|
13
|
-
|
|
8
|
+
PROVIDER = 'https://riseup.net/provider.json' # entry point for API
|
|
9
|
+
API_URL = 'https://api.black.riseup.net' # 443
|
|
10
|
+
API_VERSION = '3'
|
|
11
|
+
CA_CERT_URL = 'https://black.riseup.net/ca.crt'
|
|
12
|
+
CERT = 'cert' # not available from provider uri..
|
|
13
|
+
CLIENT_KEY_CERT_URL = "#{API_URL}/#{API_VERSION}/#{CERT}".freeze
|
|
14
|
+
EIP = 'eip-service.json' # not available from provider uri..
|
|
14
15
|
|
|
15
16
|
class << self
|
|
16
17
|
def ca_crt
|
|
17
|
-
OpenSSL::X509::Certificate.new get(
|
|
18
|
+
OpenSSL::X509::Certificate.new get(CA_CERT_URL)
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def client_key_and_crt
|
|
21
|
-
str = get(
|
|
22
|
+
str = get(CLIENT_KEY_CERT_URL)
|
|
22
23
|
[OpenSSL::PKey.read(str), OpenSSL::X509::Certificate.new(str)]
|
|
23
24
|
end
|
|
24
25
|
|
|
@@ -27,7 +28,7 @@ module RiseupVPN
|
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
def eip
|
|
30
|
-
get_json File.join(
|
|
31
|
+
get_json File.join(API_URL, API_VERSION, EIP)
|
|
31
32
|
end
|
|
32
33
|
|
|
33
34
|
def get(url)
|
data/lib/riseup_vpn/generator.rb
CHANGED
|
@@ -10,9 +10,9 @@ module RiseupVPN
|
|
|
10
10
|
class Generator
|
|
11
11
|
include Utils
|
|
12
12
|
|
|
13
|
-
TEMPLATE
|
|
14
|
-
OPTS
|
|
15
|
-
LINE_METHODS = %i[remote
|
|
13
|
+
TEMPLATE = YAML.load_file File.join(__dir__, 'template_ovpn.yml')
|
|
14
|
+
OPTS = { proto: %w[udp tcp] }.freeze # default is 1st value
|
|
15
|
+
LINE_METHODS = %i[remote verify-x509-name].freeze
|
|
16
16
|
|
|
17
17
|
attr_reader :ovpns, :ca_crt, :client_key, :client_crt
|
|
18
18
|
|
|
@@ -78,10 +78,6 @@ module RiseupVPN
|
|
|
78
78
|
"remote #{gateway['ip_address']} #{@opts[:proto] == 'udp' ? 1194 : 80}" # TODO: check port 80 in json
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
-
def tls_version_min(_gateway)
|
|
82
|
-
"tls-version-min #{Utils.tls_version}"
|
|
83
|
-
end
|
|
84
|
-
|
|
85
81
|
def verify_x509_name(gateway)
|
|
86
82
|
"verify-x509-name #{name(gateway)} name"
|
|
87
83
|
end
|
data/lib/riseup_vpn/manager.rb
CHANGED
|
@@ -5,11 +5,19 @@ require_relative 'generator'
|
|
|
5
5
|
module RiseupVPN
|
|
6
6
|
# manages config files
|
|
7
7
|
class Manager
|
|
8
|
-
|
|
9
|
-
OPTS
|
|
8
|
+
DEFAULT_CONFIG_DIR = '~/.config/riseup_vpn'
|
|
9
|
+
OPTS = Generator::OPTS
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
UFW_RULES_FILE = 'ufw_rules.txt'
|
|
12
|
+
REMOTE_ = 'remote '
|
|
13
|
+
PROTO_ = 'proto '
|
|
14
|
+
UFW_RULE_PREFIX = 'sudo ufw allow out from any to '
|
|
15
|
+
|
|
16
|
+
attr_reader :config_dir
|
|
17
|
+
|
|
18
|
+
def initialize(config_dir = DEFAULT_CONFIG_DIR, opts = {})
|
|
19
|
+
@config_dir = File.expand_path config_dir.to_s
|
|
20
|
+
create_config_dir
|
|
13
21
|
@generator = Generator.new(opts)
|
|
14
22
|
end
|
|
15
23
|
|
|
@@ -22,33 +30,58 @@ module RiseupVPN
|
|
|
22
30
|
write_ca_crt
|
|
23
31
|
write_client_key
|
|
24
32
|
write_client_crt
|
|
25
|
-
rescue Errno::ENOENT, Errno::EACCES
|
|
26
|
-
raise ArgumentError, "Dir #{@dir} doesn't exist or isn't writable"
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
def import_ovpn_configs
|
|
30
|
-
raise
|
|
36
|
+
raise NoBinaryError, 'nmcli does not appear to be installed' unless Nmcli.installed?
|
|
31
37
|
|
|
32
38
|
write_files
|
|
33
|
-
Dir["#{
|
|
39
|
+
Dir["#{config_dir}/*.ovpn"].each { |f| Nmcli.import_openvpn f }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def ufw_rules
|
|
43
|
+
File.write(File.join(config_dir, UFW_RULES_FILE), ovpns.values.map { |ovpn| ufw_rule_for(ovpn) }.join("\n"))
|
|
34
44
|
end
|
|
35
45
|
|
|
36
46
|
private
|
|
37
47
|
|
|
48
|
+
def create_config_dir
|
|
49
|
+
FileUtils.mkdir_p config_dir unless File.directory? config_dir
|
|
50
|
+
rescue Errno::EACCES
|
|
51
|
+
raise ArgumentError, "Cannot create #{config_dir}, please provide a valid dir path or create it yourself"
|
|
52
|
+
end
|
|
53
|
+
|
|
38
54
|
def write_ovpn_files
|
|
39
|
-
@generator.ovpns.each { |k, v| File.write(File.join(
|
|
55
|
+
@generator.ovpns.each { |k, v| File.write(File.join(config_dir, "#{k}.ovpn"), v) }
|
|
40
56
|
end
|
|
41
57
|
|
|
42
58
|
def write_ca_crt
|
|
43
|
-
File.write File.join(
|
|
59
|
+
File.write File.join(config_dir, 'ca.crt'), @generator.ca_crt.to_s.chomp
|
|
44
60
|
end
|
|
45
61
|
|
|
46
62
|
def write_client_key
|
|
47
|
-
File.write File.join(
|
|
63
|
+
File.write File.join(config_dir, 'client.key'), @generator.client_key.to_s.chomp
|
|
48
64
|
end
|
|
49
65
|
|
|
50
66
|
def write_client_crt
|
|
51
|
-
File.write File.join(
|
|
67
|
+
File.write File.join(config_dir, 'client.crt'), @generator.client_crt.to_s.chomp
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def ufw_rule_for(multiline_str)
|
|
71
|
+
rule_str(*ip_port_proto(multiline_str))
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def rule_str(ip, port, proto)
|
|
75
|
+
"#{UFW_RULE_PREFIX}#{ip} port #{port} proto #{proto}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def ip_port_proto(multiline_str)
|
|
79
|
+
h = hashify_config(multiline_str)
|
|
80
|
+
h['remote'] + h['proto']
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def hashify_config(multiline_str)
|
|
84
|
+
multiline_str.split("\n").map(&:split).inject({}) { |memo, arr| memo.merge({ arr[0] => arr[1..] }) }
|
|
52
85
|
end
|
|
53
86
|
end
|
|
54
87
|
end
|
data/lib/riseup_vpn/nmcli.rb
CHANGED
data/lib/riseup_vpn/utils.rb
CHANGED
data/lib/riseup_vpn/version.rb
CHANGED
data/lib/riseup_vpn.rb
CHANGED
|
@@ -4,7 +4,7 @@ require 'json'
|
|
|
4
4
|
require 'open-uri'
|
|
5
5
|
|
|
6
6
|
require_relative 'riseup_vpn/api'
|
|
7
|
-
require_relative 'riseup_vpn/
|
|
7
|
+
require_relative 'riseup_vpn/error'
|
|
8
8
|
require_relative 'riseup_vpn/generator'
|
|
9
9
|
require_relative 'riseup_vpn/manager'
|
|
10
10
|
require_relative 'riseup_vpn/nmcli'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: riseup_vpn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- MatzFan
|
|
@@ -23,7 +23,7 @@ files:
|
|
|
23
23
|
- assets/vpns.png
|
|
24
24
|
- lib/riseup_vpn.rb
|
|
25
25
|
- lib/riseup_vpn/api.rb
|
|
26
|
-
- lib/riseup_vpn/
|
|
26
|
+
- lib/riseup_vpn/error.rb
|
|
27
27
|
- lib/riseup_vpn/generator.rb
|
|
28
28
|
- lib/riseup_vpn/manager.rb
|
|
29
29
|
- lib/riseup_vpn/nmcli.rb
|
|
@@ -47,14 +47,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
47
47
|
requirements:
|
|
48
48
|
- - ">="
|
|
49
49
|
- !ruby/object:Gem::Version
|
|
50
|
-
version: 3.
|
|
50
|
+
version: 3.2.0
|
|
51
51
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
52
|
requirements:
|
|
53
53
|
- - ">="
|
|
54
54
|
- !ruby/object:Gem::Version
|
|
55
55
|
version: '0'
|
|
56
56
|
requirements: []
|
|
57
|
-
rubygems_version:
|
|
57
|
+
rubygems_version: 4.0.6
|
|
58
58
|
specification_version: 4
|
|
59
59
|
summary: RiseupVPN configuration
|
|
60
60
|
test_files: []
|