ccli 0.1.1 → 1.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/.rubocop.yml +2 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +27 -0
- data/Gemfile +3 -3
- data/Gemfile.lock +10 -12
- data/README.md +63 -20
- data/bin/cry +1 -1
- data/ccli.gemspec +15 -6
- data/docs/get_login_token.md +18 -0
- data/docs/images/access_user_settings.png +0 -0
- data/docs/images/copy_ccli_login.png +0 -0
- data/lib/adapters/cluster_secret_adapter.rb +1 -1
- data/lib/adapters/cryptopus_adapter.rb +10 -11
- data/lib/cli.rb +34 -35
- data/lib/errors.rb +1 -1
- data/lib/models/encryptable.rb +44 -0
- data/lib/models/folder.rb +7 -7
- data/lib/models/ose_secret.rb +16 -2
- data/lib/serializers/encryptable_serializer.rb +57 -0
- data/lib/serializers/ose_secret_serializer.rb +42 -4
- metadata +30 -21
- data/lib/models/account.rb +0 -44
- data/lib/serializers/account_serializer.rb +0 -57
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d2f2f0b3d9151f52f911b794e164119054e2d281d3291f06a8e04b3ada1382d0
|
|
4
|
+
data.tar.gz: 5fef82bab17bf44d9b922c8ef543c89405e8aee145cf98c138c932d42c37bb4a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 10b197547309059384bb1fdd65135f5950cbf33cbbb9aed7fb5e871cb436cb493ef8524902e31318dbabd4fd1a98d26a4f769937845e97eb72e5a6c65ad43093
|
|
7
|
+
data.tar.gz: 6ef9539d2512c4ec66ae63d5eb400bc06fd1d8c33147304dffb2f2527ed3f74ba9e7e64b7b85f2da88806bef4f9be18c41ffd73ebe6cfb3ab04a92be4902fbfa
|
data/.rubocop.yml
CHANGED
data/.tool-versions
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby 2.6.0
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
- Rename Account to Encryptable to support Cryptopus > 4.2
|
|
6
|
+
|
|
7
|
+
## 1.0.1
|
|
8
|
+
|
|
9
|
+
- Reset api user token after login
|
|
10
|
+
|
|
11
|
+
## 1.0.0
|
|
12
|
+
|
|
13
|
+
- De- and encode data from secrets
|
|
14
|
+
|
|
15
|
+
## 0.1.2
|
|
16
|
+
|
|
17
|
+
- Updating docs
|
|
18
|
+
- Bugfixing
|
|
19
|
+
|
|
20
|
+
## 0.1.1
|
|
21
|
+
|
|
22
|
+
- Adding MIT license
|
|
23
|
+
|
|
24
|
+
## 0.1.0
|
|
25
|
+
|
|
26
|
+
- Publish first version
|
|
27
|
+
- Commands: `login`, `logout`, `account`, `folder`, `{ose,k8s}-secret-pull`, `{ose,k8s}-secret-push`, `teams`, `use`
|
data/Gemfile
CHANGED
|
@@ -5,9 +5,9 @@ source 'https://rubygems.org'
|
|
|
5
5
|
gem 'commander', '~> 4.5', '>= 4.5.2'
|
|
6
6
|
gem 'rspec', '~> 3.9'
|
|
7
7
|
gem 'rubocop', '~> 0.89.0'
|
|
8
|
-
gem 'tty-command'
|
|
9
|
-
gem 'tty-exit'
|
|
10
|
-
gem 'tty-logger'
|
|
8
|
+
gem 'tty-command', '~> 0.10'
|
|
9
|
+
gem 'tty-exit', '~> 0.1'
|
|
10
|
+
gem 'tty-logger', '~> 0.6'
|
|
11
11
|
|
|
12
12
|
gem 'pry'
|
|
13
13
|
gem 'pry-byebug'
|
data/Gemfile.lock
CHANGED
|
@@ -7,14 +7,12 @@ GEM
|
|
|
7
7
|
commander (4.5.2)
|
|
8
8
|
highline (~> 2.0.0)
|
|
9
9
|
diff-lcs (1.4.4)
|
|
10
|
-
equatable (0.6.1)
|
|
11
10
|
highline (2.0.3)
|
|
12
11
|
method_source (1.0.0)
|
|
13
12
|
parallel (1.19.2)
|
|
14
13
|
parser (2.7.1.4)
|
|
15
14
|
ast (~> 2.4.1)
|
|
16
|
-
pastel (0.
|
|
17
|
-
equatable (~> 0.6)
|
|
15
|
+
pastel (0.8.0)
|
|
18
16
|
tty-color (~> 0.5)
|
|
19
17
|
pry (0.13.1)
|
|
20
18
|
coderay (~> 1.1)
|
|
@@ -24,7 +22,7 @@ GEM
|
|
|
24
22
|
pry (~> 0.13.0)
|
|
25
23
|
rainbow (3.0.0)
|
|
26
24
|
regexp_parser (1.7.1)
|
|
27
|
-
rexml (3.2.
|
|
25
|
+
rexml (3.2.5)
|
|
28
26
|
rspec (3.9.0)
|
|
29
27
|
rspec-core (~> 3.9.0)
|
|
30
28
|
rspec-expectations (~> 3.9.0)
|
|
@@ -50,12 +48,12 @@ GEM
|
|
|
50
48
|
rubocop-ast (0.3.0)
|
|
51
49
|
parser (>= 2.7.1.4)
|
|
52
50
|
ruby-progressbar (1.10.1)
|
|
53
|
-
tty-color (0.
|
|
54
|
-
tty-command (0.
|
|
55
|
-
pastel (~> 0.
|
|
51
|
+
tty-color (0.6.0)
|
|
52
|
+
tty-command (0.10.1)
|
|
53
|
+
pastel (~> 0.8)
|
|
56
54
|
tty-exit (0.1.0)
|
|
57
|
-
tty-logger (0.
|
|
58
|
-
pastel (~> 0.
|
|
55
|
+
tty-logger (0.6.0)
|
|
56
|
+
pastel (~> 0.8)
|
|
59
57
|
unicode-display_width (1.7.0)
|
|
60
58
|
|
|
61
59
|
PLATFORMS
|
|
@@ -67,9 +65,9 @@ DEPENDENCIES
|
|
|
67
65
|
pry-byebug
|
|
68
66
|
rspec (~> 3.9)
|
|
69
67
|
rubocop (~> 0.89.0)
|
|
70
|
-
tty-command
|
|
71
|
-
tty-exit
|
|
72
|
-
tty-logger
|
|
68
|
+
tty-command (~> 0.10)
|
|
69
|
+
tty-exit (~> 0.1)
|
|
70
|
+
tty-logger (~> 0.6)
|
|
73
71
|
|
|
74
72
|
BUNDLED WITH
|
|
75
73
|
2.1.4
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ccli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Command Line Client for [Cryptopus](https://github.com/puzzle/cryptopus)
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,42 +10,81 @@ This will install the `cry` command including its dependencies
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
-
- Fetch
|
|
13
|
+
- Fetch encryptable data from Cryptopus
|
|
14
14
|
- List accessable teams in Cryptopus
|
|
15
15
|
- Sync Openshift/Kubernetes Secrets to Cryptopus
|
|
16
16
|
- Sync Secrets from Cryptopus to Openshift/Kubernetes
|
|
17
17
|
|
|
18
18
|
## Usage
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
[Receiving the login token from Cryptopus](docs/get_login_token.md)
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
### Commands
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
```
|
|
25
|
+
Command: Summary:
|
|
25
26
|
|
|
27
|
+
encryptable Fetches an encryptable by the given id
|
|
28
|
+
folder Selects the Cryptopus folder by id
|
|
29
|
+
help Display global or [command] help documentation
|
|
30
|
+
k8s-secret-pull Pulls secret from Kubectl to Cryptopus
|
|
31
|
+
k8s-secret-push Pushes secret from Cryptopus to Kubectl
|
|
32
|
+
login Logs in to the ccli
|
|
33
|
+
logout Logs out of the ccli
|
|
34
|
+
ose-secret-pull Pulls secret from Openshift to Cryptopus
|
|
35
|
+
ose-secret-push Pushes secret from Cryptopus to Openshift
|
|
36
|
+
teams Lists all available teams
|
|
37
|
+
use Select the current folder
|
|
38
|
+
```
|
|
26
39
|
|
|
27
|
-
|
|
40
|
+
Show more specific documentation by calling `cry help <command>`
|
|
28
41
|
|
|
29
|
-
###
|
|
42
|
+
### Account
|
|
43
|
+
|
|
44
|
+
#### Logging in
|
|
45
|
+
|
|
46
|
+
Use the ccli login copy button from the UI or do it manually:
|
|
47
|
+
|
|
48
|
+
user=<my-user>
|
|
49
|
+
token=<my-token>
|
|
50
|
+
url=https://cryptopus.example.com
|
|
51
|
+
|
|
52
|
+
cry login $(echo -n "$user:$token" | base64)@$url
|
|
53
|
+
|
|
54
|
+
#### Retrieving
|
|
55
|
+
|
|
56
|
+
To retreive encryptable data as yaml:
|
|
30
57
|
|
|
31
58
|
```
|
|
32
|
-
|
|
59
|
+
cry encryptable 42 > encryptable.yaml
|
|
60
|
+
```
|
|
61
|
+
Retreiving encryptable's password and assign it to a variable:
|
|
33
62
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
help Display global or [command] help documentation
|
|
37
|
-
k8s-secret-pull Pulls secret from Kubectl to Cryptopus
|
|
38
|
-
k8s-secret-push Pushes secret from Cryptopus to Kubectl
|
|
39
|
-
login Logs in to the ccli
|
|
40
|
-
logout Logs out of the ccli
|
|
41
|
-
ose-secret-pull Pulls secret from Openshift to Cryptopus
|
|
42
|
-
ose-secret-push Pushes secret from Cryptopus to Openshift
|
|
43
|
-
teams Lists all available teams
|
|
44
|
-
use Select the current folder
|
|
63
|
+
```
|
|
64
|
+
PASSWORD=$(cry encryptable 42 --password)
|
|
45
65
|
```
|
|
46
66
|
|
|
47
|
-
|
|
67
|
+
#### Updating
|
|
68
|
+
|
|
69
|
+
not supported yet by ccli
|
|
70
|
+
|
|
71
|
+
### Kubernetes/Openshift
|
|
72
|
+
|
|
73
|
+
#### Required tools
|
|
74
|
+
|
|
75
|
+
First you'll have to install either [oc](https://docs.openshift.com/container-platform/4.3/cli_reference/openshift_cli/getting-started-cli.html#installing-the-cli) or [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) depending on your usage
|
|
76
|
+
|
|
77
|
+
#### Pulling Kubernetes / Openshift Secrets
|
|
78
|
+
|
|
79
|
+
when using the command `{ose|k8s}-secret-pull` after beeing logged in to a k8s/ose project, all secrets labeled with `cryptopus-sync=true` are backed up to cryptopus.
|
|
48
80
|
|
|
81
|
+
to label a specific secret do:
|
|
82
|
+
|
|
83
|
+
**oc:** `oc label secret <secret-name> cryptopus-sync=true`
|
|
84
|
+
|
|
85
|
+
**kubectl:** `kubectl label secret <secret-name> cryptopus-sync=true`
|
|
86
|
+
|
|
87
|
+
Restored secrets by `{ose|k8s}-secret-push` are labeled automatically.
|
|
49
88
|
|
|
50
89
|
## Development
|
|
51
90
|
|
|
@@ -61,3 +100,7 @@ You will need the following things properly installed on your computer:
|
|
|
61
100
|
- `rvm install 2.6.0`
|
|
62
101
|
- `gem install bundler`
|
|
63
102
|
- `bundle install`
|
|
103
|
+
|
|
104
|
+
### Running tests
|
|
105
|
+
|
|
106
|
+
`bundle exec rspec`
|
data/bin/cry
CHANGED
data/ccli.gemspec
CHANGED
|
@@ -5,8 +5,15 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
|
5
5
|
|
|
6
6
|
Gem::Specification.new do |s|
|
|
7
7
|
s.name = 'ccli'
|
|
8
|
-
s.
|
|
8
|
+
s.description = <<-EOF
|
|
9
|
+
CCLI is the Cryptopus Command Line Interface. It allows to fetch encryptable data and list teams from Cryptopus.
|
|
10
|
+
One of the main functionality is backing up secrets from cluster services (currently: openshift, kubernetes)
|
|
11
|
+
to Cryptopus and restoring them as well.
|
|
12
|
+
EOF
|
|
13
|
+
s.version = '1.1.0'
|
|
9
14
|
s.summary = 'Command line client for the opensource password manager Cryptopus'
|
|
15
|
+
s.license = 'MIT'
|
|
16
|
+
s.homepage = 'https://github.com/puzzle/ccli'
|
|
10
17
|
s.authors = ['Nils Rauch']
|
|
11
18
|
s.email = 'rauch@puzzle.ch'
|
|
12
19
|
s.require_paths = ['lib']
|
|
@@ -17,12 +24,14 @@ Gem::Specification.new do |s|
|
|
|
17
24
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
25
|
s.required_ruby_version = Gem::Requirement.new('>= 2.0')
|
|
19
26
|
s.metadata = {
|
|
20
|
-
"
|
|
27
|
+
"bug_tracker_uri" => "https://github.com/puzzle/ccli/issues",
|
|
28
|
+
"changelog_uri" => "https://github.com/puzzle/ccli/blob/master/CHANGELOG.md",
|
|
29
|
+
"source_code_uri" => "https://github.com/puzzle/ccli"
|
|
21
30
|
}
|
|
22
|
-
s.license = 'MIT'
|
|
23
31
|
|
|
24
32
|
s.add_runtime_dependency 'commander', '~> 4.5', '>= 4.5.2'
|
|
25
|
-
s.add_runtime_dependency 'tty-command'
|
|
26
|
-
s.add_runtime_dependency 'tty-exit'
|
|
27
|
-
s.add_runtime_dependency 'tty-logger'
|
|
33
|
+
s.add_runtime_dependency 'tty-command', '~> 0.10'
|
|
34
|
+
s.add_runtime_dependency 'tty-exit', '~> 0.1'
|
|
35
|
+
s.add_runtime_dependency 'tty-logger', '~> 0.6'
|
|
36
|
+
|
|
28
37
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Receiving the Login token from Cryptopus
|
|
2
|
+
|
|
3
|
+
To use the CCLI, you'll first have to receive the login token from Cryptopus.
|
|
4
|
+
|
|
5
|
+
1. Log in to your instance of Cryptopus
|
|
6
|
+
2. Navigate to your user settings
|
|
7
|
+
3. Choose or create the api user you want to use via the ccli (keep the valid time in mind)
|
|
8
|
+
4. Grant the API user permissions to access the groups you need to use with the ccli
|
|
9
|
+
5. Use the ccli login copy button
|
|
10
|
+
6. Copy the command from your clipboard to the terminal
|
|
11
|
+
|
|
12
|
+
## Accessing user settings
|
|
13
|
+
|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
## Copy CCLI Login
|
|
17
|
+
|
|
18
|
+

|
|
Binary file
|
|
Binary file
|
|
@@ -35,7 +35,7 @@ class ClusterSecretAdapter
|
|
|
35
35
|
raise client_not_logged_in_error unless client_logged_in?
|
|
36
36
|
|
|
37
37
|
File.open("/tmp/#{secret.name}.yml", 'w') do |file|
|
|
38
|
-
file.write secret.
|
|
38
|
+
file.write secret.to_yaml
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
cmd.run("#{client} delete -f /tmp/#{secret.name}.yml --ignore-not-found=true")
|
|
@@ -34,24 +34,23 @@ class CryptopusAdapter
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def save_secret(secret)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
session_adapter.selected_folder.id)
|
|
37
|
+
secret_encryptable = secret.to_encryptable
|
|
38
|
+
secret_encryptable.folder = session_adapter.selected_folder.id
|
|
39
|
+
persisted_secret = Encryptable.find_by_name_and_folder_id(secret.name,
|
|
40
|
+
session_adapter.selected_folder.id)
|
|
42
41
|
if persisted_secret
|
|
43
|
-
patch("
|
|
42
|
+
patch("encryptables/#{persisted_secret.id}", secret_encryptable.to_json)
|
|
44
43
|
else
|
|
45
|
-
post('
|
|
44
|
+
post('encryptables', secret_encryptable.to_json)
|
|
46
45
|
end
|
|
47
46
|
end
|
|
48
47
|
|
|
49
|
-
def
|
|
50
|
-
|
|
48
|
+
def find_encryptable_by_name(name)
|
|
49
|
+
secret_encryptable = Encryptable.find_by_name_and_folder_id(name, session_adapter.selected_folder.id)
|
|
51
50
|
|
|
52
|
-
raise
|
|
51
|
+
raise CryptopusEncryptableNotFoundError unless secret_encryptable
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
secret_encryptable
|
|
55
54
|
end
|
|
56
55
|
|
|
57
56
|
def renewed_auth_token
|
data/lib/cli.rb
CHANGED
|
@@ -14,7 +14,7 @@ class CLI
|
|
|
14
14
|
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metric/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/BlockLength
|
|
15
15
|
def run
|
|
16
16
|
program :name, 'cry - cryptopus cli'
|
|
17
|
-
program :version, '1.
|
|
17
|
+
program :version, '1.1.0'
|
|
18
18
|
program :description, 'CLI tool to manage Openshift Secrets via Cryptopus'
|
|
19
19
|
program :help, 'Source Code', 'https://www.github.com/puzzle/ccli'
|
|
20
20
|
program :help, 'Usage', 'cry [flags]'
|
|
@@ -49,20 +49,20 @@ class CLI
|
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
command :
|
|
53
|
-
c.syntax = 'cry
|
|
54
|
-
c.description = 'Fetches an
|
|
52
|
+
command :encryptable do |c|
|
|
53
|
+
c.syntax = 'cry encryptable <id> [options]'
|
|
54
|
+
c.description = 'Fetches an encryptable by the given id'
|
|
55
55
|
c.option '--username', String, 'Only show the username of the user'
|
|
56
56
|
c.option '--password', String, 'Only show the password of the user'
|
|
57
57
|
|
|
58
58
|
c.action do |args, options|
|
|
59
59
|
exit_with_error(:usage_error, 'id missing') if args.empty?
|
|
60
60
|
execute_action do
|
|
61
|
-
logger.info 'Fetching
|
|
62
|
-
|
|
63
|
-
out =
|
|
64
|
-
out =
|
|
65
|
-
puts out ||
|
|
61
|
+
logger.info 'Fetching encryptable...'
|
|
62
|
+
encryptable = Encryptable.find(args.first)
|
|
63
|
+
out = encryptable.username if options.username
|
|
64
|
+
out = encryptable.password if options.password
|
|
65
|
+
puts out || encryptable.to_yaml
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
end
|
|
@@ -120,25 +120,25 @@ class CLI
|
|
|
120
120
|
c.summary = 'Pushes secret from Cryptopus to Openshift'
|
|
121
121
|
c.description = 'Pushes the Secret to Openshift by retrieving it from Cryptopus first. ' \
|
|
122
122
|
'If a Secret in the selected Openshift project using the name ' \
|
|
123
|
-
'of the given
|
|
123
|
+
'of the given name is already present, it will be updated accordingly.'
|
|
124
124
|
|
|
125
125
|
c.action do |args|
|
|
126
126
|
secret_name = args.first
|
|
127
127
|
exit_with_error(:usage_error, 'Only one secret can be pushed') if args.length > 1
|
|
128
128
|
execute_action({ secret_name: secret_name }) do
|
|
129
|
-
|
|
130
|
-
logger.info 'Fetching all
|
|
131
|
-
session_adapter.selected_folder.
|
|
129
|
+
secret_encryptables = if secret_name.nil?
|
|
130
|
+
logger.info 'Fetching all encryptables in folder...'
|
|
131
|
+
session_adapter.selected_folder.encryptables
|
|
132
132
|
else
|
|
133
|
-
logger.info "Fetching
|
|
134
|
-
[cryptopus_adapter.
|
|
133
|
+
logger.info "Fetching encryptable #{secret_name}..."
|
|
134
|
+
[cryptopus_adapter.find_encryptable_by_name(secret_name)]
|
|
135
135
|
end
|
|
136
|
-
|
|
137
|
-
logger.info "Fetching secret #{
|
|
138
|
-
|
|
139
|
-
logger.info "Inserting secret #{
|
|
140
|
-
ose_adapter.insert_secret(
|
|
141
|
-
log_success "Secret #{
|
|
136
|
+
secret_encryptables.each do |encryptable|
|
|
137
|
+
logger.info "Fetching secret #{encryptable.name}..."
|
|
138
|
+
secret_encryptable = Encryptable.find(encryptable.id)
|
|
139
|
+
logger.info "Inserting secret #{encryptable.name}..."
|
|
140
|
+
ose_adapter.insert_secret(secret_encryptable.to_osesecret)
|
|
141
|
+
log_success "Secret #{secret_encryptable.name} was successfully applied"
|
|
142
142
|
end
|
|
143
143
|
end
|
|
144
144
|
end
|
|
@@ -148,7 +148,7 @@ class CLI
|
|
|
148
148
|
c.syntax = 'cry k8s-secret-pull <secret-name>'
|
|
149
149
|
c.summary = 'Pulls secret from Kubectl to Cryptopus'
|
|
150
150
|
c.description = "Pulls the Secret from Kubectl and pushes them to Cryptopus.\n" \
|
|
151
|
-
'If a Cryptopus
|
|
151
|
+
'If a Cryptopus Encryptable in the selected folder using the name ' \
|
|
152
152
|
"of the given secret is already present, it will be updated accordingly.\n" \
|
|
153
153
|
'If no name is given, it will pull all secrets inside the selected project.'
|
|
154
154
|
|
|
@@ -180,25 +180,24 @@ class CLI
|
|
|
180
180
|
c.summary = 'Pushes secret from Cryptopus to Kubectl'
|
|
181
181
|
c.description = 'Pushes the Secret to Kubectl by retrieving it from Cryptopus first. ' \
|
|
182
182
|
'If a Secret in the selected Kubectl project using the name ' \
|
|
183
|
-
'of the given
|
|
183
|
+
'of the given name is already present, it will be updated accordingly.'
|
|
184
184
|
|
|
185
185
|
c.action do |args|
|
|
186
186
|
secret_name = args.first
|
|
187
187
|
exit_with_error(:usage_error, 'Only one secret can be pushed') if args.length > 1
|
|
188
188
|
execute_action({ secret_name: secret_name }) do
|
|
189
|
-
|
|
190
|
-
logger.info 'Fetching all
|
|
191
|
-
session_adapter.selected_folder.
|
|
189
|
+
secret_encryptables = if secret_name.nil?
|
|
190
|
+
logger.info 'Fetching all encryptables in folder...'
|
|
191
|
+
session_adapter.selected_folder.encryptables
|
|
192
192
|
else
|
|
193
|
-
logger.info "Fetching
|
|
194
|
-
[cryptopus_adapter.
|
|
193
|
+
logger.info "Fetching encryptable #{secret_name}..."
|
|
194
|
+
[cryptopus_adapter.find_encryptable_by_name(secret_name)]
|
|
195
195
|
end
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
log_success "Secret #{secret_account.accountname} was successfully applied"
|
|
196
|
+
secret_encryptables.each do |encryptable|
|
|
197
|
+
secret_encryptable = Encryptable.find(encryptable.id)
|
|
198
|
+
logger.info "Inserting secret #{encryptable.name}..."
|
|
199
|
+
k8s_adapter.insert_secret(secret_encryptable.to_osesecret)
|
|
200
|
+
log_success "Secret #{secret_encryptable.name} was successfully applied"
|
|
202
201
|
end
|
|
203
202
|
end
|
|
204
203
|
end
|
|
@@ -264,7 +263,7 @@ class CLI
|
|
|
264
263
|
exit_with_error(:usage_error, 'kubectl is not installed')
|
|
265
264
|
rescue KubernetesClientNotLoggedInError
|
|
266
265
|
exit_with_error(:usage_error, 'kubectl is not logged in')
|
|
267
|
-
rescue
|
|
266
|
+
rescue CryptopusEncryptableNotFoundError
|
|
268
267
|
exit_with_error(:usage_error, 'Secret with the given name ' \
|
|
269
268
|
"#{options[:secret_name]} was not found")
|
|
270
269
|
rescue OpenshiftSecretNotFoundError
|
data/lib/errors.rb
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Encryptable
|
|
4
|
+
attr_reader :id, :name, :username, :password, :type, :ose_secret
|
|
5
|
+
attr_accessor :folder
|
|
6
|
+
|
|
7
|
+
def initialize(name: nil, username: nil, password: nil,
|
|
8
|
+
ose_secret: nil, type: nil, id: nil)
|
|
9
|
+
@id = id
|
|
10
|
+
@name = name
|
|
11
|
+
@username = username
|
|
12
|
+
@password = password
|
|
13
|
+
@ose_secret = ose_secret
|
|
14
|
+
@type = type || 'credentials'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_json(*_args)
|
|
18
|
+
EncryptableSerializer.to_json(self)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_yaml
|
|
22
|
+
EncryptableSerializer.to_yaml(self)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_osesecret
|
|
26
|
+
EncryptableSerializer.to_osesecret(self)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
def find(id)
|
|
31
|
+
EncryptableSerializer.from_json(CryptopusAdapter.new.get("encryptables/#{id}"))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def find_by_name_and_folder_id(name, id)
|
|
35
|
+
Folder.find(id).encryptables.find do |encryptable|
|
|
36
|
+
encryptable.name.downcase == name.downcase
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def from_json(json)
|
|
41
|
+
EncryptableSerializer.from_json(json)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/models/folder.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Folder
|
|
4
|
-
attr_reader :name, :id, :
|
|
4
|
+
attr_reader :name, :id, :encryptables
|
|
5
5
|
|
|
6
|
-
def initialize(name: nil, id: nil,
|
|
6
|
+
def initialize(name: nil, id: nil, encryptables: [])
|
|
7
7
|
@name = name
|
|
8
8
|
@id = id
|
|
9
|
-
@
|
|
9
|
+
@encryptables = encryptables
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
class << self
|
|
@@ -15,11 +15,11 @@ class Folder
|
|
|
15
15
|
symbolize_names: true)
|
|
16
16
|
included = json[:included] || []
|
|
17
17
|
name = json[:data][:attributes][:name]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
encryptables = included.map do |record|
|
|
19
|
+
Encryptable.from_json(record.to_json) if %w[encryptable_ose_secrets
|
|
20
|
+
encryptable_credentials].include? record[:type]
|
|
21
21
|
end.compact
|
|
22
|
-
Folder.new(id: id, name: name,
|
|
22
|
+
Folder.new(id: id, name: name, encryptables: encryptables)
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
data/lib/models/ose_secret.rb
CHANGED
|
@@ -8,15 +8,29 @@ class OSESecret
|
|
|
8
8
|
@ose_secret = ose_secret
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def
|
|
12
|
-
OSESecretSerializer.
|
|
11
|
+
def to_encryptable
|
|
12
|
+
OSESecretSerializer.to_encryptable(self)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def to_yaml
|
|
16
16
|
OSESecretSerializer.to_yaml(self)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def encoded_data(data)
|
|
22
|
+
data.transform_values do |value|
|
|
23
|
+
Base64.strict_encode64(value)
|
|
24
|
+
rescue ArgumentError
|
|
25
|
+
value
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
19
29
|
class << self
|
|
30
|
+
def from_yaml(yaml)
|
|
31
|
+
OSESecretSerializer.from_yaml(yaml)
|
|
32
|
+
end
|
|
33
|
+
|
|
20
34
|
def find_by_name(name)
|
|
21
35
|
OSESecretSerializer.from_yaml(OSEAdapter.new.fetch_secret(name))
|
|
22
36
|
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
class EncryptableSerializer
|
|
6
|
+
class << self
|
|
7
|
+
# rubocop:disable Metrics/MethodLength
|
|
8
|
+
def to_json(encryptable)
|
|
9
|
+
{
|
|
10
|
+
data: {
|
|
11
|
+
type: 'encryptables',
|
|
12
|
+
id: encryptable.id,
|
|
13
|
+
attributes: {
|
|
14
|
+
name: encryptable.name,
|
|
15
|
+
type: encryptable.type,
|
|
16
|
+
cleartext_username: encryptable.username,
|
|
17
|
+
cleartext_password: encryptable.password,
|
|
18
|
+
cleartext_ose_secret: encryptable.ose_secret
|
|
19
|
+
},
|
|
20
|
+
relationships: {
|
|
21
|
+
folder: {
|
|
22
|
+
data: {
|
|
23
|
+
id: encryptable.folder,
|
|
24
|
+
type: 'folders'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}.compact.to_json
|
|
30
|
+
end
|
|
31
|
+
# rubocop:enable Metrics/MethodLength
|
|
32
|
+
|
|
33
|
+
def to_yaml(encryptable)
|
|
34
|
+
{ 'id' => encryptable.id,
|
|
35
|
+
'name' => encryptable.name,
|
|
36
|
+
'username' => encryptable.username,
|
|
37
|
+
'password' => encryptable.password,
|
|
38
|
+
'type' => encryptable.type }.to_yaml
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def from_json(json)
|
|
42
|
+
json = JSON.parse(json, symbolize_names: true)
|
|
43
|
+
data = json[:data] || json
|
|
44
|
+
attributes = data[:attributes]
|
|
45
|
+
Encryptable.new(name: attributes[:name],
|
|
46
|
+
username: attributes[:cleartext_username],
|
|
47
|
+
password: attributes[:cleartext_password],
|
|
48
|
+
ose_secret: attributes[:ose_secret],
|
|
49
|
+
type: attributes[:type],
|
|
50
|
+
id: data[:id])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def to_osesecret(account)
|
|
54
|
+
OSESecret.from_yaml(account.ose_secret)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -1,16 +1,54 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'psych'
|
|
4
|
+
require 'base64'
|
|
4
5
|
|
|
5
6
|
class OSESecretSerializer
|
|
6
7
|
class << self
|
|
8
|
+
# rubocop:disable Metrics/MethodLength
|
|
7
9
|
def from_yaml(yaml)
|
|
8
|
-
secret_hash = Psych.load(yaml
|
|
9
|
-
|
|
10
|
+
secret_hash = Psych.load(yaml)
|
|
11
|
+
data = {
|
|
12
|
+
'apiVersion' => secret_hash['apiVersion'],
|
|
13
|
+
'data' => decoded_data(secret_hash['data']),
|
|
14
|
+
'kind' => secret_hash['kind'],
|
|
15
|
+
'metadata' => {
|
|
16
|
+
'name' => secret_hash['metadata']['name'],
|
|
17
|
+
'labels' => secret_hash['metadata']['labels']
|
|
18
|
+
}
|
|
19
|
+
}.to_yaml
|
|
20
|
+
OSESecret.new(secret_hash['metadata']['name'], data.to_s)
|
|
10
21
|
end
|
|
22
|
+
# rubocop:enable Metrics/MethodLength
|
|
11
23
|
|
|
12
|
-
def
|
|
13
|
-
|
|
24
|
+
def to_encryptable(secret)
|
|
25
|
+
Encryptable.new(name: secret.name, ose_secret: secret.ose_secret, type: 'ose_secret')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_yaml(secret)
|
|
29
|
+
secret_hash = Psych.load(secret.ose_secret)
|
|
30
|
+
secret_hash['data'] = encoded_data(secret_hash['data'])
|
|
31
|
+
secret_hash.to_yaml
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def decoded_data(data)
|
|
37
|
+
return {} unless data
|
|
38
|
+
|
|
39
|
+
data.transform_values do |value|
|
|
40
|
+
Base64.strict_decode64(value)
|
|
41
|
+
rescue ArgumentError
|
|
42
|
+
value
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def encoded_data(data)
|
|
47
|
+
return {} unless data
|
|
48
|
+
|
|
49
|
+
data.transform_values do |value|
|
|
50
|
+
Base64.strict_encode64(value)
|
|
51
|
+
end
|
|
14
52
|
end
|
|
15
53
|
end
|
|
16
54
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ccli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nils Rauch
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-05-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: commander
|
|
@@ -34,45 +34,48 @@ dependencies:
|
|
|
34
34
|
name: tty-command
|
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '0'
|
|
39
|
+
version: '0.10'
|
|
40
40
|
type: :runtime
|
|
41
41
|
prerelease: false
|
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
|
-
- - "
|
|
44
|
+
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '0'
|
|
46
|
+
version: '0.10'
|
|
47
47
|
- !ruby/object:Gem::Dependency
|
|
48
48
|
name: tty-exit
|
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
|
-
- - "
|
|
51
|
+
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '0'
|
|
53
|
+
version: '0.1'
|
|
54
54
|
type: :runtime
|
|
55
55
|
prerelease: false
|
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
|
57
57
|
requirements:
|
|
58
|
-
- - "
|
|
58
|
+
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '0'
|
|
60
|
+
version: '0.1'
|
|
61
61
|
- !ruby/object:Gem::Dependency
|
|
62
62
|
name: tty-logger
|
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
|
-
- - "
|
|
65
|
+
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '0'
|
|
67
|
+
version: '0.6'
|
|
68
68
|
type: :runtime
|
|
69
69
|
prerelease: false
|
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
|
71
71
|
requirements:
|
|
72
|
-
- - "
|
|
72
|
+
- - "~>"
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '0'
|
|
75
|
-
description:
|
|
74
|
+
version: '0.6'
|
|
75
|
+
description: |2
|
|
76
|
+
CCLI is the Cryptopus Command Line Interface. It allows to fetch encryptable data and list teams from Cryptopus.
|
|
77
|
+
One of the main functionality is backing up secrets from cluster services (currently: openshift, kubernetes)
|
|
78
|
+
to Cryptopus and restoring them as well.
|
|
76
79
|
email: rauch@puzzle.ch
|
|
77
80
|
executables:
|
|
78
81
|
- cry
|
|
@@ -80,12 +83,17 @@ extensions: []
|
|
|
80
83
|
extra_rdoc_files: []
|
|
81
84
|
files:
|
|
82
85
|
- ".rubocop.yml"
|
|
86
|
+
- ".tool-versions"
|
|
83
87
|
- ".travis.yml"
|
|
88
|
+
- CHANGELOG.md
|
|
84
89
|
- Gemfile
|
|
85
90
|
- Gemfile.lock
|
|
86
91
|
- README.md
|
|
87
92
|
- bin/cry
|
|
88
93
|
- ccli.gemspec
|
|
94
|
+
- docs/get_login_token.md
|
|
95
|
+
- docs/images/access_user_settings.png
|
|
96
|
+
- docs/images/copy_ccli_login.png
|
|
89
97
|
- lib/adapters/cluster_secret_adapter.rb
|
|
90
98
|
- lib/adapters/cryptopus_adapter.rb
|
|
91
99
|
- lib/adapters/k8s_adapter.rb
|
|
@@ -93,21 +101,23 @@ files:
|
|
|
93
101
|
- lib/adapters/session_adapter.rb
|
|
94
102
|
- lib/cli.rb
|
|
95
103
|
- lib/errors.rb
|
|
96
|
-
- lib/models/
|
|
104
|
+
- lib/models/encryptable.rb
|
|
97
105
|
- lib/models/folder.rb
|
|
98
106
|
- lib/models/k8s_secret.rb
|
|
99
107
|
- lib/models/ose_secret.rb
|
|
100
108
|
- lib/models/team.rb
|
|
101
109
|
- lib/presenters/team_presenter.rb
|
|
102
|
-
- lib/serializers/
|
|
110
|
+
- lib/serializers/encryptable_serializer.rb
|
|
103
111
|
- lib/serializers/folder_serializer.rb
|
|
104
112
|
- lib/serializers/ose_secret_serializer.rb
|
|
105
113
|
- lib/serializers/team_serializer.rb
|
|
106
|
-
homepage:
|
|
114
|
+
homepage: https://github.com/puzzle/ccli
|
|
107
115
|
licenses:
|
|
108
116
|
- MIT
|
|
109
117
|
metadata:
|
|
110
|
-
|
|
118
|
+
bug_tracker_uri: https://github.com/puzzle/ccli/issues
|
|
119
|
+
changelog_uri: https://github.com/puzzle/ccli/blob/master/CHANGELOG.md
|
|
120
|
+
source_code_uri: https://github.com/puzzle/ccli
|
|
111
121
|
post_install_message:
|
|
112
122
|
rdoc_options: []
|
|
113
123
|
require_paths:
|
|
@@ -123,8 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
123
133
|
- !ruby/object:Gem::Version
|
|
124
134
|
version: '0'
|
|
125
135
|
requirements: []
|
|
126
|
-
|
|
127
|
-
rubygems_version: 2.7.9
|
|
136
|
+
rubygems_version: 3.0.8
|
|
128
137
|
signing_key:
|
|
129
138
|
specification_version: 4
|
|
130
139
|
summary: Command line client for the opensource password manager Cryptopus
|
data/lib/models/account.rb
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class Account
|
|
4
|
-
attr_reader :id, :accountname, :username, :password, :type, :ose_secret
|
|
5
|
-
attr_accessor :folder
|
|
6
|
-
|
|
7
|
-
def initialize(accountname: nil, username: nil, password: nil,
|
|
8
|
-
ose_secret: nil, type: nil, id: nil)
|
|
9
|
-
@id = id
|
|
10
|
-
@accountname = accountname
|
|
11
|
-
@username = username
|
|
12
|
-
@password = password
|
|
13
|
-
@ose_secret = ose_secret
|
|
14
|
-
@type = type || 'credentials'
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def to_json(*_args)
|
|
18
|
-
AccountSerializer.to_json(self)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def to_yaml
|
|
22
|
-
AccountSerializer.to_yaml(self)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def to_osesecret
|
|
26
|
-
AccountSerializer.to_osesecret(self)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
class << self
|
|
30
|
-
def find(id)
|
|
31
|
-
AccountSerializer.from_json(CryptopusAdapter.new.get("accounts/#{id}"))
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def find_by_name_and_folder_id(name, id)
|
|
35
|
-
Folder.find(id).accounts.find do |account|
|
|
36
|
-
account.accountname.downcase == name.downcase
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def from_json(json)
|
|
41
|
-
AccountSerializer.from_json(json)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'yaml'
|
|
4
|
-
|
|
5
|
-
class AccountSerializer
|
|
6
|
-
class << self
|
|
7
|
-
# rubocop:disable Metrics/MethodLength
|
|
8
|
-
def to_json(account)
|
|
9
|
-
{
|
|
10
|
-
data: {
|
|
11
|
-
type: 'accounts',
|
|
12
|
-
id: account.id,
|
|
13
|
-
attributes: {
|
|
14
|
-
accountname: account.accountname,
|
|
15
|
-
type: account.type,
|
|
16
|
-
cleartext_username: account.username,
|
|
17
|
-
cleartext_password: account.password,
|
|
18
|
-
ose_secret: account.ose_secret
|
|
19
|
-
},
|
|
20
|
-
relationships: {
|
|
21
|
-
folder: {
|
|
22
|
-
data: {
|
|
23
|
-
id: account.folder,
|
|
24
|
-
type: 'folders'
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}.compact.to_json
|
|
30
|
-
end
|
|
31
|
-
# rubocop:enable Metrics/MethodLength
|
|
32
|
-
|
|
33
|
-
def to_yaml(account)
|
|
34
|
-
{ 'id' => account.id,
|
|
35
|
-
'accountname' => account.accountname,
|
|
36
|
-
'username' => account.username,
|
|
37
|
-
'password' => account.password,
|
|
38
|
-
'type' => account.type }.to_yaml
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def from_json(json)
|
|
42
|
-
json = JSON.parse(json, symbolize_names: true)
|
|
43
|
-
data = json[:data] || json
|
|
44
|
-
attributes = data[:attributes]
|
|
45
|
-
Account.new(accountname: attributes[:accountname],
|
|
46
|
-
username: attributes[:cleartext_username],
|
|
47
|
-
password: attributes[:cleartext_password],
|
|
48
|
-
ose_secret: attributes[:ose_secret],
|
|
49
|
-
type: attributes[:type],
|
|
50
|
-
id: data[:id])
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def to_osesecret(account)
|
|
54
|
-
OSESecret.new(account.accountname, account.ose_secret)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|