smart_proxy_container_gateway 3.3.0 → 3.4.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/lib/smart_proxy_container_gateway/container_gateway_api.rb +142 -39
- data/lib/smart_proxy_container_gateway/container_gateway_main.rb +94 -0
- data/lib/smart_proxy_container_gateway/foreman_api.rb +8 -2
- data/lib/smart_proxy_container_gateway/rhsm_client.rb +33 -0
- data/lib/smart_proxy_container_gateway/sequel_migrations/005_host_repositories.rb +24 -0
- data/lib/smart_proxy_container_gateway/version.rb +1 -1
- data/lib/smart_proxy_container_gateway.rb +1 -0
- metadata +6 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 470113167c3182629d62ee7babddf775b3e99e4c21ce0df5a9716894867dad86
|
4
|
+
data.tar.gz: 7adbe20e3c0dda9a9a3704501144c5f4134c7d9c266f36b19f24e83df09ad997
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b017c8a19c575d9ddcae254500917089eef21739914b5dc73447ba39af9840679028a4401bf15fd1216ae7299b7da7f55c6200a87830d7553fd8b380bc6c821b
|
7
|
+
data.tar.gz: e37b37684d5eaace152ab018067f4fa9292a5f5663543e030ed07eba894e59d6486c6b43a0c465c11dd7de65af14599b2e54a22882ff541dd5cb45e1ae381776
|
@@ -7,9 +7,11 @@ require 'sinatra'
|
|
7
7
|
require 'smart_proxy_container_gateway/container_gateway'
|
8
8
|
require 'smart_proxy_container_gateway/container_gateway_main'
|
9
9
|
require 'smart_proxy_container_gateway/foreman_api'
|
10
|
+
require 'smart_proxy_container_gateway/rhsm_client'
|
10
11
|
|
11
12
|
module Proxy
|
12
13
|
module ContainerGateway
|
14
|
+
# rubocop:disable Metrics/ClassLength
|
13
15
|
class Api < ::Sinatra::Base
|
14
16
|
include ::Proxy::Log
|
15
17
|
helpers ::Proxy::Helpers
|
@@ -19,6 +21,35 @@ module Proxy
|
|
19
21
|
inject_attr :database_impl, :database
|
20
22
|
inject_attr :container_gateway_main_impl, :container_gateway_main
|
21
23
|
|
24
|
+
get '/index/static/?' do
|
25
|
+
client_cert = ::Cert::RhsmClient.new(cert_from_request) if valid_cert?
|
26
|
+
valid_uuid = client_cert&.uuid&.present?
|
27
|
+
|
28
|
+
pulp_response = container_gateway_main.flatpak_static_index(translated_headers_for_proxy, params)
|
29
|
+
|
30
|
+
if pulp_response.code.to_i >= 400
|
31
|
+
status pulp_response.code.to_i
|
32
|
+
body pulp_response.body
|
33
|
+
elsif valid_uuid
|
34
|
+
host = database.connection[:hosts][{ uuid: client_cert.uuid }]
|
35
|
+
if host.nil?
|
36
|
+
repo_response = ForemanApi.new.fetch_host_repositories(client_cert.uuid, request.params)
|
37
|
+
halt repo_response.code.to_i, repo_response.body unless repo_response.code.to_i == 200
|
38
|
+
container_gateway_main.update_host_repositories(client_cert.uuid,
|
39
|
+
JSON.parse(repo_response.body)['repositories'])
|
40
|
+
end
|
41
|
+
catalog = container_gateway_main.host_catalog(client_cert.uuid).select_map(::Sequel[:repositories][:name])
|
42
|
+
pulp_index = JSON.parse(pulp_response.body)
|
43
|
+
halt 400, "Error: 'Results' key is missing in pulp_index" unless pulp_index.key?("Results")
|
44
|
+
pulp_index["Results"].select! { |result| catalog.include?(result["Name"]) }
|
45
|
+
status 200
|
46
|
+
body pulp_index.to_json
|
47
|
+
else
|
48
|
+
status pulp_response.code.to_i
|
49
|
+
body pulp_response.body
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
22
53
|
get '/v1/_ping/?' do
|
23
54
|
pulp_response = container_gateway_main.ping(translated_headers_for_proxy)
|
24
55
|
status pulp_response.code.to_i
|
@@ -26,7 +57,9 @@ module Proxy
|
|
26
57
|
end
|
27
58
|
|
28
59
|
get '/v2/?' do
|
29
|
-
|
60
|
+
client_cert = ::Cert::RhsmClient.new(cert_from_request) if valid_cert?
|
61
|
+
valid_uuid = client_cert&.uuid&.present?
|
62
|
+
if valid_uuid || (auth_header.present? && (auth_header.unauthorized_token? || auth_header.valid_user_token?))
|
30
63
|
response.headers['Docker-Distribution-API-Version'] = 'registry/2.0'
|
31
64
|
pulp_response = container_gateway_main.ping(translated_headers_for_proxy)
|
32
65
|
status pulp_response.code.to_i
|
@@ -117,9 +150,18 @@ module Proxy
|
|
117
150
|
end
|
118
151
|
|
119
152
|
get '/v2/_catalog/?' do
|
153
|
+
client_cert = ::Cert::RhsmClient.new(cert_from_request) if valid_cert?
|
120
154
|
catalog = []
|
121
|
-
if
|
122
|
-
|
155
|
+
if client_cert&.uuid&.present?
|
156
|
+
host = database.connection[:hosts][{ uuid: client_cert.uuid }]
|
157
|
+
if host.nil?
|
158
|
+
repo_response = ForemanApi.new.fetch_host_repositories(client_cert.uuid, request.params)
|
159
|
+
container_gateway_main.update_host_repositories(client_cert.uuid,
|
160
|
+
JSON.parse(repo_response.body)['repositories'])
|
161
|
+
end
|
162
|
+
catalog = container_gateway_main.host_catalog(client_cert.uuid).select_map(::Sequel[:repositories][:name])
|
163
|
+
elsif auth_header.present?
|
164
|
+
if auth_header.unauthenticated_token? || auth_header.unauthorized_token?
|
123
165
|
catalog = container_gateway_main.catalog.select_map(::Sequel[:repositories][:name])
|
124
166
|
elsif auth_header.valid_user_token?
|
125
167
|
catalog = container_gateway_main.catalog(auth_header.user).select_map(::Sequel[:repositories][:name])
|
@@ -150,42 +192,33 @@ module Proxy
|
|
150
192
|
request.params['account'] ||= username if username.present?
|
151
193
|
end
|
152
194
|
|
153
|
-
unless auth_header.present? && auth_header.basic_auth?
|
154
|
-
return { token: AuthorizationHeader::UNAUTHORIZED_TOKEN, issued_at: Time.now.rfc3339,
|
155
|
-
expires_in: 1.year.seconds.to_i }.to_json
|
156
|
-
end
|
157
|
-
|
158
195
|
token_response = ForemanApi.new.fetch_token(auth_header.raw_header, request.params)
|
159
|
-
|
160
|
-
halt token_response.code.to_i, token_response.body
|
161
|
-
else
|
162
|
-
# This returned token should follow OAuth2 spec. We need some minor conversion
|
163
|
-
# to store the token with the expires_at time (using rfc3339).
|
164
|
-
token_response_body = JSON.parse(token_response.body)
|
196
|
+
halt token_response.code.to_i, token_response.body unless token_response.code.to_i == 200
|
165
197
|
|
166
|
-
|
167
|
-
|
168
|
-
|
198
|
+
token_response_body = JSON.parse(token_response.body)
|
199
|
+
halt 502, "Recieved malformed token response" if token_response_body['token'].nil?
|
200
|
+
|
201
|
+
# Check for unauthorized tokens and respond with 401
|
202
|
+
halt 401, "unauthorized" if token_response_body['token'] == AuthorizationHeader::UNAUTHORIZED_TOKEN
|
203
|
+
|
204
|
+
# Skip storing the token if it is unauthenticated
|
205
|
+
unless token_response_body['token'] == AuthorizationHeader::UNAUTHENTICATED_TOKEN
|
169
206
|
|
170
207
|
# "issued_at" is an optional field. Per OAuth2 we assume time of token response as
|
171
208
|
# the issue time if the field is ommitted.
|
172
209
|
token_issue_time = (token_response_body["issued_at"] || token_response["Date"])&.to_time
|
173
|
-
if token_issue_time.nil?
|
174
|
-
halt 502, "Recieved malformed token response"
|
175
|
-
end
|
210
|
+
halt 502, "Recieved malformed token response" if token_issue_time.nil?
|
176
211
|
|
212
|
+
# This returned token should follow OAuth2 spec. We need some minor conversion
|
213
|
+
# to store the token with the expires_at time (using rfc3339).
|
177
214
|
# 'expires_in' is an optional field. If not provided, assume 60 seconds per OAuth2 spec
|
178
215
|
expires_in = token_response_body.fetch("expires_in", 60)
|
179
216
|
expires_at = token_issue_time + expires_in.seconds
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
)
|
186
|
-
else
|
187
|
-
halt 401, "unauthorized"
|
188
|
-
end
|
217
|
+
container_gateway_main.insert_token(
|
218
|
+
request.params['account'],
|
219
|
+
token_response_body['token'],
|
220
|
+
expires_at.rfc3339
|
221
|
+
)
|
189
222
|
|
190
223
|
repo_response = ForemanApi.new.fetch_user_repositories(auth_header.raw_header, request.params)
|
191
224
|
if repo_response.code.to_i != 200
|
@@ -194,10 +227,10 @@ module Proxy
|
|
194
227
|
container_gateway_main.update_user_repositories(request.params['account'],
|
195
228
|
JSON.parse(repo_response.body)['repositories'])
|
196
229
|
end
|
197
|
-
|
198
|
-
# Return the original token response from Katello
|
199
|
-
return token_response.body
|
200
230
|
end
|
231
|
+
|
232
|
+
# Return the original token response from Katello
|
233
|
+
return token_response.body
|
201
234
|
end
|
202
235
|
|
203
236
|
get '/users/?' do
|
@@ -222,12 +255,51 @@ module Proxy
|
|
222
255
|
{}
|
223
256
|
end
|
224
257
|
|
258
|
+
put '/update_hosts/?' do
|
259
|
+
do_authorize_any
|
260
|
+
hosts = params['hosts'] || []
|
261
|
+
# Refresh hosts table
|
262
|
+
database.connection.transaction(isolation: :serializable, retry_on: [Sequel::SerializationFailure]) do
|
263
|
+
hosts_table = database.connection[:hosts]
|
264
|
+
hosts_table.delete
|
265
|
+
hosts_table.import(%i[uuid], hosts.map { |host| [host['uuid']] })
|
266
|
+
end
|
267
|
+
{}
|
268
|
+
end
|
269
|
+
|
270
|
+
put '/host_repository_mapping/?' do
|
271
|
+
do_authorize_any
|
272
|
+
container_gateway_main.update_host_repo_mapping(params)
|
273
|
+
{}
|
274
|
+
end
|
275
|
+
|
276
|
+
put '/update_host_repositories/?' do
|
277
|
+
do_authorize_any
|
278
|
+
params['hosts'].flat_map do |host_map|
|
279
|
+
host_map.filter_map do |host_uuid, repos|
|
280
|
+
if repos.nil? || repos.empty?
|
281
|
+
repo_names = []
|
282
|
+
else
|
283
|
+
repo_names = repos
|
284
|
+
.select { |repo| repo['auth_required'].to_s.downcase == "true" }
|
285
|
+
.map { |repo| repo['repository'] }
|
286
|
+
end
|
287
|
+
container_gateway_main.update_host_repositories(host_uuid, repo_names)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
{}
|
291
|
+
end
|
292
|
+
|
225
293
|
private
|
226
294
|
|
227
295
|
def flatpak_client?
|
228
296
|
request.user_agent&.downcase&.include?('flatpak')
|
229
297
|
end
|
230
298
|
|
299
|
+
def valid_cert?
|
300
|
+
cert_from_request.present? && !cert_from_request.empty? && !cert_from_request.include?('null')
|
301
|
+
end
|
302
|
+
|
231
303
|
def head_or_get_blobs
|
232
304
|
repository = params[:splat][0]
|
233
305
|
digest = params[:splat][1]
|
@@ -281,6 +353,8 @@ module Proxy
|
|
281
353
|
end
|
282
354
|
|
283
355
|
def handle_repo_auth(repository, auth_header, request)
|
356
|
+
return if handle_client_cert_auth(repository)
|
357
|
+
|
284
358
|
user_token_is_valid = false
|
285
359
|
if auth_header.present? && auth_header.valid_user_token?
|
286
360
|
user_token_is_valid = true
|
@@ -288,19 +362,33 @@ module Proxy
|
|
288
362
|
# For flatpak client, header doesn't contain user name. Extract it from token.
|
289
363
|
username ||= container_gateway_main.token_user(@value.split(' ')[1]) if flatpak_client?
|
290
364
|
end
|
291
|
-
username
|
365
|
+
username ||= request.params['account']
|
292
366
|
|
293
367
|
return if container_gateway_main.authorized_for_repo?(repository, user_token_is_valid, username)
|
294
368
|
|
369
|
+
handle_unauthorized_access(username)
|
370
|
+
end
|
371
|
+
|
372
|
+
def handle_unauthorized_access(username)
|
295
373
|
redirect_authorization_headers
|
374
|
+
halt 401, "unauthorized" if flatpak_client? && username.nil?
|
375
|
+
throw_repo_not_found_error
|
376
|
+
end
|
296
377
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
378
|
+
def handle_client_cert_auth(repository)
|
379
|
+
client_cert = ::Cert::RhsmClient.new(cert_from_request) if valid_cert?
|
380
|
+
valid_uuid = client_cert&.uuid&.present?
|
381
|
+
if valid_uuid
|
382
|
+
host = database.connection[:hosts][{ uuid: client_cert.uuid }]
|
383
|
+
if host.nil?
|
384
|
+
repo_response = ForemanApi.new.fetch_host_repositories(client_cert.uuid, request.params)
|
385
|
+
container_gateway_main.update_host_repositories(client_cert.uuid,
|
386
|
+
JSON.parse(repo_response.body)['repositories'])
|
387
|
+
end
|
388
|
+
halt 401, "unauthorized" unless container_gateway_main.cert_authorized_for_repo?(repository, client_cert.uuid)
|
389
|
+
return true
|
301
390
|
end
|
302
|
-
|
303
|
-
throw_repo_not_found_error
|
391
|
+
false
|
304
392
|
end
|
305
393
|
|
306
394
|
def redirect_authorization_headers
|
@@ -310,6 +398,15 @@ module Proxy
|
|
310
398
|
"scope=\"repository:registry:pull,push\""
|
311
399
|
end
|
312
400
|
|
401
|
+
def cert_from_request
|
402
|
+
request.env['HTTP_X_RHSM_SSL_CLIENT_CERT'] ||
|
403
|
+
request.env['SSL_CLIENT_CERT'] ||
|
404
|
+
request.env['HTTP_SSL_CLIENT_CERT'] ||
|
405
|
+
ENV['HTTP_X_RHSM_SSL_CLIENT_CERT'] ||
|
406
|
+
ENV['SSL_CLIENT_CERT'] ||
|
407
|
+
ENV['HTTP_SSL_CLIENT_CERT']
|
408
|
+
end
|
409
|
+
|
313
410
|
def auth_header
|
314
411
|
AuthorizationHeader.new(request.env['HTTP_AUTHORIZATION'])
|
315
412
|
end
|
@@ -320,6 +417,7 @@ module Proxy
|
|
320
417
|
inject_attr :database_impl, :database
|
321
418
|
inject_attr :container_gateway_main_impl, :container_gateway_main
|
322
419
|
UNAUTHORIZED_TOKEN = 'unauthorized'.freeze
|
420
|
+
UNAUTHENTICATED_TOKEN = 'unauthenticated'.freeze
|
323
421
|
|
324
422
|
def initialize(value)
|
325
423
|
@value = value || ''
|
@@ -345,6 +443,10 @@ module Proxy
|
|
345
443
|
@value.split(' ')[1] == UNAUTHORIZED_TOKEN
|
346
444
|
end
|
347
445
|
|
446
|
+
def unauthenticated_token?
|
447
|
+
@value.split(' ')[1] == UNAUTHENTICATED_TOKEN
|
448
|
+
end
|
449
|
+
|
348
450
|
def token_auth?
|
349
451
|
@value.split(' ')[0] == 'Bearer'
|
350
452
|
end
|
@@ -367,5 +469,6 @@ module Proxy
|
|
367
469
|
end
|
368
470
|
end
|
369
471
|
end
|
472
|
+
# rubocop:enable Metrics/ClassLength
|
370
473
|
end
|
371
474
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
require 'uri'
|
3
3
|
require 'digest'
|
4
|
+
require 'erb'
|
4
5
|
require 'smart_proxy_container_gateway/dependency_injection'
|
5
6
|
require 'sequel'
|
6
7
|
module Proxy
|
@@ -40,6 +41,14 @@ module Proxy
|
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
44
|
+
def flatpak_static_index(headers, params = {})
|
45
|
+
uri = URI.parse("#{@pulp_endpoint}/pulpcore_registry/index/static")
|
46
|
+
unless params.empty?
|
47
|
+
uri.query = params.map { |k, v| "#{ERB::Util.url_encode(k.to_s)}=#{ERB::Util.url_encode(v.to_s)}" }.join('&')
|
48
|
+
end
|
49
|
+
pulp_registry_request(uri, headers)
|
50
|
+
end
|
51
|
+
|
43
52
|
def ping(headers)
|
44
53
|
uri = URI.parse("#{@pulp_endpoint}/pulpcore_registry/v2/")
|
45
54
|
pulp_registry_request(uri, headers)
|
@@ -101,6 +110,17 @@ module Proxy
|
|
101
110
|
end
|
102
111
|
end
|
103
112
|
|
113
|
+
def host_catalog(host_uuid = nil)
|
114
|
+
if host_uuid.nil?
|
115
|
+
unauthenticated_repos
|
116
|
+
else
|
117
|
+
database.connection[:repositories].
|
118
|
+
left_join(:hosts_repositories, repository_id: :id).
|
119
|
+
left_join(:hosts, ::Sequel[:hosts][:id] => :host_id).where(uuid: host_uuid).
|
120
|
+
or(Sequel[:repositories][:auth_required] => false).order(::Sequel[:repositories][:name])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
104
124
|
def unauthenticated_repos
|
105
125
|
database.connection[:repositories].where(auth_required: false).order(:name)
|
106
126
|
end
|
@@ -163,6 +183,66 @@ module Proxy
|
|
163
183
|
end
|
164
184
|
end
|
165
185
|
|
186
|
+
# Replaces the entire host-repo mapping for all hosts.
|
187
|
+
# Assumes host is present in the DB.
|
188
|
+
def update_host_repo_mapping(host_repo_maps)
|
189
|
+
# Get DB tables
|
190
|
+
hosts_repositories = database.connection[:hosts_repositories]
|
191
|
+
|
192
|
+
# Build list of [repository_id, host_id] pairs
|
193
|
+
entries = build_host_repository_mapping(host_repo_maps)
|
194
|
+
|
195
|
+
# Insert all in a single transaction
|
196
|
+
database.connection.transaction(isolation: :serializable, retry_on: [Sequel::SerializationFailure]) do
|
197
|
+
hosts_repositories.delete
|
198
|
+
hosts_repositories.import(%i[repository_id host_id], entries)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def build_host_repository_mapping(host_repo_maps)
|
203
|
+
hosts = database.connection[:hosts]
|
204
|
+
repositories = database.connection[:repositories]
|
205
|
+
entries = host_repo_maps['hosts'].flat_map do |host_map|
|
206
|
+
host_map.filter_map do |host_uuid, repos|
|
207
|
+
host = hosts[{ uuid: host_uuid }]
|
208
|
+
next unless host
|
209
|
+
|
210
|
+
repo_names = repos
|
211
|
+
.select { |repo| repo['auth_required'].to_s.downcase == "true" }
|
212
|
+
.map { |repo| repo['repository'] }
|
213
|
+
|
214
|
+
repositories
|
215
|
+
.where(name: repo_names, auth_required: true)
|
216
|
+
.select(:id)
|
217
|
+
.map { |repo| [repo[:id], host[:id]] }
|
218
|
+
end
|
219
|
+
end
|
220
|
+
entries.flatten!(1)
|
221
|
+
end
|
222
|
+
|
223
|
+
def update_host_repositories(uuid, repositories)
|
224
|
+
host = find_or_create_host(uuid)
|
225
|
+
hosts_repositories = database.connection[:hosts_repositories]
|
226
|
+
database.connection.transaction(isolation: :serializable,
|
227
|
+
retry_on: [Sequel::SerializationFailure],
|
228
|
+
num_retries: 10) do
|
229
|
+
hosts_repositories.where(host_id: host[:id]).delete
|
230
|
+
return if repositories.nil? || repositories.empty?
|
231
|
+
|
232
|
+
hosts_repositories.import(
|
233
|
+
%i[repository_id host_id],
|
234
|
+
database.connection[:repositories].where(name: repositories, auth_required: true).select(:id).map do |repo|
|
235
|
+
[repo[:id], host[:id]]
|
236
|
+
end
|
237
|
+
)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def find_or_create_host(uuid)
|
242
|
+
database.connection[:hosts].insert_conflict(target: :uuid, action: :ignore).insert(uuid: uuid)
|
243
|
+
database.connection[:hosts][{ uuid: uuid }]
|
244
|
+
end
|
245
|
+
|
166
246
|
# Returns:
|
167
247
|
# true if the user is authorized to access the repo, or
|
168
248
|
# false if the user is not authorized to access the repo or if it does not exist
|
@@ -185,6 +265,20 @@ module Proxy
|
|
185
265
|
false
|
186
266
|
end
|
187
267
|
|
268
|
+
def cert_authorized_for_repo?(repo_name, uuid)
|
269
|
+
database.connection.transaction(isolation: :serializable, retry_on: [Sequel::SerializationFailure]) do
|
270
|
+
repository = database.connection[:repositories][{ name: repo_name }]
|
271
|
+
return false if repository.nil?
|
272
|
+
return true unless repository[:auth_required]
|
273
|
+
|
274
|
+
database.connection[:hosts_repositories]
|
275
|
+
.where(repository_id: repository[:id])
|
276
|
+
.join(:hosts, id: :host_id)
|
277
|
+
.where(Sequel[:hosts][:uuid] => uuid)
|
278
|
+
.any?
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
188
282
|
def token_user(token)
|
189
283
|
database.connection[:users][{
|
190
284
|
id: database.connection[:authentication_tokens].where(token_checksum: checksum(token)).select(:user_id)
|
@@ -1,15 +1,17 @@
|
|
1
1
|
require 'uri'
|
2
|
+
require 'openssl'
|
2
3
|
|
3
4
|
module Proxy
|
4
5
|
module ContainerGateway
|
5
6
|
class ForemanApi
|
6
|
-
def registry_request(auth_header, params, suffix)
|
7
|
+
def registry_request(auth_header, params, suffix, uuid: '', cert: false)
|
7
8
|
uri = URI.join(Proxy::SETTINGS.foreman_url, Proxy::ContainerGateway::Plugin.settings.katello_registry_path, suffix)
|
8
9
|
uri.query = process_params(params)
|
9
10
|
|
10
11
|
req = Net::HTTP::Get.new(uri)
|
11
|
-
req.add_field('Authorization', auth_header)
|
12
|
+
req.add_field('Authorization', auth_header) unless cert
|
12
13
|
req.add_field('Accept', 'application/json')
|
14
|
+
req.add_field('HostUUID', uuid) if cert
|
13
15
|
req.content_type = 'application/json'
|
14
16
|
http = Net::HTTP.new(uri.hostname, uri.port)
|
15
17
|
http.use_ssl = true
|
@@ -29,6 +31,10 @@ module Proxy
|
|
29
31
|
def fetch_user_repositories(auth_header, params)
|
30
32
|
registry_request(auth_header, params, '_catalog')
|
31
33
|
end
|
34
|
+
|
35
|
+
def fetch_host_repositories(uuid, params)
|
36
|
+
registry_request(nil, params, '_catalog', uuid: uuid, cert: true)
|
37
|
+
end
|
32
38
|
end
|
33
39
|
end
|
34
40
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module Cert
|
5
|
+
class RhsmClient
|
6
|
+
attr_accessor :cert
|
7
|
+
|
8
|
+
def initialize(cert)
|
9
|
+
self.cert = extract(cert)
|
10
|
+
end
|
11
|
+
|
12
|
+
def uuid
|
13
|
+
@uuid ||= @cert.subject.to_a.find { |entry| entry[0] == 'CN' }&.[](1)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def extract(cert)
|
19
|
+
raise('Invalid cert provided. Ensure that the provided cert is not empty.') if cert.empty?
|
20
|
+
|
21
|
+
cert = strip_cert(cert)
|
22
|
+
cert = Base64.decode64(cert)
|
23
|
+
OpenSSL::X509::Certificate.new(cert)
|
24
|
+
end
|
25
|
+
|
26
|
+
def strip_cert(cert)
|
27
|
+
cert = cert.to_s.gsub("-----BEGIN CERTIFICATE-----", "").gsub("-----END CERTIFICATE-----", "")
|
28
|
+
cert.delete!(' ')
|
29
|
+
cert.delete!("\n")
|
30
|
+
cert
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
up do
|
3
|
+
# Create the hosts table
|
4
|
+
create_table(:hosts) do
|
5
|
+
primary_key :id
|
6
|
+
String :uuid, null: false, unique: true
|
7
|
+
end
|
8
|
+
|
9
|
+
# Create the hosts_repositories join table
|
10
|
+
create_table(:hosts_repositories) do
|
11
|
+
foreign_key :host_id, :hosts, on_delete: :cascade
|
12
|
+
foreign_key :repository_id, :repositories, on_delete: :cascade
|
13
|
+
primary_key %i[host_id repository_id]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
down do
|
18
|
+
# Drop the hosts_repositories join table
|
19
|
+
drop_table(:hosts_repositories)
|
20
|
+
|
21
|
+
# Drop the hosts table
|
22
|
+
drop_table(:hosts)
|
23
|
+
end
|
24
|
+
end
|
@@ -2,3 +2,4 @@ require 'smart_proxy_container_gateway/version'
|
|
2
2
|
require 'smart_proxy_container_gateway/database'
|
3
3
|
require 'smart_proxy_container_gateway/container_gateway'
|
4
4
|
require 'smart_proxy_container_gateway/container_gateway_main'
|
5
|
+
require 'smart_proxy_container_gateway/rhsm_client'
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_container_gateway
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ian Ballou
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: activesupport
|
@@ -77,8 +76,8 @@ email: ianballou67@gmail.com
|
|
77
76
|
executables: []
|
78
77
|
extensions: []
|
79
78
|
extra_rdoc_files:
|
80
|
-
- README.md
|
81
79
|
- LICENSE
|
80
|
+
- README.md
|
82
81
|
files:
|
83
82
|
- LICENSE
|
84
83
|
- README.md
|
@@ -91,17 +90,18 @@ files:
|
|
91
90
|
- lib/smart_proxy_container_gateway/database.rb
|
92
91
|
- lib/smart_proxy_container_gateway/dependency_injection.rb
|
93
92
|
- lib/smart_proxy_container_gateway/foreman_api.rb
|
93
|
+
- lib/smart_proxy_container_gateway/rhsm_client.rb
|
94
94
|
- lib/smart_proxy_container_gateway/sequel_migrations/001_initial.rb
|
95
95
|
- lib/smart_proxy_container_gateway/sequel_migrations/002_auth_tokens.rb
|
96
96
|
- lib/smart_proxy_container_gateway/sequel_migrations/003_authorization_reorg.rb
|
97
97
|
- lib/smart_proxy_container_gateway/sequel_migrations/004_users_repositories_cascade_delete.rb
|
98
|
+
- lib/smart_proxy_container_gateway/sequel_migrations/005_host_repositories.rb
|
98
99
|
- lib/smart_proxy_container_gateway/version.rb
|
99
100
|
- settings.d/container_gateway.yml.example
|
100
101
|
homepage: https://github.com/Katello/smart_proxy_container_gateway
|
101
102
|
licenses:
|
102
103
|
- GPL-3.0-only
|
103
104
|
metadata: {}
|
104
|
-
post_install_message:
|
105
105
|
rdoc_options: []
|
106
106
|
require_paths:
|
107
107
|
- lib
|
@@ -116,8 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0'
|
118
118
|
requirements: []
|
119
|
-
rubygems_version: 3.
|
120
|
-
signing_key:
|
119
|
+
rubygems_version: 3.6.7
|
121
120
|
specification_version: 4
|
122
121
|
summary: Pulp 3 container registry support for Foreman/Katello Smart-Proxy
|
123
122
|
test_files: []
|