puppet-sneakernet 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3a034a93d2bef7ae705dd70e90796c7170265ad3b847f546a790d991ac000c7d
4
+ data.tar.gz: ce59052840c0e8a42278bae4b27daaea54208625c5b46c3f6a3be60b5ab394af
5
+ SHA512:
6
+ metadata.gz: ebdea103825dc15fb42feb133a855a0ebaef50bb46c8b53e48d27d67eec1bb9dc9881e99e5f3d9887a22e680ed5040c1bc43785843d8999c70e119df9bdf3498
7
+ data.tar.gz: a530789c7b9d4e2277cc8008a6933df9f14668cd9038f7ce4b154c3c4b07d2d55a37e84ce460894e4205a5ea73ab35003c3baf513a19eb88a86f7abac9539d7b
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # Changelog
2
+
3
+ ## v0.0.1
4
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "{}"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright {yyyy} {name of copyright owner}
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # Puppet Sneakernet
2
+
3
+ This is a simple POC web service that will turn a `Puppetfile` into a tarball
4
+ of a complete Puppet environment. All you need to do is paste the contents of
5
+ the `Puppetfile` into the textbox and press *Download*.
6
+
7
+ This will resolve the dependencies of your `Puppetfile`, create an environment
8
+ from them, and then pack the whole thing into a tarball. Save that tarball to
9
+ a USB key, then perform any review or approval required by your security and
10
+ quality policies.
11
+
12
+ Once approved, walk the USB key with the modules tarball across your air-gap
13
+ and uncompress them into your codebase. For example:
14
+
15
+ ```
16
+ $ cd /etc/puppetlabs/code/environments/staging
17
+ $ tar -xvzf /media/USB/Puppetfile.packed.<date>.tar.gz --strip-components=1
18
+ ```
19
+
20
+ We recommend using an MD5 checksum to prove that the tarball you deploy is the
21
+ same as the tarball you get approved. You can generate that with one of the
22
+ following commands, depending on your platform.
23
+
24
+ * `md5 Puppetfile.packed.<date>.tar.gz > md5sum`
25
+ * `md5sum Puppetfile.packed.<date>.tar.gz > md5sum`
26
+
27
+ ## *⚠️ Warning! ⚠️*
28
+
29
+ Resolving dependencies in a `Puppetfile` means that you'll be installing code
30
+ that you didn't specifically request into your environment. Make sure you audit
31
+ the modules from the tarball, not just code from the source repositories of the
32
+ modules you specified in your `Puppetfile`.
33
+
@@ -0,0 +1,82 @@
1
+ #! /usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'puppet-sneakernet'
4
+
5
+ options = {
6
+ :port => 9000,
7
+ :csrf => false,
8
+ }
9
+ logfile = $stderr
10
+ loglevel = Logger::WARN
11
+ ssl_opts = {:verify_peer => false}
12
+
13
+ optparse = OptionParser.new { |opts|
14
+ opts.banner = "Usage : puppet-sneakernet [-p <port>] [-l [logfile]] [-x]
15
+ -- Runs the Puppet Sneakernet web service.
16
+
17
+ "
18
+
19
+ opts.on("-d", "--debug", "Display or log debugging messages") do
20
+ loglevel = Logger::DEBUG
21
+ end
22
+
23
+ opts.on("-l [LOGFILE]", "--logfile [LOGFILE]", "Path to logfile. Defaults to no logging, or /var/log/puppetfile-packer if no filename is passed.") do |arg|
24
+ logfile = arg || '/var/log/puppet-sneakernet'
25
+ end
26
+
27
+ opts.on("-p PORT", "--port", "Port to listen on. Defaults to 9000.") do |arg|
28
+ options[:port] = arg
29
+ end
30
+
31
+ opts.on("-x", "--csrf", "Protect from cross site request forgery. Requires Puppetfile to be submitted via the webpage.") do
32
+ options[:csrf] = true
33
+ end
34
+
35
+ opts.on("--ssl", "Run with SSL support. Autogenerates a self-signed certificates by default.") do
36
+ options[:ssl] = true
37
+ end
38
+
39
+ opts.on("--ssl-cert FILE", "Specify the SSL certificate you'd like use use. Pair with --ssl-key.") do |arg|
40
+ ssl_opts[:cert_chain_file] = arg
41
+ end
42
+
43
+ opts.on("--ssl-key FILE", "Specify the SSL key file you'd like use use. Pair with --ssl-cert.") do |arg|
44
+ ssl_opts[:private_key_file] = arg
45
+ end
46
+
47
+ opts.separator('')
48
+
49
+ opts.on("-h", "--help", "Displays this help") do
50
+ puts
51
+ puts opts
52
+ puts
53
+ exit
54
+ end
55
+ }
56
+ optparse.parse!
57
+
58
+ logger = Logger.new(logfile)
59
+ logger.level = loglevel
60
+ options[:logger] = logger
61
+
62
+ if ssl_opts[:cert_chain_file] and ssl_opts[:private_key_file]
63
+ options[:ssl] = true
64
+ end
65
+
66
+ # These options should either both be nil or both be Strings
67
+ unless ssl_opts[:cert_chain_file].class == ssl_opts[:private_key_file].class
68
+ raise 'You must specify both the certificate and key file!'
69
+ end
70
+
71
+ PuppetSneakernet.run!(options) do |server|
72
+ if options[:ssl]
73
+ if server.respond_to? 'ssl='
74
+ logger.info 'Enabling SSL support.'
75
+ server.ssl = true
76
+ server.ssl_options = ssl_opts
77
+ else
78
+ logger.warn "Please 'gem install thin' or run via an app server for SSL support."
79
+ end
80
+ end
81
+ end
82
+
@@ -0,0 +1,167 @@
1
+ require 'logger'
2
+ require 'sinatra/base'
3
+ require 'cgi'
4
+ require 'uri'
5
+ require 'ripper'
6
+
7
+ MAXSIZE = 100000 # something like 3,000 lines of code
8
+
9
+ class PuppetSneakernet < Sinatra::Base
10
+ require 'date'
11
+ require 'minitar'
12
+ require 'zlib'
13
+ require 'puppet_forge'
14
+ require 'puppetfile-resolver'
15
+ require 'puppetfile-resolver/puppetfile/parser/r10k_eval'
16
+
17
+ PuppetForge.user_agent = 'Puppet Sneakernet/0.0.1'
18
+
19
+ set :logging, true
20
+ set :strict, true
21
+ set :root, File.dirname(__FILE__) +'/..'
22
+
23
+ enable :sessions
24
+
25
+ before do
26
+ env["rack.logger"] = settings.logger if settings.logger
27
+
28
+ if settings.csrf
29
+ session[:csrf] ||= SecureRandom.hex(32)
30
+ response.set_cookie 'authenticity_token', {
31
+ :path => '/',
32
+ :value => session[:csrf],
33
+ :expires => Time.now + (60 * 60 * 24),
34
+ }
35
+ end
36
+ end
37
+
38
+ get '/' do
39
+ erb :index
40
+ end
41
+
42
+ post '/download' do
43
+ logger.info "Packing Puppetfile from #{request.ip}."
44
+ logger.debug "Packing Puppetfile from #{request.ip}: #{params['code']}"
45
+
46
+ validate_request!
47
+ sanitize_code!
48
+
49
+ pack_puppetfile
50
+ end
51
+
52
+
53
+ not_found do
54
+ halt 404, "You shall not pass! (page not found)"
55
+ end
56
+
57
+ helpers do
58
+ def validate_request!
59
+ csrf_safe!
60
+ check_size_limit!
61
+ end
62
+
63
+ def csrf_safe!
64
+ return true unless settings.csrf
65
+ if session[:csrf] == params['_csrf'] && session[:csrf] == request.cookies['authenticity_token']
66
+ true
67
+ else
68
+ logger.warn 'CSRF attempt detected. Ensure that server time is correct.'
69
+ logger.debug "session: #{session[:csrf]}"
70
+ logger.debug " param: #{params['_csrf']}"
71
+ logger.debug " cookie: #{request.cookies['authenticity_token']}"
72
+
73
+ halt 403, 'Request validation failed.'
74
+ end
75
+ end
76
+
77
+ def check_size_limit!
78
+ content = request.body.read
79
+ request.body.rewind
80
+
81
+ if content.size > MAXSIZE
82
+ halt 400, "Submitted code size is #{content.size}, which is larger than the maximum size of #{MAXSIZE}."
83
+ end
84
+ end
85
+
86
+ def sanitize_code!
87
+ variants = [:command, :call, :fcall, :vcall]
88
+
89
+ tokens = Ripper.sexp(params['code']).flatten
90
+ indices = tokens.map.with_index { |a, i| variants.include?(a) ? i : nil }.compact
91
+ methods = indices.map { |i| tokens[i + 2] }.flatten.compact
92
+
93
+ methods.reject! { |name| ['mod', 'forge', 'moduledir'].include? name }
94
+ halt 400, "Arbitrary Ruby code is not supported. Please remove '#{methods.join(', ')}' and try again." unless methods.empty?
95
+ end
96
+
97
+ def pack_puppetfile
98
+ begin
99
+ # Parse the Puppetfile into an object model
100
+ puppetfile = ::PuppetfileResolver::Puppetfile::Parser::R10KEval.parse(params['code'])
101
+ rescue PuppetfileResolver::Puppetfile::Parser::ParserError => e
102
+ logger.error 'Syntax error in Puppetfile'
103
+ halt 400, "Syntax error in Puppetfile: #{e.message}"
104
+ end
105
+
106
+ # Make sure the Puppetfile is valid
107
+ unless puppetfile.valid?
108
+ logger.error 'Puppetfile is not valid'
109
+ puppetfile.validation_errors.each { |err| logger.warn err }
110
+ halt 400, 'Puppetfile is not valid'
111
+ end
112
+
113
+ resolver = PuppetfileResolver::Resolver.new(puppetfile, nil)
114
+ result = resolver.resolve(strict_mode: true)
115
+
116
+ result.validation_errors.each { |err| logger.warn "Dependency resolution: #{err}"}
117
+
118
+ unless result.dependency_graph.count > 0
119
+ logger.warn 'No modules resolved!'
120
+ halt 400, 'No modules resolved, press back and try again.'
121
+ end
122
+
123
+ buffer = StringIO.new
124
+ tmpdir = Dir.mktmpdir
125
+ Dir.mktmpdir('sneakernet') do |dir|
126
+ Dir.chdir(dir) do
127
+ Dir.mkdir('modules')
128
+
129
+ File.open('Puppetfile', "w+") do |puppetfile|
130
+ result.dependency_graph.each do |dep|
131
+ mod = dep.payload
132
+ next unless mod.is_a? PuppetfileResolver::Models::ModuleSpecification
133
+
134
+ # record the module we're downloading
135
+ puppetfile.write "mod '#{dep.payload.owner}-#{dep.payload.name}', '#{dep.payload.version}'\n"
136
+
137
+ release_slug = "#{dep.payload.owner}-#{dep.payload.name}-#{dep.payload.version}"
138
+ release_tarball = release_slug + ".tar.gz"
139
+ destination = "modules/#{dep.payload.name}"
140
+
141
+ logger.debug "Retrieving #{release_slug}"
142
+
143
+ begin
144
+ release = PuppetForge::Release.find(release_slug)
145
+ release.download(Pathname(release_tarball))
146
+ release.verify(Pathname(release_tarball))
147
+ PuppetForge::Unpacker.unpack(release_tarball, destination, tmpdir)
148
+ FileUtils.rm(release_tarball)
149
+
150
+ rescue Faraday::BadRequestError
151
+ logger.error "Error retrieving #{release_slug}"
152
+ halt 400, "Error retrieving #{release_slug}"
153
+ end
154
+ end
155
+ end
156
+
157
+ Minitar.pack('.', Zlib::GzipWriter.new(buffer))
158
+ end
159
+ end
160
+ FileUtils.rm_rf(tmpdir)
161
+
162
+ attachment("Puppetfile.packed.#{Date.today}.tar.gz")
163
+ buffer.string
164
+ end
165
+
166
+ end
167
+ end
data/public/styles.css ADDED
@@ -0,0 +1,71 @@
1
+ body {
2
+ min-width: 690px;
3
+ padding: 0 2em;
4
+ }
5
+
6
+ .warning {
7
+ background-color: #ffb6b6;
8
+ }
9
+
10
+ div.links {
11
+ margin: 1em auto;
12
+ padding: 0.5em;
13
+ border-top: 1px solid black;
14
+ text-align: center;
15
+ }
16
+ div.links a {
17
+ margin: 0.5em;
18
+ }
19
+
20
+ div.entry {
21
+ width: 80%;
22
+ margin: 0 auto;
23
+ border: 1px solid #ccc;
24
+ padding: 0.5em;
25
+ border-radius: 0.5em;
26
+ background-color: #f7f7f7;
27
+ }
28
+
29
+ form.validated div.entry {
30
+ background-color: #f1ffe8;
31
+ border: 1px solid #1dd12d;
32
+ }
33
+ form.validated.failed div.entry {
34
+ background-color: #ffd4d4;
35
+ border: 1px solid #981818;
36
+ }
37
+
38
+ div.entry textarea {
39
+ width: 100%;
40
+ font-family: monospace;
41
+ }
42
+ div.row {
43
+ text-align: center;
44
+ }
45
+
46
+ .info {
47
+ column-count: 2;
48
+ background: #fefefe;
49
+ border: 1px solid #efefef;
50
+ padding: 0.5em;
51
+ padding-left: 50px;
52
+ width: 80%;
53
+ margin: 0 auto;
54
+ -moz-border-radius: 0.5em;
55
+ -webkit-border-radius: 0.5em;
56
+ -khtml-border-radius: 0.5em;
57
+ border-radius: 0.5em;
58
+ }
59
+ .info i.fas {
60
+ float: left;
61
+ margin-left: -5px;
62
+ padding-right: 5px;
63
+ }
64
+ .info div.column {
65
+ break-after: column;
66
+ }
67
+
68
+ /* jquery UI overrides */
69
+ .ui-widget {
70
+ font-size: 0.85em;
71
+ }
data/views/index.erb ADDED
@@ -0,0 +1,63 @@
1
+ <html>
2
+ <head>
3
+ <title>Puppet Sneakernet</title>
4
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta2/css/fontawesome.min.css">
5
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta2/css/solid.min.css">
6
+ <link rel="stylesheet" href="/styles.css">
7
+
8
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
9
+ <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
10
+ <script src="/scripts.js"></script>
11
+ </head>
12
+ <body>
13
+ <a href="https://github.com/puppetlabs/puppetfile-packer"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"></a>
14
+ <h1>Puppet Sneakernet</h1>
15
+ <h2>Paste a <code>Puppetfile</code> into the following textbox, then download the environment it represents as a tarball.</h2>
16
+
17
+ <form action="/download" method="post">
18
+ <% if settings.csrf %><input name="_csrf", type="hidden" value="<%= session[:csrf] %>" /><% end %>
19
+ <div class="entry">
20
+ <textarea name="code" id="code" cols="65" rows="25"> </textarea>
21
+ <div class="row">
22
+ <input type="submit" value="Download" id="download">
23
+ </div>
24
+ </div>
25
+ </form>
26
+
27
+ <div class="info">
28
+ <div class="column">
29
+ <p>
30
+ <i class="fas fa-info-circle fa-3x" aria-hidden="true"></i>
31
+ When you press the download button, the service will resolve the dependencies of
32
+ your <code>Puppetfile</code>, create an environment from them, and then pack the
33
+ whole thing into a tarball. Save that tarball to a USB key, then perform any review
34
+ or approval required by your security and quality policies.
35
+ </p>
36
+ <p>
37
+ Once approved, walk the USB key with the modules tarball across your air-gap
38
+ and uncompress them into your codebase. For example:
39
+ <code><pre>$ cd /etc/puppetlabs/code/environments/staging
40
+ $ tar -xvzf /media/USB/Puppetfile.packed.&lt;date&gt;.tar.gz --strip-components=1</pre></code>
41
+ </p>
42
+ <p>
43
+ We recommend using an MD5 checksum to prove that the tarball you deploy is the
44
+ same as the tarball you get approved. You can generate that with one of the
45
+ following commands, depending on your platform.
46
+ <ul>
47
+ <li><code>md5 Puppetfile.packed.&lt;date&gt;.tar.gz &gt; md5sum</code></li>
48
+ <li><code>md5sum Puppetfile.packed.&lt;date&gt;.tar.gz &gt; md5sum</code></li>
49
+ </ul>
50
+ </p>
51
+ </div>
52
+ <div class="column">
53
+ <p>
54
+ <i class="fas fa-exclamation-triangle fa-3x" aria-hidden="true"></i>
55
+ Resolving dependencies in a <code>Puppetfile</code> means installing code that
56
+ you didn't specifically request into your environment. Make sure you audit the
57
+ modules from the tarball, not just code from the source repositories of the
58
+ modules you specified in your <code>Puppetfile</code>.
59
+ </p>
60
+ </div>
61
+ </div>
62
+ </body>
63
+ </html>
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: puppet-sneakernet
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Puppetlabs
8
+ - Ben Ford
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2021-11-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sinatra
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: minitar
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: puppet_forge
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: puppetfile-resolver
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 0.5.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 0.5.0
70
+ description: |2
71
+ Puppet Sneakernet is a simple web service that turns a Puppetfile into a tarball
72
+ of the complete environment that Puppetfile represents, with all the module
73
+ dependencies resolved. Just untar that into the proper environmentpath on an
74
+ air-gapped Puppet server.
75
+ email: community@puppet.com
76
+ executables:
77
+ - puppet-sneakernet
78
+ extensions: []
79
+ extra_rdoc_files: []
80
+ files:
81
+ - CHANGELOG.md
82
+ - LICENSE
83
+ - README.md
84
+ - bin/puppet-sneakernet
85
+ - lib/puppet-sneakernet.rb
86
+ - public/styles.css
87
+ - views/index.erb
88
+ homepage: https://github.com/puppetlabs/puppet-sneakernet/
89
+ licenses:
90
+ - Apache-2.0
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubygems_version: 3.1.4
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Helps you retrieve the Puppet Forge modules you need for an air-gapped environment.
111
+ test_files: []