ovpnmcgen.rb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +30 -0
- data/Rakefile +1 -0
- data/bin/ovpnmcgen.rb +64 -0
- data/lib/ovpnmcgen.rb +133 -0
- data/lib/ovpnmcgen/version.rb +4 -0
- data/ovpnmcgen.rb.gemspec +26 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1c5cb5752cb5bbfd9b9db08e87949e0807c250c3
|
4
|
+
data.tar.gz: 349c51df8ade0b4d5aea93276068dd491c379c67
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c37390ffe65b3b17a535aa0a434663e63fba3a8d8f2415e7e130dbb78acef5d6d98bf091d17ccfc9ef22ab33fb54801d8e552deb4745f91650a5976de328e67c
|
7
|
+
data.tar.gz: a7b2706e22de7978969a5327278978d869e7a5eb649f6c5b9e2c97c945a13723637c1e5a09f2dca2c1cfa426f284690a06b5bd75b4577ad8bf5d1c104401b4fd
|
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ovpnmcgen
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.1.1
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Ronald Ip
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Ovpnmcgen.rb
|
2
|
+
|
3
|
+
OpenVPN iOS Configuration Profile Utility
|
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>.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Install it yourself as:
|
10
|
+
|
11
|
+
$ gem install ovpnmcgen.rb
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
### Typical Usage
|
16
|
+
$ 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
|
17
|
+
|
18
|
+
### Extended Usage
|
19
|
+
$ 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
|
+
|
21
|
+
### Using OpenSSL to generate a PKCS#12 file
|
22
|
+
openssl pkcs12 -export -out path/to/john-ipad.p12 -inkey path/to/john-ipad.key -in path/to/john-ipad.crt -passout pass:p12passphrase
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
|
26
|
+
1. Fork it (<http://github.com/iphoting/ovpnmcgen.rb/fork>)
|
27
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
28
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
29
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
30
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/ovpnmcgen.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'ovpnmcgen'
|
4
|
+
require 'commander/import'
|
5
|
+
|
6
|
+
program :version, Ovpnmcgen::VERSION
|
7
|
+
program :description, Ovpnmcgen::SUMMARY
|
8
|
+
program :help, 'Usage', 'ovpnmcgen.rb <command> [options] <args...>'
|
9
|
+
program :help_formatter, :compact
|
10
|
+
default_command :help
|
11
|
+
never_trace!
|
12
|
+
#global_option '-c', '--config FILE', 'Specify path to config file' #not implemented yet
|
13
|
+
|
14
|
+
command :generate do |c|
|
15
|
+
c.syntax = 'ovpnmcgen.rb generate [options] <user> <device>'
|
16
|
+
c.summary = 'Generates iOS Configuration Profiles'
|
17
|
+
c.description = 'Generates a .mobileconfig plist.'
|
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
|
+
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'
|
21
|
+
c.option '--cafile FILE', 'Path to OpenVPN CA file. (Required)'
|
22
|
+
c.option '--tafile FILE', 'Path to TLS Key file. (Required)'
|
23
|
+
c.option '--host HOSTNAME', 'Hostname of OpenVPN server. (Required)'
|
24
|
+
c.option '--p12file FILE', 'Path to user PKCS#12 file. (Required)'
|
25
|
+
c.option '--p12pass PASSWORD', 'Password to unlock PKCS#12 file.'
|
26
|
+
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 '-o', '--output FILE', 'Output to file. [Default: stdout]'
|
30
|
+
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)
|
52
|
+
else
|
53
|
+
# write to file
|
54
|
+
begin
|
55
|
+
File.write(options.output, Ovpnmcgen.generate(inputs))
|
56
|
+
rescue Errno::ENOENT
|
57
|
+
puts "Error writing to: #{options.output}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
alias_command :g, :generate
|
64
|
+
alias_command :gen, :generate
|
data/lib/ovpnmcgen.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
require "ovpnmcgen/version"
|
2
|
+
require 'plist'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
module Ovpnmcgen
|
6
|
+
class StringData < String
|
7
|
+
def to_plist_node
|
8
|
+
return "<data>\n#{self}\n</data>"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate(inputs = {})
|
13
|
+
identifier = inputs[:identifier] || inputs[:host].split('.').reverse!.join('.')
|
14
|
+
port = inputs[:port] || 1194
|
15
|
+
certUUID = inputs[:certUUID] || `uuidgen`.chomp.upcase
|
16
|
+
user, device, domain, host = inputs[:user], inputs[:device], inputs[:host], inputs[:host]
|
17
|
+
enableVOD = inputs[:enableVOD]
|
18
|
+
p12pass = inputs[:p12pass] || ''
|
19
|
+
trusted_ssids = inputs[:trusted_ssids] || false
|
20
|
+
untrusted_ssids = inputs[:untrusted_ssids] || false
|
21
|
+
|
22
|
+
begin
|
23
|
+
ca_cert = File.readlines(inputs[:cafile]).map { |x| x.chomp }.join('\n')
|
24
|
+
rescue Errno::ENOENT
|
25
|
+
puts "CA file not found: #{inputs[:cafile]}!"
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
tls_auth = File.readlines(inputs[:tafile]).map { |x| x.chomp }.join('\n')
|
31
|
+
rescue Errno::ENOENT
|
32
|
+
puts "TLS file not found: #{inputs[:tafile]}!"
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
|
36
|
+
begin
|
37
|
+
p12file = Base64.encode64(File.read(inputs[:p12file]))
|
38
|
+
rescue Errno::ENOENT
|
39
|
+
puts "PCKS#12 file not found: #{inputs[:p12file]}!"
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
vpnOnDemandRules = Array.new
|
44
|
+
vodTrusted = { # Trust only Wifi SSID
|
45
|
+
'SSIDMatch' => trusted_ssids,
|
46
|
+
'Action' => 'Disconnect'
|
47
|
+
}
|
48
|
+
vodUntrusted = { # Untrust Wifi
|
49
|
+
'SSIDMatch' => untrusted_ssids,
|
50
|
+
'Action' => 'Connect'
|
51
|
+
}
|
52
|
+
vpnOnDemandRules << vodTrusted if trusted_ssids
|
53
|
+
vpnOnDemandRules << vodUntrusted if untrusted_ssids
|
54
|
+
|
55
|
+
vpnOnDemandRules << { # Untrust all Wifi
|
56
|
+
'InterfaceTypeMatch' => 'WiFi',
|
57
|
+
'Action' => 'Connect'
|
58
|
+
} << { # Trust Cellular
|
59
|
+
'InterfaceTypeMatch' => 'Cellular',
|
60
|
+
'Action' => 'Ignore'
|
61
|
+
} << { # Default catch-all
|
62
|
+
'Action' => 'Connect'
|
63
|
+
}
|
64
|
+
|
65
|
+
cert = {
|
66
|
+
'Password' => p12pass,
|
67
|
+
'PayloadCertificateFileName' => "#{user}-#{device}.p12",
|
68
|
+
'PayloadContent' => StringData.new(p12file),
|
69
|
+
'PayloadDescription' => 'Provides device authentication (certificate or identity).',
|
70
|
+
'PayloadDisplayName' => "#{user}-#{device}.p12",
|
71
|
+
'PayloadIdentifier' => "#{identifier}.#{user}-#{device}.credential",
|
72
|
+
'PayloadOrganization' => domain,
|
73
|
+
'PayloadType' => 'com.apple.security.pkcs12',
|
74
|
+
'PayloadUUID' => certUUID,
|
75
|
+
'PayloadVersion' => 1
|
76
|
+
}
|
77
|
+
|
78
|
+
vpn = {
|
79
|
+
'PayloadDescription' => "Configures VPN settings, including authentication.",
|
80
|
+
'PayloadDisplayName' => "VPN (#{host}/VoD)",
|
81
|
+
'PayloadIdentifier' => "#{identifier}.#{user}-#{device}.vpnconfig",
|
82
|
+
'PayloadOrganization' => domain,
|
83
|
+
'PayloadType' => 'com.apple.vpn.managed',
|
84
|
+
'PayloadUUID' => `uuidgen`.chomp.upcase,
|
85
|
+
'PayloadVersion' => 1,
|
86
|
+
'UserDefinedName' => "#{host}/VoD",
|
87
|
+
'VPN' => {
|
88
|
+
'AuthenticationMethod' => 'Certificate',
|
89
|
+
'OnDemandEnabled' => (enableVOD)? 1 : 0,
|
90
|
+
'OnDemandRules' => vpnOnDemandRules,
|
91
|
+
'PayloadCertificateUUID' => certUUID,
|
92
|
+
'RemoteAddress' => 'DEFAULT'
|
93
|
+
},
|
94
|
+
'VPNSubType' => 'net.openvpn.OpenVPN-Connect.vpnplugin',
|
95
|
+
'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
|
+
}
|
112
|
+
|
113
|
+
plistPayloadContent = [vpn, cert] # to encrypt this array
|
114
|
+
#encPlistPayloadContent = cmsEncrypt([vpn, cert].to_plist).der_format
|
115
|
+
|
116
|
+
plist = {
|
117
|
+
'PayloadDescription' => "OpenVPN Configuration Payload for #{user}-#{device}@#{host}",
|
118
|
+
'PayloadDisplayName' => "#{host} OpenVPN #{user}@#{device}",
|
119
|
+
'PayloadIdentifier' => "#{identifier}.#{user}-#{device}",
|
120
|
+
'PayloadOrganization' => domain,
|
121
|
+
'PayloadRemovalDisallowed' => false,
|
122
|
+
'PayloadType' => 'Configuration',
|
123
|
+
'PayloadUUID' => `uuidgen`.chomp.upcase,
|
124
|
+
'PayloadVersion' => 1,
|
125
|
+
#'EncryptedPayloadContent' => StringData.new(encPlistPayloadContent)
|
126
|
+
'PayloadContent' => plistPayloadContent
|
127
|
+
}
|
128
|
+
|
129
|
+
return plist.to_plist
|
130
|
+
end
|
131
|
+
|
132
|
+
module_function :generate
|
133
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ovpnmcgen/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ovpnmcgen.rb"
|
8
|
+
spec.version = Ovpnmcgen::VERSION
|
9
|
+
spec.authors = ["Ronald Ip"]
|
10
|
+
spec.email = ["myself@iphoting.com"]
|
11
|
+
spec.summary = Ovpnmcgen::SUMMARY
|
12
|
+
spec.description = "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"
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
spec.bindir = 'bin'
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
spec.add_runtime_dependency "plist"
|
25
|
+
spec.add_runtime_dependency "commander", "~> 4.1"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ovpnmcgen.rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ronald Ip
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: plist
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: commander
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '4.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '4.1'
|
69
|
+
description: This utility generates configuration profiles that enables VPN-on-Demand,
|
70
|
+
as documented by Apple in https://developer.apple.com/library/ios/featuredarticles/iPhoneConfigurationProfileRef/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010206-CH1-SW27
|
71
|
+
email:
|
72
|
+
- myself@iphoting.com
|
73
|
+
executables:
|
74
|
+
- ovpnmcgen.rb
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- ".ruby-gemset"
|
80
|
+
- ".ruby-version"
|
81
|
+
- Gemfile
|
82
|
+
- LICENSE.txt
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- bin/ovpnmcgen.rb
|
86
|
+
- lib/ovpnmcgen.rb
|
87
|
+
- lib/ovpnmcgen/version.rb
|
88
|
+
- ovpnmcgen.rb.gemspec
|
89
|
+
homepage: ''
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.2.2
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: OpenVPN iOS Configuration Profile Utility
|
113
|
+
test_files: []
|