ovpnmcgen.rb 0.0.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a7eecb8a62bc004485044c5ffcc87d38684dc999
4
- data.tar.gz: 1c1dec6d4e95b295104435550d443c5aecf6bfef
3
+ metadata.gz: 367d863a65d76ef7131544d0cfeabe155b43ada8
4
+ data.tar.gz: 638bd7fb996c3f265005992d0d452d41fceb1ab3
5
5
  SHA512:
6
- metadata.gz: d165dffd3e79a28e59292811e3dca883ffff50de6e44b615ee4c6e6e05fd0a789c073441d81759b3359afa1d71e0b5a03c3eb0903127f35a55dd2b25aa10688c
7
- data.tar.gz: 80e881f3de0dba88a691d6391e049a3b6f944fefb4789746e0dce8467fd9b0e52d6396878fd0b9a14be7fafa88730441ccb71a4bdc5e1260d3ed1bca7269ebc2
6
+ metadata.gz: 11d4d579cb0a28ed0536afb99ba0c98c88c97923c0c1deaa7acc4dedd01dc8ef24f315a0bcc2fa2164511216c65164b39bdcdd7c97b6506ef7078e715aace314
7
+ data.tar.gz: f981db017988637593dc2b883531014040fefbe4b0846187a330519960a2e310690c757bd0e1a9020253a16dd139f4f0aa6cdbff8491ced055da851b1c27cd46
data/.gitignore CHANGED
@@ -16,3 +16,5 @@ spec/reports
16
16
  test/tmp
17
17
  test/version_tmp
18
18
  tmp
19
+ .ruby-version
20
+ .ruby-gemset
data/ChangeLog ADDED
@@ -0,0 +1,11 @@
1
+ = 0.1.0 / 2014-03-27
2
+ * Added support for `--ovpnconfigfile`, `--port`, `--proto`.
3
+ * Shorter switches for `--[un]trusted-ssids`.
4
+ * Improved Documentation.
5
+
6
+ = 0.0.2 / 2014-03-26
7
+ * Require ruby >= 1.9.3.
8
+ * Improved Documentation.
9
+
10
+ = 0.0.1 / 2014-03-26
11
+ * Initial Release.
data/README.md CHANGED
@@ -2,16 +2,56 @@
2
2
 
3
3
  OpenVPN iOS Configuration Profile Utility
4
4
 
5
- This utility generates configuration profiles that enables VPN-on-Demand, as documented by Apple in <https://developer.apple.com/library/ios/featuredarticles/iPhoneConfigurationProfileRef/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010206-CH1-SW27>.
5
+ Generates iOS configuration profiles (.mobileconfig) that configures OpenVPN for use with VPN-on-Demand that are not accessible through the Apple Configurator or the iPhone Configuration Utility.
6
+
7
+ Although there are many possible VPN-on-Demand (VoD) triggers, this utility currently only implements `SSIDMatch` and `InterfaceTypeMatch`. The following algorithm is executed upon network changes, in order:
8
+
9
+ - If wireless SSID matches any specified with `--trusted-ssids`, tear down the VPN connection and do not reconnect on demand.
10
+ - Else if wireless SSID matches any specified with `--untrusted-ssids`, unconditionally bring up the VPN connection on the next network attempt.
11
+ - Else if the primary network interface becomes Wifi (any SSID except those above), bring up the VPN connection.
12
+ - Else if the primary network interface becomes Cellular, leave any existing VPN connection up, but do not reconnect on demand.
13
+ - Else, unconditionally bring up the VPN connection on the next network attempt.
14
+
15
+ Note: The other match triggers, such as `DNSDomainMatch`, `DNSServerAddressMatch`, `URLStringProbe`, and per-connection domain inspection (`ActionParameters`), are not implemented. I reckon some kind of DSL will need to be built to support them; pull-requests are welcome.
6
16
 
7
17
  ## Installation
8
18
 
9
- Install it yourself as:
19
+ Install the production version from Rubygems.org:
10
20
 
11
21
  $ gem install ovpnmcgen.rb
12
22
 
23
+ ### Local Development
24
+
25
+ Clone the source:
26
+
27
+ $ git clone https://github.com/iphoting/ovpnmcgen.rb
28
+
29
+ Build and install the gem:
30
+
31
+ $ cd ovpnmcgen.rb/
32
+ $ bundle install # install dependencies
33
+ # Hack away...
34
+ $ rake install # build and install gem
35
+
13
36
  ## Usage
14
37
 
38
+ ```
39
+ Usage: ovpnmcgen.rb generate [options] <user> <device>
40
+
41
+ Options:
42
+ --cafile FILE Path to OpenVPN CA file. (Required)
43
+ --tafile FILE Path to TLS Key file. (Required)
44
+ --host HOSTNAME Hostname of OpenVPN server. (Required)
45
+ --p12file FILE Path to user PKCS#12 file. (Required)
46
+ --p12pass PASSWORD Password to unlock PKCS#12 file.
47
+ --[no-]vod Enable or Disable VPN-On-Demand. [Default: Enabled]
48
+ --trusted-ssids SSIDS List of comma-separated trusted SSIDs.
49
+ --untrusted-ssids SSIDS List of comma-separated untrusted SSIDs.
50
+ -o, --output FILE Output to file. [Default: stdout]
51
+ ```
52
+
53
+ ## Examples
54
+
15
55
  ### Typical Usage
16
56
  $ ovpnmcgen.rb gen --trusted-ssids home --host vpn.example.com \
17
57
  --cafile path/to/ca.pem --tafile path/to/ta.key \
@@ -225,10 +265,29 @@ Output similar to above:
225
265
  </plist>
226
266
  ```
227
267
 
228
- ### Using OpenSSL to generate a PKCS#12 file
268
+ ### Using OpenSSL to convert files into PKCS#12 (.p12)
229
269
  openssl pkcs12 -export -out path/to/john-ipad.p12 \
230
270
  -inkey path/to/john-ipad.key -in path/to/john-ipad.crt \
231
- -passout pass:p12passphrase
271
+ -passout pass:p12passphrase -name john-ipad@vpn.example.com
272
+
273
+ ## TODO
274
+
275
+ - Config file to specify global options, such as `--cafile`, `--tafile`, `--host`, `--[un]trusted-ssids`.
276
+ - Batch-operation mode, with CSV-file as input, and a CSV UUID-index file to track generated profiles as output.
277
+
278
+ The same UUID should be used for profile updates, so that iOS knows which profile to replace, especially in MDM environments.
279
+
280
+ - Adopt OpenVPN parameters from an OpenVPN-compatible client.conf input file.
281
+
282
+ Implemented, but lacks support for inline `<ca|tls-auth>` data enclosures.
283
+
284
+ - Sign/Encrypt .mobileconfig.
285
+
286
+ Current workaround is to use a trusted MDM solution to securely push these unsigned, unencrypted profiles to iOS devices, through the encrypted MDM connected.
287
+
288
+ ## References
289
+
290
+ - Apple, at <https://developer.apple.com/library/ios/featuredarticles/iPhoneConfigurationProfileRef/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010206-CH1-SW27>.
232
291
 
233
292
  ## Contributing
234
293
 
data/bin/ovpnmcgen.rb CHANGED
@@ -13,44 +13,51 @@ never_trace!
13
13
 
14
14
  command :generate do |c|
15
15
  c.syntax = 'ovpnmcgen.rb generate [options] <user> <device>'
16
- c.summary = 'Generates iOS Configuration Profiles'
17
- c.description = 'Generates a .mobileconfig plist.'
16
+ c.summary = 'Generates iOS Configuration Profiles (.mobileconfig)'
17
+ c.description = 'Generates iOS configuration profiles (.mobileconfig) that configures OpenVPN for use with VPN-on-Demand that are not accessible through the Apple Configurator or the iPhone Configuration Utility.'
18
18
  c.example 'Typical Usage', 'ovpnmcgen.rb gen --trusted-ssids home --host vpn.example.com --cafile path/to/ca.pem --tafile path/to/ta.key --p12file path/to/john-ipad.p12 --p12pass p12passphrase john ipad'
19
19
  c.example 'Extended Usage', 'ovpnmcgen.rb gen --trusted-ssids home,school --untrusted-ssids virusnet --host vpn.example.com --cafile path/to/ca.pem --tafile path/to/ta.key --p12file path/to/john-ipad.p12 --p12pass p12passphrase john ipad'
20
- c.example 'Creating a .p12 file using OpenSSL', 'openssl pkcs12 -export -out path/to/john-ipad.p12 -inkey path/to/john-ipad.key -in path/to/john-ipad.crt -passout pass:p12passphrase'
20
+ c.example 'Using OpenSSL to convert files into PKCS#12 (.p12)', 'openssl pkcs12 -export -out path/to/john-ipad.p12 -inkey path/to/john-ipad.key -in path/to/john-ipad.crt -passout pass:p12passphrase -name john-ipad@vpn.example.com'
21
21
  c.option '--cafile FILE', 'Path to OpenVPN CA file. (Required)'
22
22
  c.option '--tafile FILE', 'Path to TLS Key file. (Required)'
23
23
  c.option '--host HOSTNAME', 'Hostname of OpenVPN server. (Required)'
24
+ c.option '--proto PROTO', 'OpenVPN server protocol. [Default: udp]'
25
+ c.option '-p', '--port PORT', 'OpenVPN server port. [Default: 1194]'
24
26
  c.option '--p12file FILE', 'Path to user PKCS#12 file. (Required)'
25
27
  c.option '--p12pass PASSWORD', 'Password to unlock PKCS#12 file.'
26
28
  c.option '--[no-]vod', 'Enable or Disable VPN-On-Demand. [Default: Enabled]'
27
- c.option '--trusted-ssids SSIDS', Array, 'List of comma-separated trusted SSIDs.'
28
- c.option '--untrusted-ssids SSIDS', Array, 'List of comma-separated untrusted SSIDs.'
29
+ c.option '-t', '--trusted-ssids SSIDS', Array, 'List of comma-separated trusted SSIDs.'
30
+ c.option '-u', '--untrusted-ssids SSIDS', Array, 'List of comma-separated untrusted SSIDs.'
31
+ c.option '--ovpnconfigfile FILE', 'Path to OpenVPN client config file.'
29
32
  c.option '-o', '--output FILE', 'Output to file. [Default: stdout]'
30
33
  c.action do |args, options|
31
- raise ArgumentError.new "Invalid arguments. Run #{File.basename(__FILE__)} help for guidance." if args.nil? or args.length < 2
32
- raise ArgumentError.new "Host is required." unless options.host
33
- raise ArgumentError.new "cafile is required." unless options.cafile
34
- raise ArgumentError.new "tafile is required." unless options.tafile
35
- raise ArgumentError.new "PKCS#12 file is required." unless options.p12file
36
- options.default :vod => true
37
- user, device, p12file, p12pass = args
38
- inputs = {
39
- :user => user,
40
- :device => device,
41
- :p12file => options.p12file,
42
- :p12pass => options.p12pass,
43
- :cafile => options.cafile,
44
- :tafile => options.tafile,
45
- :host => options.host,
46
- :enableVOD => options.vod,
47
- :trusted_ssids => options.trusted_ssids,
48
- :untrusted_ssids => options.untrusted_ssids
49
- }
50
- unless options.output
51
- puts Ovpnmcgen.generate(inputs)
34
+ raise ArgumentError.new "Invalid arguments. Run '#{File.basename(__FILE__)} help generate' for guidance" if args.nil? or args.length < 2
35
+ raise ArgumentError.new "Host is required" unless options.host
36
+ raise ArgumentError.new "cafile is required" unless options.cafile
37
+ raise ArgumentError.new "tafile is required" unless options.tafile
38
+ raise ArgumentError.new "PKCS#12 file is required" unless options.p12file
39
+ options.default :vod => true, :proto => 'udp', :port => 1194
40
+ user, device, p12file, p12pass = args
41
+ inputs = {
42
+ :user => user,
43
+ :device => device,
44
+ :p12file => options.p12file,
45
+ :p12pass => options.p12pass,
46
+ :cafile => options.cafile,
47
+ :tafile => options.tafile,
48
+ :host => options.host,
49
+ :proto => options.proto,
50
+ :port => options.port,
51
+ :enableVOD => options.vod,
52
+ :trusted_ssids => options.trusted_ssids,
53
+ :untrusted_ssids => options.untrusted_ssids
54
+ }
55
+ inputs[:ovpnconfigfile] = options.ovpnconfigfile if options.ovpnconfigfile
56
+
57
+ unless options.output
58
+ puts Ovpnmcgen.generate(inputs)
52
59
  else
53
- # write to file
60
+ # write to file
54
61
  begin
55
62
  File.write(options.output, Ovpnmcgen.generate(inputs))
56
63
  rescue Errno::ENOENT
data/lib/ovpnmcgen.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "ovpnmcgen/version"
2
+ require "ovpnmcgen/ovpnconfig"
2
3
  require 'plist'
3
4
  require 'base64'
4
5
 
@@ -13,8 +14,7 @@ module Ovpnmcgen
13
14
  identifier = inputs[:identifier] || inputs[:host].split('.').reverse!.join('.')
14
15
  port = inputs[:port] || 1194
15
16
  certUUID = inputs[:certUUID] || `uuidgen`.chomp.upcase
16
- user, device, domain, host = inputs[:user], inputs[:device], inputs[:host], inputs[:host]
17
- enableVOD = inputs[:enableVOD]
17
+ user, device, domain, host, proto, enableVOD = inputs[:user], inputs[:device], inputs[:host], inputs[:host], inputs[:proto], inputs[:enableVOD]
18
18
  p12pass = inputs[:p12pass] || ''
19
19
  trusted_ssids = inputs[:trusted_ssids] || false
20
20
  untrusted_ssids = inputs[:untrusted_ssids] || false
@@ -40,6 +40,21 @@ module Ovpnmcgen
40
40
  exit
41
41
  end
42
42
 
43
+ unless inputs[:ovpnconfigfile].nil?
44
+ ovpnconfighash = Ovpnmcgen.getOVPNVendorConfigHash(inputs[:ovpnconfigfile])
45
+ else # Bare minimum configuration
46
+ ovpnconfighash = {
47
+ 'client' => 'NOARGS',
48
+ 'comp-lzo' => 'NOARGS',
49
+ 'dev' => 'tun',
50
+ 'key-direction' => '1',
51
+ 'remote-cert-tls' => 'server'
52
+ }
53
+ end
54
+ ovpnconfighash['remote'] = "#{host} #{port} #{proto}"
55
+ ovpnconfighash['ca'] = ca_cert
56
+ ovpnconfighash['tls-auth'] = tls_auth
57
+
43
58
  vpnOnDemandRules = Array.new
44
59
  vodTrusted = { # Trust only Wifi SSID
45
60
  'SSIDMatch' => trusted_ssids,
@@ -93,21 +108,7 @@ module Ovpnmcgen
93
108
  },
94
109
  'VPNSubType' => 'net.openvpn.OpenVPN-Connect.vpnplugin',
95
110
  'VPNType' => 'VPN',
96
- 'VendorConfig' => {
97
- 'ca' => ca_cert,
98
- 'client' => 'NOARGS',
99
- 'comp-lzo' => 'NOARGS',
100
- 'dev' => 'tun',
101
- 'key-direction' => '1',
102
- 'persist-key' => 'NOARGS',
103
- 'persist-tun' => 'NOARGS',
104
- 'proto' => 'udp',
105
- 'remote' => "#{host} #{port} udp",
106
- 'remote-cert-tls' => 'server',
107
- 'resolv-retry' => 'infinite',
108
- 'tls-auth' => tls_auth,
109
- 'verb' => '3'
110
- }
111
+ 'VendorConfig' => ovpnconfighash
111
112
  }
112
113
 
113
114
  plistPayloadContent = [vpn, cert] # to encrypt this array
@@ -0,0 +1,51 @@
1
+
2
+ module Ovpnmcgen
3
+ def getOVPNVendorConfigHash(ovpnfilepath)
4
+ ovpnfile = ""
5
+ begin
6
+ # get rid of comments in the file.
7
+ ovpnfile = File.readlines(ovpnfilepath).delete_if { |l| ; l.start_with?('#') or l.start_with?(';') or l.chomp.empty? }
8
+
9
+ # TODO: [Warning] Get rid of/handle <ca>...</ca>, <cert>...</cert>, <key>...</key>, <tls-auth>...</tls-auth> inline cert/key enclosures.
10
+ # Bail when inline cert/key enclosures are detected.
11
+ ovpnfile.each do |l|
12
+ r = l.chomp.match(/<.*>/)
13
+ raise ArgumentError.new "OpenVPN client config file contains inline data enclosures: #{r.to_a.join(', ')}!\nSuch files are not yet supported. Remove them and try again" unless r.nil?
14
+ end
15
+
16
+ # TODO: Handle multiple remote lines.
17
+ # Currently, the last remote line will be used.
18
+
19
+ # map to key => value pairs for plist purposes. Singular verbs will be: 'verb' => 'NOARGS'.
20
+ ovpnhash = Hash[ovpnfile.map do |l|
21
+ a = l.split
22
+ if a.length == 1
23
+ a << "NOARGS"
24
+ elsif a.length > 2
25
+ b = a.take(1)
26
+ c = a.drop(1).join ' '
27
+ a.replace(b << c)
28
+ end
29
+ a
30
+ end]
31
+
32
+ # delete obviously unsupported keys.
33
+ ovpnhash.delete_if do |key, value|
34
+ case key
35
+ when 'fragment', 'mssfix', 'secret', 'socks-proxy', 'persist-key', 'persist-tun', 'resolv-retry', 'nobind', 'verb', 'user', 'group', 'pull', 'mute'
36
+ true
37
+ when 'remote', 'ca', 'pkcs12', 'tls-auth', 'cert', 'key', 'proto' # specified with switches.
38
+ true
39
+ else
40
+ false
41
+ end
42
+ end
43
+ rescue Errno::ENOENT
44
+ puts "OpenVPN config file not found: #{ovpnfilepath}!"
45
+ exit
46
+ end
47
+ return ovpnhash
48
+ end
49
+
50
+ module_function :getOVPNVendorConfigHash
51
+ end
@@ -1,4 +1,4 @@
1
1
  module Ovpnmcgen
2
- VERSION = "0.0.2"
3
- SUMMARY = "An OpenVPN iOS Configuration Profile Utility"
2
+ VERSION = "0.1.0"
3
+ SUMMARY = "An OpenVPN iOS Configuration Profile (.mobileconfig) Utility"
4
4
  end
data/ovpnmcgen.rb.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Ronald Ip"]
10
10
  spec.email = ["myself@iphoting.com"]
11
11
  spec.summary = Ovpnmcgen::SUMMARY
12
- spec.description = "This utility generates configuration profiles that enables VPN-on-Demand."
12
+ spec.description = "Generates iOS configuration profiles (.mobileconfig) that configures OpenVPN for use with VPN-on-Demand that are not accessible through the Apple Configurator or the iPhone Configuration Utility."
13
13
  spec.homepage = ""
14
14
  spec.license = "MIT"
15
15
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ovpnmcgen.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ronald Ip
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-26 00:00:00.000000000 Z
11
+ date: 2014-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,7 +66,9 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '4.1'
69
- description: This utility generates configuration profiles that enables VPN-on-Demand.
69
+ description: Generates iOS configuration profiles (.mobileconfig) that configures
70
+ OpenVPN for use with VPN-on-Demand that are not accessible through the Apple Configurator
71
+ or the iPhone Configuration Utility.
70
72
  email:
71
73
  - myself@iphoting.com
72
74
  executables:
@@ -75,14 +77,14 @@ extensions: []
75
77
  extra_rdoc_files: []
76
78
  files:
77
79
  - ".gitignore"
78
- - ".ruby-gemset"
79
- - ".ruby-version"
80
+ - ChangeLog
80
81
  - Gemfile
81
82
  - LICENSE.txt
82
83
  - README.md
83
84
  - Rakefile
84
85
  - bin/ovpnmcgen.rb
85
86
  - lib/ovpnmcgen.rb
87
+ - lib/ovpnmcgen/ovpnconfig.rb
86
88
  - lib/ovpnmcgen/version.rb
87
89
  - ovpnmcgen.rb.gemspec
88
90
  homepage: ''
@@ -108,5 +110,5 @@ rubyforge_project:
108
110
  rubygems_version: 2.2.2
109
111
  signing_key:
110
112
  specification_version: 4
111
- summary: An OpenVPN iOS Configuration Profile Utility
113
+ summary: An OpenVPN iOS Configuration Profile (.mobileconfig) Utility
112
114
  test_files: []
data/.ruby-gemset DELETED
@@ -1 +0,0 @@
1
- ovpnmcgen
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- ruby-2.1.1