docker_registry_cli 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|