docker_registry_cli 0.0.3 → 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 +4 -4
- data/auth/BasicAuthService.rb +32 -14
- data/auth/TokenAuthService.rb +44 -12
- data/bin/docker_registry_cli +7 -7
- data/commands/DockerRegistryCommand.rb +1 -1
- data/requests/DockerRegistryRequest.rb +27 -18
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0318db7ea65bbf5d67e1f4d2da3d0295f243b1f6
|
4
|
+
data.tar.gz: fad5ebcd07f37a4d5361f35fa89dd56c33fda087
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22f009f75a5a27ee8793a10d84d617e7cc515cd22890227dc9ab4bb73cc8510a9751a79efb47417bd064db129645dcc7fa78b6b939993cff39ca61c96349fcda
|
7
|
+
data.tar.gz: 27a62e49792644cee56d22a4ab5f06386ff8bc45a8084f789d8016e85aa05dd855468852aa774572d19c295c04570144b9273c822caa3873fe49ef1f18708b41
|
data/auth/BasicAuthService.rb
CHANGED
@@ -1,26 +1,44 @@
|
|
1
1
|
class BasicAuthService
|
2
|
-
|
3
|
-
@@requestToBeAuthed = nil
|
2
|
+
@requestToBeAuthed = nil
|
4
3
|
def initialize(requestToBeAuthed)
|
5
|
-
|
4
|
+
@requestToBeAuthed = requestToBeAuthed
|
6
5
|
end
|
7
6
|
def byCredentials(user, pass)
|
8
|
-
|
7
|
+
@requestToBeAuthed.class.basic_auth user, pass
|
9
8
|
end
|
10
9
|
|
11
|
-
def byToken
|
10
|
+
def byToken
|
12
11
|
# load the docker config and see, if the domain is included there - reuse the auth token
|
13
|
-
config =
|
14
|
-
|
15
|
-
if(
|
16
|
-
|
17
|
-
throw('No auth token found for this domain')
|
12
|
+
config = nil
|
13
|
+
config_path = File.join(ENV['HOME'], '.docker/config.json')
|
14
|
+
if File.exist?(config_path)
|
15
|
+
config = JSON.parse(File.read(config_path))
|
18
16
|
end
|
19
17
|
|
20
|
-
|
18
|
+
if !config.nil? && config['auths'].key?(@requestToBeAuthed.registry_domain) && config['auths'][@requestToBeAuthed.registry_domain]
|
19
|
+
pp 'Using existing token from config.json not the keychain. Do not do that!!'.red
|
20
|
+
# decorate our request object
|
21
|
+
# set the Authorization header directly, since it is already base64 encoded
|
22
|
+
token = config['auths'][@requestToBeAuthed.registry_domain]['auth']
|
23
|
+
if !token
|
24
|
+
pp config if @@debug
|
25
|
+
throw 'No auth token found for this domain'
|
26
|
+
end
|
27
|
+
@requestToBeAuthed.class.headers['Authorization'] = "Basic #{token}"
|
28
|
+
else
|
29
|
+
use_keychain_auth
|
30
|
+
end
|
31
|
+
end
|
21
32
|
|
22
|
-
|
23
|
-
|
24
|
-
|
33
|
+
def use_keychain_auth
|
34
|
+
osx_cred_entry = Keychain.internet_passwords.where(:path => @requestToBeAuthed.registry_domain).first
|
35
|
+
if osx_cred_entry.nil?
|
36
|
+
puts "Not found any keychain entry for registry #{@requestToBeAuthed.registry_domain}, please auth using: docker login #{@requestToBeAuthed.registry_domain}".red if @debug
|
37
|
+
throw 'Keychain entry not found'
|
38
|
+
elsif @debug
|
39
|
+
puts "Found user '#{osx_cred_entry.account}' in keychain".white
|
40
|
+
end
|
41
|
+
# we do nto use basic_auth here, since we cannot override it later if we need to use Bearer auth
|
42
|
+
@requestToBeAuthed.class.headers['Authorization'] = @requestToBeAuthed.class.basic_encode(osx_cred_entry.account, osx_cred_entry.password)
|
25
43
|
end
|
26
44
|
end
|
data/auth/TokenAuthService.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
|
+
require 'keychain'
|
2
|
+
require 'colorize'
|
3
|
+
|
1
4
|
class TokenAuthService
|
2
|
-
|
5
|
+
@debug
|
6
|
+
@requestToBeAuthed = nil
|
3
7
|
def initialize(requestToBeAuthed)
|
4
|
-
|
8
|
+
@requestToBeAuthed = requestToBeAuthed
|
9
|
+
@debug = @requestToBeAuthed.is_debug
|
5
10
|
end
|
6
11
|
|
7
12
|
def tokenAuth(responseToBeAuthed)
|
8
13
|
# retrieve how we will contact the token service
|
9
|
-
auth_description = responseToBeAuthed.headers
|
14
|
+
auth_description = responseToBeAuthed.headers['www-authenticate']
|
10
15
|
options = {
|
11
16
|
:query => {
|
12
17
|
}
|
@@ -17,25 +22,52 @@ class TokenAuthService
|
|
17
22
|
|
18
23
|
# get the service we will generate the tokens for
|
19
24
|
m = /service="(?<service>[^"]+)"/.match(auth_description)
|
20
|
-
if
|
25
|
+
if m
|
21
26
|
options[:query][:service] = m['service']
|
22
27
|
end
|
23
|
-
# the scope,
|
28
|
+
# the scope, important if scopes are used in the token service
|
24
29
|
# this is not always set
|
25
30
|
m = /scope="(?<scope>[^"]+)"/.match(auth_description)
|
26
|
-
if
|
31
|
+
if m
|
27
32
|
options[:query][:scope] = m['scope']
|
28
33
|
end
|
29
34
|
|
30
|
-
unless
|
31
|
-
puts
|
32
|
-
throw
|
35
|
+
unless url
|
36
|
+
puts 'Could not extract auth information for Bearer token'.colorize(:red) if @debug
|
37
|
+
throw 'Could not extract auth information for Bearer token'
|
38
|
+
end
|
39
|
+
|
40
|
+
config = nil
|
41
|
+
config_path = File.join(ENV['HOME'], '.docker/config.json')
|
42
|
+
if File.exist?(config_path)
|
43
|
+
config = JSON.parse(File.read(config_path))
|
33
44
|
end
|
34
|
-
|
35
|
-
|
45
|
+
|
46
|
+
if !config.nil? && config['auths'].key?(@requestToBeAuthed.registry_domain) && config['auths'][@requestToBeAuthed.registry_domain]
|
47
|
+
domain_settings = config['auths'][@requestToBeAuthed.registry_domain]
|
48
|
+
puts 'Using existing token from config.json not the keychain. Do not do that, use the keychain!'.red
|
49
|
+
# decorate our request object
|
50
|
+
# set the Authorization header directly, since it is already base64 encoded
|
51
|
+
options[:headers] = {}
|
52
|
+
options[:headers]['Authorization'] = "Basic #{domain_settings['auth']}"
|
53
|
+
else
|
54
|
+
# load credentials from the keychain
|
55
|
+
puts 'getting credentials from keychain'.white if @debug
|
56
|
+
osx_cred_entry = Keychain.internet_passwords.where(:path => @requestToBeAuthed.registry_domain).first
|
57
|
+
if osx_cred_entry.nil?
|
58
|
+
puts "Not found any keychain entry for registry #{@requestToBeAuthed.registry_domain}, please auth using: docker login #{@requestToBeAuthed.registry_domain}".red if @debug
|
59
|
+
throw 'Keychain entry not found'
|
60
|
+
elsif @debug
|
61
|
+
puts "Found user '#{osx_cred_entry.account}' in keychain".white
|
62
|
+
end
|
63
|
+
# get the bearer token
|
64
|
+
options[:basic_auth] = {username: osx_cred_entry.account, password: osx_cred_entry.password}
|
65
|
+
end
|
66
|
+
|
67
|
+
response = HTTParty.get(url, options)
|
36
68
|
|
37
69
|
throw "Failed to authenticate, code #{response.code}" unless response.code == 200 || response.has_key?('token')
|
38
70
|
# decorate our request object
|
39
|
-
|
71
|
+
@requestToBeAuthed.class.headers['authorization'] = "Bearer #{response['token']}"
|
40
72
|
end
|
41
73
|
end
|
data/bin/docker_registry_cli
CHANGED
@@ -7,7 +7,7 @@ require 'pp'
|
|
7
7
|
require_relative '../commands/DockerRegistryCommand'
|
8
8
|
|
9
9
|
# our defaults / defines
|
10
|
-
options = {:user => nil
|
10
|
+
options = {:user => nil, :password => nil, :registry_domain => nil, :debug => false}
|
11
11
|
ops = ['list','search', 'tags', 'delete_image', 'delete_tag']
|
12
12
|
|
13
13
|
# define our options and help
|
@@ -20,10 +20,10 @@ OptionParser.new do |opts|
|
|
20
20
|
opts.on("-p", "--password PASSWORD", "optional, password to login") do |v|
|
21
21
|
options[:password] = v
|
22
22
|
end
|
23
|
-
opts.on("--domain DOMAIN", "Set this to override the domain you defined in ~./docker_registry.yml") do |v|
|
24
|
-
options[:
|
23
|
+
opts.on("-d", "--domain DOMAIN", "Set this to override the domain you defined in ~./docker_registry.yml") do |v|
|
24
|
+
options[:registry_domain] = v
|
25
25
|
end
|
26
|
-
opts.on("-
|
26
|
+
opts.on("-v", "--verbose", "verbose mode") do |v|
|
27
27
|
options[:debug] = v
|
28
28
|
end
|
29
29
|
opts.on("-h", "--help", "Prints this help") do
|
@@ -52,7 +52,7 @@ begin
|
|
52
52
|
pp config
|
53
53
|
end
|
54
54
|
|
55
|
-
options[:
|
55
|
+
options[:registry_domain] = config['domain'] if config['domain'] && !options[:registry_domain]
|
56
56
|
options[:user] = config['user'] if config['user'] && !options[:user]
|
57
57
|
options[:password] = config['password'] if config['password'] && !options[:password]
|
58
58
|
rescue
|
@@ -62,7 +62,7 @@ rescue
|
|
62
62
|
puts 'The first argument should be your registry domain without schema (HTTPS mandatory), optional with :port'.colorize(:red)
|
63
63
|
exit 1
|
64
64
|
else
|
65
|
-
options[:
|
65
|
+
options[:registry_domain] = ARGV.shift
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
@@ -86,7 +86,7 @@ if options[:debug]
|
|
86
86
|
end
|
87
87
|
|
88
88
|
# configure our request handler
|
89
|
-
registry = DockerRegistryCommand.new(options[:
|
89
|
+
registry = DockerRegistryCommand.new(options[:registry_domain], options[:user], options[:password], options[:debug])
|
90
90
|
|
91
91
|
# run the operations, which can actually have different amounts of mandatory arguments
|
92
92
|
case op
|
@@ -7,7 +7,7 @@ class DockerRegistryCommand < DockerRegistryRequest
|
|
7
7
|
def list(search_key = nil)
|
8
8
|
response = send_get_request("/_catalog")
|
9
9
|
if response['repositories'].nil?
|
10
|
-
puts
|
10
|
+
puts 'no repositories found'
|
11
11
|
exit 1
|
12
12
|
else
|
13
13
|
response['repositories'].each { |repo|
|
@@ -14,45 +14,54 @@ class DockerRegistryRequest
|
|
14
14
|
format :json
|
15
15
|
headers 'Content-Type' => 'application/json'
|
16
16
|
headers 'Accept' => 'application/json'
|
17
|
-
|
17
|
+
@debug = false
|
18
|
+
@registry_domain = nil
|
19
|
+
def initialize(registry_domain, user = nil, pass = nil, debug = false)
|
20
|
+
@debug = debug
|
21
|
+
@registry_domain = registry_domain
|
22
|
+
self.class.base_uri "https://#{registry_domain}/v2"
|
23
|
+
handle_pre_auth(registry_domain, user, pass)
|
24
|
+
end
|
25
|
+
|
26
|
+
def registry_domain
|
27
|
+
@registry_domain
|
28
|
+
end
|
18
29
|
|
19
|
-
def
|
20
|
-
|
21
|
-
self.class.base_uri "https://#{domain}/v2"
|
22
|
-
handle_preauth(domain, user, pass)
|
30
|
+
def is_debug
|
31
|
+
return @debug
|
23
32
|
end
|
24
33
|
|
25
|
-
def
|
34
|
+
def handle_pre_auth(domain, user = nil, pass = nil)
|
26
35
|
# Only BasicAuth is supported
|
27
|
-
|
36
|
+
auth_service = BasicAuthService.new(self)
|
28
37
|
begin
|
29
38
|
if user && pass
|
30
39
|
# this will base64 encode automatically
|
31
|
-
|
40
|
+
auth_service.byCredentials(user, pass)
|
32
41
|
else
|
33
|
-
|
42
|
+
auth_service.byToken
|
34
43
|
end
|
35
44
|
rescue Exception
|
36
|
-
puts
|
45
|
+
puts 'No BasicAuth pre-auth available, will try to use different auth-services later'.green if is_debug
|
37
46
|
end
|
38
47
|
|
39
48
|
end
|
40
49
|
|
41
50
|
### check if the login actually will succeed
|
42
51
|
def authenticate(response)
|
43
|
-
headers = response.headers
|
52
|
+
headers = response.headers
|
44
53
|
begin
|
45
54
|
if headers.has_key?('www-authenticate')
|
46
55
|
auth_description = headers['www-authenticate']
|
47
56
|
if auth_description.match('Bearer realm=')
|
48
|
-
|
49
|
-
|
57
|
+
auth_service = TokenAuthService.new(self)
|
58
|
+
auth_service.tokenAuth(response)
|
50
59
|
else
|
51
60
|
throw "Auth method not supported #{auth_description}"
|
52
61
|
end
|
53
62
|
end
|
54
|
-
rescue Exception
|
55
|
-
puts "Authentication failed".colorize(:red)
|
63
|
+
rescue Exception => e
|
64
|
+
puts "Authentication failed #{e.message}".colorize(:red)
|
56
65
|
end
|
57
66
|
end
|
58
67
|
|
@@ -73,7 +82,7 @@ class DockerRegistryRequest
|
|
73
82
|
unless response.code == 200
|
74
83
|
throw "Could not finish request, status #{response.code}"
|
75
84
|
end
|
76
|
-
|
85
|
+
response
|
77
86
|
end
|
78
87
|
|
79
88
|
## sends a delete request, authenticates if needed
|
@@ -91,7 +100,7 @@ class DockerRegistryRequest
|
|
91
100
|
if response.code != 200 && response.code != 202
|
92
101
|
throw "Could not finish request, status #{response.code}"
|
93
102
|
end
|
94
|
-
|
103
|
+
response
|
95
104
|
end
|
96
105
|
|
97
106
|
### returns the digest for a tag
|
@@ -114,6 +123,6 @@ class DockerRegistryRequest
|
|
114
123
|
exit 1
|
115
124
|
end
|
116
125
|
|
117
|
-
|
126
|
+
response.headers['docker-content-digest']
|
118
127
|
end
|
119
128
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: docker_registry_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eugen Mayer
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ruby-keychain
|
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'
|
41
55
|
description: This cli-tool lets you query your private docker registry for different
|
42
56
|
things. For now, there is no tool provided by docker to do so
|
43
57
|
email: eugen.mayer@kontextwork.de
|