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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9fb7defcb7a949292d44dc949acfdc4524eb599a21ec1c90f5f1680c8b9d0ff
4
- data.tar.gz: dcbb98a3d236a6bf35d3fab3f073549836e98d976a747a4dd134417f6f3ad43e
3
+ metadata.gz: 6aadf2b162813b7cc25700ac2352eda76e899498d25f8566ef6bb51b7c7aadbd
4
+ data.tar.gz: fbb7f9bb058abcf6c48e4a1d993ac62bd9bfcefacf67673821222abbc0a398bb
5
5
  SHA512:
6
- metadata.gz: 01bf804c22e6edde11abfd61073f3a9093d33b9463e05f5456aa66f7537e44e19ef37708979499ca8193b05045439acdba5b3a46337ef4edc219053e9603e373
7
- data.tar.gz: 219ee6e1de2521e85e94a558e28dda6347b3cb74e288bb293f04a86599975f76e00f4477b1d73493562403971f2e0435ff9fb4bde77404a774a690bd489dbd7f
6
+ metadata.gz: 628be1b80961627205b0888eb827f074bf1b2227fd06b41e1103e8871560cfafd9a04ba9077ea0cd7582f819577e68e9341a4d8eb713d4ab1b794afdf2cb83ab
7
+ data.tar.gz: 14d196bb02bcf7a457c4ee4d7954b356c3fa3ae9d3d8d78225286c56fd4eaaf0f75d3a17b4d8ab9f1580d6448557bb1389943f7f98ab6bc583c911932a8d28c8
data/.rubocop.yml CHANGED
@@ -5,7 +5,7 @@ plugins:
5
5
  - rubocop-rake
6
6
 
7
7
  AllCops:
8
- TargetRubyVersion: 3.1
8
+ TargetRubyVersion: 3.2
9
9
  NewCops: enable
10
10
 
11
11
  Style/HashSyntax:
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: The last 2 expire after 30 days and therefore need to be updated regularly.
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
- Also note: OpenVPN requires that cert & key files shared across .ovpn client config files are present in the same directory.
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 VPNs available to your apps!
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 (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.
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 and we are working into incorporating that functionality into this gem.
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
 
@@ -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 = 'https://riseup.net/provider.json' # entry point for API
9
- API_URI = 'https://api.black.riseup.net' # 443
10
- API_VERSION = '3'
11
- CA_CERT_URI = 'https://black.riseup.net/ca.crt'
12
- CERT = 'cert' # not available from provider uri..
13
- EIP = 'eip-service.json' # not available from provider uri..
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(CA_CERT_URI)
18
+ OpenSSL::X509::Certificate.new get(CA_CERT_URL)
18
19
  end
19
20
 
20
21
  def client_key_and_crt
21
- str = get(File.join(API_URI, API_VERSION, CERT))
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(API_URI, API_VERSION, EIP)
31
+ get_json File.join(API_URL, API_VERSION, EIP)
31
32
  end
32
33
 
33
34
  def get(url)
@@ -4,4 +4,5 @@ module RiseupVPN
4
4
  class Error < StandardError; end
5
5
 
6
6
  class APIError < Error; end
7
+ class NoBinaryError < Error; end
7
8
  end
@@ -10,9 +10,9 @@ module RiseupVPN
10
10
  class Generator
11
11
  include Utils
12
12
 
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 tls-version-min verify-x509-name].freeze
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
@@ -5,11 +5,19 @@ require_relative 'generator'
5
5
  module RiseupVPN
6
6
  # manages config files
7
7
  class Manager
8
- DEFAULT_DIR = '~/.config/riseup_vpn'
9
- OPTS = Generator::OPTS
8
+ DEFAULT_CONFIG_DIR = '~/.config/riseup_vpn'
9
+ OPTS = Generator::OPTS
10
10
 
11
- def initialize(dir = DEFAULT_DIR, opts = {})
12
- @dir = File.expand_path dir.to_s
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 NoMethodError, 'nmcli does not appear to be installed' unless Utils.nmcli?
36
+ raise NoBinaryError, 'nmcli does not appear to be installed' unless Nmcli.installed?
31
37
 
32
38
  write_files
33
- Dir["#{@dir}/*.ovpn"].each { |f| Nmcli.import_openvpn f }
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(@dir, "#{k}.ovpn"), v) }
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(@dir, 'ca.crt'), @generator.ca_crt.to_s.chomp
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(@dir, 'client.key'), @generator.client_key.to_s.chomp
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(@dir, 'client.crt'), @generator.client_crt.to_s.chomp
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
@@ -5,6 +5,10 @@ require_relative 'generator'
5
5
  module RiseupVPN
6
6
  # in the absence of an nmcli gem..
7
7
  module Nmcli
8
+ def self.installed?
9
+ !`which nmcli`.empty?
10
+ end
11
+
8
12
  def self.nmcli(command_string)
9
13
  `nmcli #{command_string}`
10
14
  end
@@ -13,13 +13,5 @@ module RiseupVPN
13
13
 
14
14
  raise Error, "Unsupported OS: '#{os}'"
15
15
  end
16
-
17
- def self.tls_version
18
- "1.#{OpenSSL::VERSION.to_i}".to_f
19
- end
20
-
21
- def self.nmcli?
22
- !`which nmcli`.empty?
23
- end
24
16
  end
25
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RiseupVPN
4
- VERSION = '1.1.0'
4
+ VERSION = '1.2.0'
5
5
  end
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/errors'
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.1.0
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/errors.rb
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.1.0
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: 3.6.7
57
+ rubygems_version: 4.0.6
58
58
  specification_version: 4
59
59
  summary: RiseupVPN configuration
60
60
  test_files: []