sanctum 0.8.2 → 0.8.5.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9da5af097cce5114f4cc439f6ae8ef176aa454365d50a739a3ddccfea18f164f
4
- data.tar.gz: f04ce42579a296395fbcd53618f3e4deb779aed2bdc25d11526e9e67bd75ebe7
3
+ metadata.gz: 7669c1c9dff3c31915cef379c1d95ce55a58e21a8c785f4b543d7a8b55dff42f
4
+ data.tar.gz: 9d87df11f2f28a3ae833271c6ea35329f82d4f08767765bc3383ef5ae66fccba
5
5
  SHA512:
6
- metadata.gz: f8436b62b4667e5924a679c9aa6796f5dda9b49a5bbde35e018f6ac7ce58dcc249f2f0224ef755fad9c869ab6ff9ad9876a7fcd2bc51714c606a9998453112c9
7
- data.tar.gz: cf830c2961a5e0c0624f2df49044f9f333644e1e45e376e9b4b229b950d5c75700d08cc3cfac3c1df88ecf9e3e4ace8db60c4c27c364ed40c1e746c6fdfbecf4
6
+ metadata.gz: 144efff039e4dead3c2d14c7557aef22cde70972b9d2130ef7f94acc8dd1f7a28349382e51d81ca927d40d8e434d8a2cf1d366c52d0394a6a5246296151deef0
7
+ data.tar.gz: 216aedadda62921b7941b52aab144a57a3152dce5f23db1e5febe19d001c4af679c46f53c6c91b2d24b966ca27cb0eeb1a858b01fc56d6582755bcda9ae5fbf8
data/.gitignore CHANGED
@@ -1,11 +1,11 @@
1
+ .rspec_status
1
2
  /.bundle/
2
3
  /.yardoc
3
4
  /_yardoc/
5
+ /cache/
4
6
  /coverage/
5
7
  /doc/
6
8
  /pkg/
7
9
  /spec/reports/
8
10
  /tmp/
9
-
10
- # rspec failure tracking
11
- .rspec_status
11
+ docker-compose.override.yml
@@ -0,0 +1,22 @@
1
+ ---
2
+ image:
3
+ name: docker/compose:1.23.2
4
+ entrypoint: ["/bin/sh", "-c"]
5
+
6
+ variables:
7
+ COMPOSE_FILE: 'docker-compose.test.yml'
8
+
9
+ stages:
10
+ - test
11
+
12
+ before_script:
13
+ - docker version
14
+ - docker info
15
+ - docker-compose version
16
+
17
+ testing:
18
+ stage: test
19
+ script:
20
+ - docker-compose build
21
+ - docker-compose run -T --rm sanctum ls -lart -hu
22
+ - docker-compose run -T --rm sanctum bundle exec rspec
@@ -0,0 +1,39 @@
1
+ ---
2
+ require: rubocop-rspec
3
+
4
+ Rails:
5
+ Enabled: false
6
+
7
+ AllCops:
8
+ DefaultFormatter: progress
9
+ DisplayCopNames: true
10
+ Exclude: []
11
+ CacheRootDirectory: cache/
12
+
13
+ Bundler/OrderedGems:
14
+ Enabled: false
15
+
16
+ RSpec/ExampleLength:
17
+ Max: 25
18
+
19
+ Style/FormatString:
20
+ Enabled: false
21
+
22
+ Style/FormatStringToken:
23
+ Enabled: false
24
+
25
+ Style/StringLiterals:
26
+ EnforcedStyle: double_quotes
27
+ ConsistentQuotesInMultiline: true
28
+
29
+ Style/TrailingCommaInHashLiteral:
30
+ EnforcedStyleForMultiline: consistent_comma
31
+
32
+ Style/TrailingCommaInArrayLiteral:
33
+ EnforcedStyleForMultiline: consistent_comma
34
+
35
+ Style/TrailingUnderscoreVariable:
36
+ Enabled: false
37
+
38
+ Metrics/MethodLength:
39
+ Max: 25
@@ -0,0 +1,31 @@
1
+ FROM ruby:2.5-alpine
2
+
3
+ USER root
4
+
5
+ ENV VERSION 1.0.2
6
+ ADD https://releases.hashicorp.com/vault/${VERSION}/vault_${VERSION}_linux_amd64.zip /tmp/
7
+ ADD https://releases.hashicorp.com/vault/${VERSION}/vault_${VERSION}_SHA256SUMS /tmp/
8
+ ADD https://releases.hashicorp.com/vault/${VERSION}/vault_${VERSION}_SHA256SUMS.sig /tmp/
9
+
10
+ # Install additional dependencies
11
+ # As well as nice to haves locally
12
+ RUN apk --no-cache add git vim busybox-extras curl gnupg build-base \
13
+ && until gpg --keyserver hkp://p80.pool.sks-keyservers.net --recv-key 0x348FFC4C; do echo "Retry"; sleep 30; done \
14
+ && gpg --verify /tmp/vault_${VERSION}_SHA256SUMS.sig \
15
+ && cat /tmp/vault_${VERSION}_SHA256SUMS | grep linux_amd64 | sha256sum /tmp/vault_${VERSION}_linux_amd64.zip \
16
+ && unzip /tmp/vault_${VERSION}_linux_amd64.zip \
17
+ && mv vault /usr/local/bin/ \
18
+ && rm -rf /tmp/*
19
+
20
+ # Setup up app directory
21
+ ENV APP_HOME /usr/src/app/
22
+ WORKDIR $APP_HOME
23
+
24
+ # Add app code
25
+ COPY . $APP_HOME
26
+
27
+ # Install gems
28
+ RUN bundle install --jobs=8
29
+
30
+ # Install sanctum gem
31
+ RUN bundle exec rake install
@@ -1,49 +1,70 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sanctum (0.8.2)
5
- gli (~> 2)
6
- hashdiff (~> 0)
7
- vault (~> 0)
4
+ sanctum (0.8.5.rc1)
5
+ gli (~> 2.18)
6
+ hashdiff (~> 0.3)
7
+ vault (~> 0.12)
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- aws-sigv4 (1.0.2)
12
+ ast (2.4.0)
13
+ aws-sigv4 (1.0.3)
13
14
  coderay (1.1.2)
14
15
  diff-lcs (1.3)
15
- gli (2.17.1)
16
- hashdiff (0.3.7)
17
- method_source (0.9.0)
18
- pry (0.11.3)
16
+ gli (2.18.0)
17
+ hashdiff (0.3.8)
18
+ jaro_winkler (1.5.2)
19
+ method_source (0.9.2)
20
+ parallel (1.13.0)
21
+ parser (2.6.0.0)
22
+ ast (~> 2.4.0)
23
+ powerpack (0.1.2)
24
+ pry (0.12.2)
19
25
  coderay (~> 1.1.0)
20
26
  method_source (~> 0.9.0)
21
- rake (10.5.0)
22
- rspec (3.7.0)
23
- rspec-core (~> 3.7.0)
24
- rspec-expectations (~> 3.7.0)
25
- rspec-mocks (~> 3.7.0)
26
- rspec-core (3.7.1)
27
- rspec-support (~> 3.7.0)
28
- rspec-expectations (3.7.0)
27
+ rainbow (3.0.0)
28
+ rake (12.3.2)
29
+ rspec (3.8.0)
30
+ rspec-core (~> 3.8.0)
31
+ rspec-expectations (~> 3.8.0)
32
+ rspec-mocks (~> 3.8.0)
33
+ rspec-core (3.8.0)
34
+ rspec-support (~> 3.8.0)
35
+ rspec-expectations (3.8.2)
29
36
  diff-lcs (>= 1.2.0, < 2.0)
30
- rspec-support (~> 3.7.0)
31
- rspec-mocks (3.7.0)
37
+ rspec-support (~> 3.8.0)
38
+ rspec-mocks (3.8.0)
32
39
  diff-lcs (>= 1.2.0, < 2.0)
33
- rspec-support (~> 3.7.0)
34
- rspec-support (3.7.1)
35
- vault (0.11.0)
40
+ rspec-support (~> 3.8.0)
41
+ rspec-support (3.8.0)
42
+ rubocop (0.63.1)
43
+ jaro_winkler (~> 1.5.1)
44
+ parallel (~> 1.10)
45
+ parser (>= 2.5, != 2.5.1.1)
46
+ powerpack (~> 0.1)
47
+ rainbow (>= 2.2.2, < 4.0)
48
+ ruby-progressbar (~> 1.7)
49
+ unicode-display_width (~> 1.4.0)
50
+ rubocop-rspec (1.32.0)
51
+ rubocop (>= 0.60.0)
52
+ ruby-progressbar (1.10.0)
53
+ unicode-display_width (1.4.1)
54
+ vault (0.12.0)
36
55
  aws-sigv4
37
56
 
38
57
  PLATFORMS
39
58
  ruby
40
59
 
41
60
  DEPENDENCIES
42
- bundler (~> 1.16)
43
- pry (~> 0)
44
- rake (~> 10.0)
61
+ bundler (~> 1.0)
62
+ pry (~> 0.12.0)
63
+ rake (~> 12.0)
45
64
  rspec (~> 3.0)
65
+ rubocop (~> 0.63.0)
66
+ rubocop-rspec (~> 1.32.0)
46
67
  sanctum!
47
68
 
48
69
  BUNDLED WITH
49
- 1.16.2
70
+ 1.17.2
data/README.md CHANGED
@@ -42,6 +42,7 @@ sanctum config - Generate an example config file.
42
42
  sanctum create - Create an encrypted local file.
43
43
  sanctum edit - Edit an encrypted local file.
44
44
  sanctum view - View an encrypted local file.
45
+ sanctum update - Update secrets backend to v2 api.
45
46
  ```
46
47
 
47
48
 
@@ -80,9 +81,14 @@ The configuration file is a Hash represented in YAML format with three possible
80
81
  * At lease one application/target definition is required.
81
82
 
82
83
  ## Roadmap
83
- * Better/more Tests
84
+ * <strike>Add vault v2 api support</strike>
85
+ * <strike>Add upgrade option for v2 api</strike>
86
+ * If transit key doesn't exist try to create it(automatically)
87
+ * If secrets mount doesn't exist try to create it(automatically)
88
+ * <strike>Better/more Tests</strike>
84
89
  * Built in Backup features
85
90
  * Performance optimizations
91
+ * Reorganize/cleanup code(add adapters, etc)
86
92
 
87
93
  ## Backup scenario.
88
94
  One possible use case for sanctum is for backing up vault secrets. This feature is NOT built in yet.
@@ -111,10 +117,11 @@ This would allow you to be able to quickly decrypt local secrets in a disaster r
111
117
  3. Restore the key to your locally running vault instance.
112
118
 
113
119
  ## Development
114
- Ensure you have the [vault](https://www.vaultproject.io/downloads.html) binary in your path. Tests need this in order to setup a dev vault server(`vault server -dev`)
115
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
120
+ Install [docker](https://docs.docker.com/install/) and [docker-compose](https://docs.docker.com/compose/install/)
121
+ After checking out the repo, run `docker-compose build`. To run tests run `docker-compose run --rm sanctum bundle exec rspec`.
116
122
 
117
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
123
+ To release a new version, `cp docker-compose.override.yml_sample docker-compose.override.yml` and add gem credentials. Update the version number in `version.rb`.
124
+ then run `docker-compose run --rm sanctum bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
118
125
 
119
126
  ## Contributing
120
127
 
@@ -0,0 +1,11 @@
1
+ ---
2
+ version: '3.4'
3
+
4
+ services:
5
+ sanctum:
6
+ environment:
7
+ GEM_HOST_API_KEY:
8
+ GIT_AUTHOR_NAME:
9
+ GIT_AUTHOR_EMAIL:
10
+ GIT_COMMITTER_NAME:
11
+ GIT_COMMITTER_EMAIL:
@@ -0,0 +1,17 @@
1
+ ---
2
+ version: '3.4'
3
+
4
+ services:
5
+ sanctum:
6
+ build:
7
+ context: "."
8
+ command: /bin/true
9
+ depends_on:
10
+ - vault
11
+ vault:
12
+ image: vault:1.0.2
13
+ environment:
14
+ SKIP_SETCAP: "true"
15
+ VAULT_DEV_ROOT_TOKEN_ID: "514c55f0-c452-99e3-55e0-8301b770b92c"
16
+ VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:8200"
17
+ command: ["vault", "server", "-dev"]
@@ -0,0 +1,19 @@
1
+ ---
2
+ version: '3.4'
3
+
4
+ services:
5
+ sanctum:
6
+ build:
7
+ context: "."
8
+ command: /bin/true
9
+ depends_on:
10
+ - vault
11
+ volumes:
12
+ - ".:/usr/src/app"
13
+ vault:
14
+ image: vault:1.0.2
15
+ environment:
16
+ SKIP_SETCAP: "true"
17
+ VAULT_DEV_ROOT_TOKEN_ID: "514c55f0-c452-99e3-55e0-8301b770b92c"
18
+ VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:8200"
19
+ command: ["vault", "server", "-dev"]
@@ -95,6 +95,15 @@ module Sanctum
95
95
  end
96
96
  end
97
97
 
98
+ desc 'Update secrets mount'
99
+ command :update do |c|
100
+ common_options c, :config, :force
101
+ c.flag [:targets, :t], desc: 'Comma seperated list of targets', required: true
102
+ c.action do
103
+ Command::Update.new(@options_hash).run
104
+ end
105
+ end
106
+
98
107
  pre do |_,_,options,_|
99
108
  @options_hash = GetConfig::ConfigMerge.new(config_file: options[:c], targets: options[:t], force: options[:force]).final_options
100
109
  Colorizer.colorize = @options_hash[:sanctum][:color]
@@ -13,4 +13,5 @@ require 'sanctum/command/create'
13
13
  require 'sanctum/command/edit'
14
14
  require 'sanctum/command/pull'
15
15
  require 'sanctum/command/push'
16
+ require 'sanctum/command/update'
16
17
  require 'sanctum/command/view'
@@ -17,7 +17,8 @@ module Sanctum
17
17
  @args = args
18
18
 
19
19
  @transit_key = options.fetch(:vault).fetch(:transit_key)
20
- @targets = options.fetch(:sync)
20
+ # TODO: Fix, to much is happening to targets in this initializer!
21
+ @targets = update_prefix_or_path(set_secrets_version(options.fetch(:sync)))
21
22
  @config_file = options.fetch(:config_file)
22
23
  end
23
24
 
@@ -25,6 +26,60 @@ module Sanctum
25
26
  @vault_client ||= VaultClient.build(options[:vault][:url], options[:vault][:token])
26
27
  end
27
28
 
29
+ private
30
+ # TODO: Fix! This is a bit hacky, will update once vault-ruby gets updated with better support for v2 api
31
+
32
+
33
+ # Internal: gets information about mounts that the user has permissions on
34
+ # Returns: hash
35
+ def mounts_info
36
+ @mounts_info ||= vault_client.request(:get, "/v1/sys/internal/ui/mounts")
37
+ rescue Vault::HTTPClientError
38
+ warn red(
39
+ "Unable to gather info about mounts this maybe due to vault connectivity or permissions"\
40
+ "\nTo list info about mounts you have permissions to have following permissions added"\
41
+ "\npath \"sys/internal/ui/mounts\" { capabilities = [\"read\"] }"\
42
+ )
43
+ end
44
+
45
+
46
+ # Internal: automatically detect the api version of the secrets mount
47
+ # and adds :secrets_version to hash if it doesn't exist
48
+ #
49
+ # Parameter: is an array of hashes: [{}, {}]
50
+ # Returns array of hashes: [{:name=>"vault-test", :prefix=>"vault-test", :path=>"vault/vault-test", :secrets_version=>"2"},{}]
51
+ def set_secrets_version(targets)
52
+ mounts_hash = mounts_info
53
+
54
+ targets.each do |h|
55
+ next if h.key?(:secrets_version)
56
+
57
+ # If mount options is nil default to api version 1 otherwise use version value
58
+ if mounts_hash.dig(:data, :secret, "#{h[:prefix]}/".to_sym, :options).nil?
59
+ h[:secrets_version] = "1"
60
+ else
61
+ h[:secrets_version] = mounts_hash.dig(:data, :secret, "#{h[:prefix]}/".to_sym, :options, :version)
62
+ end
63
+ end
64
+ rescue Vault::VaultError
65
+ warn red(
66
+ "Unable to automatically determine secrets_version. This maybe due to vault connectivity or permissions"\
67
+ "\nTry again, or you could explicitly add `secrets_version:` to your sanctum.yaml to bypass auto detect"
68
+ )
69
+ raise
70
+ end
71
+
72
+ # Internal, add /data to prefix or path if secrets_version == "2"
73
+ # Parameter is an array of hashes: [{}, {}]
74
+ # Returns array of hashes: [{:name=>"vault-test", :prefix=>"vault-test/data", :path=>"vault/vault-test/data", :secrets_version=>"2"},{}]
75
+ def update_prefix_or_path(targets)
76
+ targets.each do |h|
77
+ next unless h[:secrets_version] == "2"
78
+
79
+ h[:prefix] = h[:prefix].include?("/data") ? h[:prefix] : "#{h[:prefix]}/data"
80
+ h[:path] = h[:path].include?("/data") ? h[:path] : "#{h[:path]}/data"
81
+ end
82
+ end
28
83
  end
29
84
  end
30
85
  end
@@ -12,7 +12,7 @@ module Sanctum
12
12
  local_secrets = VaultTransit.decrypt(vault_client, local_secrets, transit_key)
13
13
 
14
14
  # Recursively get vault secrets for each prefix specified in sanctum.yaml
15
- secrets_list = VaultSecrets.new(vault_client, target[:prefix]).get
15
+ secrets_list = VaultSecrets.new(vault_client, target[:prefix], target[:secrets_version]).get_all
16
16
 
17
17
  # Only one entry in this hash (which will be the target).
18
18
  tree = secrets_list.values.first
@@ -14,7 +14,7 @@ module Sanctum
14
14
  end
15
15
 
16
16
  # Recursively get vault secrets for each prefix specified in sanctum.yaml
17
- secrets_list = VaultSecrets.new(vault_client, target[:prefix]).get
17
+ secrets_list = VaultSecrets.new(vault_client, target[:prefix], target[:secrets_version]).get_all
18
18
  secrets_list.each do |k,v|
19
19
 
20
20
  vault_secrets = build_vault_secrets(v, [target[:path]])
@@ -14,14 +14,14 @@ module Sanctum
14
14
  force = target.fetch(:force) {options[:sanctum][:force]}
15
15
  end
16
16
 
17
- # Build array of local paths by recursively searching for local files for each prefix specified in sanctum.yaml
17
+ # Build array of local paths by recursively searching for local files for each path specified in sanctum.yaml
18
18
  local_paths = get_local_paths(File.join(File.dirname(config_file), target[:path]))
19
19
 
20
20
  local_secrets = build_local_secrets(local_paths)
21
- vault_secrets = build_vault_secrets(local_paths, target[:prefix], target[:path])
21
+ vault_secrets = build_vault_secrets(local_paths, target[:prefix], target[:path], target[:secrets_version])
22
22
 
23
23
  # Compare secrets
24
- # vault_secrets paths have been mapped to local_paths to make comparison easier
24
+ # vault_secrets prefix have been mapped to local_paths to make comparison easier
25
25
  differences = compare_secrets(vault_secrets, local_secrets, target[:name], "push")
26
26
  next if differences.nil?
27
27
 
@@ -36,21 +36,24 @@ module Sanctum
36
36
 
37
37
  if force
38
38
  warn red("#{target[:name]}: Forcefully writing differences to vault(push)")
39
- VaultTransit.write_to_vault(vault_client, vault_secrets)
39
+ VaultTransit.write_to_vault(vault_client, vault_secrets, target[:secrets_version])
40
40
  else
41
41
  #Confirm with user, and write to local file if approved
42
42
  next unless confirmed_with_user?
43
- VaultTransit.write_to_vault(vault_client, vault_secrets)
43
+ VaultTransit.write_to_vault(vault_client, vault_secrets, target[:secrets_version])
44
44
  end
45
45
  end
46
46
  end
47
47
 
48
- def read_remote(paths, prefix)
48
+ # Right now this duplicates a bit of logic that already exists in
49
+ # VaultSecrets client.
50
+ # TODO: remove this method once code is rearranged
51
+ def read_remote(paths, prefix, secrets_version)
49
52
  tmp_hash = Hash.new
50
53
  paths.each do |k,v|
51
54
  p = File.join(prefix, k)
52
55
  unless vault_client.logical.read(p).nil?
53
- v = vault_client.logical.read(p).data
56
+ v = secrets_version == "2" ? vault_client.logical.read(p).data[:data] : vault_client.logical.read(p).data
54
57
  tmp_hash["#{k}"] = v
55
58
  else
56
59
  next
@@ -77,7 +80,7 @@ module Sanctum
77
80
  local_secrets = VaultTransit.decrypt(vault_client, local_secrets, transit_key)
78
81
  end
79
82
 
80
- def build_vault_secrets(local_paths, target_prefix, target_path)
83
+ def build_vault_secrets(local_paths, target_prefix, target_path, secrets_version)
81
84
  # Map local_paths into vault_paths
82
85
  vault_paths = local_paths.map{|x| x.gsub(File.join(File.dirname(config_file), target_path), "")}
83
86
 
@@ -86,9 +89,9 @@ module Sanctum
86
89
  # We will not for example, see differences if a secret exists in vault but not locally.
87
90
 
88
91
  # Read secrets from vault
89
- vault_secrets = read_remote(vault_paths, target_prefix)
92
+ vault_secrets = read_remote(vault_paths, target_prefix, secrets_version)
90
93
 
91
- # To make comparing a bit easier map vault_secrets paths back local_paths
94
+ # To make comparing a bit easier map vault_secrets prefixs back local_paths
92
95
  # Convert to json, then read, to make keys strings vs symbols
93
96
  vault_secrets = JSON(map_local_path(vault_secrets, target_path).to_json)
94
97
  end
@@ -18,15 +18,18 @@ vault:
18
18
 
19
19
  sync:
20
20
  # sync is an array of hashes of sync target configurations
21
- # at least one app definition is REQUIRED Fields:
21
+ # at least one app definition is REQUIRED
22
+ # Fields:
22
23
  # name - (required) Friendly name of the sync target.
23
24
  # prefix - (required) The vault prefix(secret mount) to synchronize to.
24
- # path - (required) The relative filesystem path to the directory
25
- # containing the files with content to synchronize to Vault.
26
- # This path is calculated relative to the directory containing
27
- # the configuration file.
25
+ # path - (required) The relative filesystem path that gets synchronized
26
+ # with Vault. This path is calculated relative to the directory containing
27
+ # the sanctum configuration file.
28
28
  # force - Whether or not to force push, pull actions (no user input)
29
29
  # This inherits the setting from the `sanctum` section.
30
+ # secrets_version - The k/v secrets version `1`, or `2`. Sanctum will try to detect this automatically
31
+ # if not valued. This does not apply to `generic` secrets backend.
32
+ # see https://www.vaultproject.io/docs/secrets/kv/index.html
30
33
  #- name: app-foo
31
34
  #prefix: secrets/app-foo
32
35
  #path: vault/app-foo
@@ -0,0 +1,96 @@
1
+ require "pathname"
2
+
3
+ module Sanctum
4
+ module Command
5
+ class Update < Base
6
+ def run
7
+ targets.each do |target|
8
+ # Use command line if force: true
9
+ if options[:cli][:force]
10
+ force = options[:cli][:force]
11
+ else
12
+ force = target.fetch(:force) {options[:sanctum][:force]}
13
+ end
14
+
15
+ update_mount(target, force)
16
+
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def update_mount(target, force)
23
+ data = { options: { version: "2" }, listing_visability: "unauth" }.to_json
24
+ pre_upgrade_warning
25
+
26
+ if force
27
+ warn yellow("\nUpgrading #{target[:prefix]}")
28
+ upgrade_response = vault_client.request(:post, "/v1/sys/mounts/#{target[:prefix]}/tune", data)
29
+ upgrade_response.nil? ? nothing_happened_warning : (warn yellow("#{upgrade_response}"))
30
+ else
31
+ already_upgraded_warning if target[:secrets_version] == "2"
32
+ warn yellow("#{vault_client.request(:post, "/v1/sys/mounts/#{target[:prefix]}/tune", data)}") if confirm_upgrade?(target)
33
+ end
34
+ post_upgrade_warning(target)
35
+ end
36
+
37
+ def pre_upgrade_warning
38
+ warn yellow(
39
+ "\nPlease read 'Upgrading from Version 1' documentation BEFORE you upgrade"\
40
+ "\nThe addition of `/data`, and `/metadata` endpoints will break applications that are depending on v1 endpoints"\
41
+ "\nYou will want to update permissions policies, and applications BEFORE you upgrade"\
42
+ "\nhttps://www.vaultproject.io/docs/secrets/kv/kv-v2.html#upgrading-from-version-1"\
43
+ )
44
+ additional_acl_warning
45
+ end
46
+
47
+ def post_upgrade_warning(target)
48
+ warn yellow(
49
+ "\nOnce the upgrade has been completed, make sure you update your sanctum.yaml."\
50
+ "\nPlease add the `secrets_version: 2` key to the #{target[:prefix]} config."
51
+ )
52
+ end
53
+
54
+ def additional_acl_warning
55
+ warn yellow(
56
+ "\nIf you use policies to limit secrets access you may need to have your permissions updated"\
57
+ "\nSee https://www.vaultproject.io/docs/secrets/kv/kv-v2.html#acl-rules for more info"\
58
+ "\nSpecifically you may need add something similar to the following:"\
59
+ "\npath \"<secrets_mount>/data/*\" { capabilities = [\"list\",\"read\",\"create\",\"update\",\"delete\"] }"\
60
+ "\npath \"<secrets_mount>/metadata/*\" { capabilities = [\"list\",\"read\",\"create\",\"update\",\"delete\"] }"\
61
+ "\npath \"<secrets_mount>/destroy/*\" { capabilities = [\"update\"] }"\
62
+ "\npath \"<secrets_mount>/delete/*\" { capabilities = [\"update\"] }"\
63
+ "\npath \"<secrets_mount>/undelete/*\" { capabilities = [\"update\"] }"
64
+ )
65
+ end
66
+
67
+ def already_upgraded_warning
68
+ raise red(
69
+ "Mount appears to have already been updated. This could be due to `secrets_version: 2` specified"\
70
+ "\nin sanctum.yaml, or the mount having already been upgraded."\
71
+ "\nTo try anyway you can pass `--force` on the command line"
72
+ )
73
+ end
74
+
75
+ def nothing_happened_warning
76
+ warn yellow(
77
+ "Request was successfull but returned a nil response, this generally means the mount has is already upgraded!"
78
+ )
79
+ end
80
+
81
+ def confirm_upgrade?(target)
82
+ warn yellow("\nUpgrading will make the mount temporarily unavailable")
83
+ warn yellow("Would you like to continue?: ")
84
+ question = STDIN.gets.chomp.upcase
85
+
86
+ if ["Y", "YES"].include? question
87
+ warn yellow("\nUpgrading #{target[:prefix]}")
88
+ true
89
+ else
90
+ warn yellow("\nSkipping....\n")
91
+ false
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -6,15 +6,15 @@ module Sanctum
6
6
  class DefaultOptions
7
7
  attr_reader :config_file
8
8
 
9
- def initialize(config_file=nil)
9
+ def initialize(config_file = nil)
10
10
  @config_file = config_file
11
11
  end
12
12
 
13
13
  def run
14
14
  {
15
15
  config_file: config_file.nil? ? config_file_search : config_file,
16
- sanctum: { force: false, color: true },
17
- vault: { url: "https://127.0.0.1:8200", token: get_vault_token }
16
+ sanctum: { color: true, force: false },
17
+ vault: { token: get_vault_token, url: "https://127.0.0.1:8200" },
18
18
  }
19
19
  end
20
20
 
@@ -1,26 +1,39 @@
1
1
  module Sanctum
2
2
  class VaultSecrets
3
3
  include Colorizer
4
- attr_reader :vault_client, :prefix
4
+ attr_reader :vault_client, :prefix, :secrets_version
5
5
 
6
- def initialize(vault_client, prefix)
6
+ def initialize(vault_client, prefix, secrets_version="1")
7
7
  @vault_client = vault_client
8
8
  @prefix = prefix
9
+ @secrets_version = secrets_version
9
10
  end
10
11
 
11
- def get
12
- if invalid_prefix?
13
- raise yellow("Warning: Vault prefix: '#{prefix}' does not exist.. ")
14
- end
12
+ # API version 2 uses /metadata path to list, but /data to read.
13
+ #TODO Fix, change list_prefix back to prefix at some point. Use new kv from vault-ruby once it's updated
14
+ def get_all
15
+ raise yellow("Warning: Vault prefix: '#{prefix}' does not exist.. ") if invalid_prefix?
15
16
 
16
17
  secrets_from_vault = Hash.new
17
- secrets_from_vault[prefix] = JSON(list_recursive(prefix).to_json)
18
+ secrets_from_vault[prefix] = JSON(list_recursive(list_prefix).to_json)
18
19
  secrets_from_vault
19
20
  end
20
21
 
21
22
  private
22
- def list_recursive(prefix, parent = '')
23
- me = File.join(parent, prefix)
23
+
24
+ # API version 2 uses /metadata path to list, but /data to read.
25
+ # TODO remove method and use kv from vault-ruby once available.
26
+ def list_prefix
27
+ if secrets_version == "2"
28
+ prefix.include?("/data") ? prefix.sub(/data/, "metadata") : "#{prefix}/metadata"
29
+ else
30
+ prefix
31
+ end
32
+ end
33
+
34
+ # TODO Fix, change list_prefix back to prefix at some point. Use new kv from vault-ruby once it's updated
35
+ def list_recursive(list_prefix, parent = '')
36
+ me = File.join(parent, list_prefix)
24
37
  result = vault_client.logical.list(me).inject({}) do |hash, item|
25
38
  case item
26
39
  when /.*\/$/
@@ -33,14 +46,25 @@ module Sanctum
33
46
  result
34
47
  end
35
48
 
49
+ # Used by list_recursive method only
50
+ # API version 2 uses /metadata path to list, but /data to read.
51
+ # TODO Update to use kv from vault-ruby once available.
36
52
  def read_data(item, parent = '')
37
53
  me = File.join(parent, item)
38
- vault_client.logical.read(me).data
54
+
55
+ # me will contain /metadata if secrets_version 2 due to list_prefix method
56
+ if secrets_version == "2"
57
+ me = me.sub(/metadata/, "data")
58
+ vault_client.logical.read(me).data[:data]
59
+ else
60
+ vault_client.logical.read(me).data
61
+ end
39
62
  end
40
63
 
64
+ # API version 2 uses /metadata path to list, but /data to read.
65
+ # TODO Fix, change list_prefix back to prefix at some point. Use new kv from vault-ruby once it's updated
41
66
  def invalid_prefix?
42
- vault_client.logical.list(prefix).empty?
67
+ vault_client.logical.list(list_prefix).empty?
43
68
  end
44
-
45
69
  end
46
70
  end
@@ -34,13 +34,14 @@ module Sanctum
34
34
  def self.write_to_file(vault_client, secrets, transit_key)
35
35
  secrets = encrypt(vault_client, secrets, transit_key)
36
36
  secrets.each do |k, v|
37
+ create_path(k)
37
38
  File.write(k, v.data[:ciphertext])
38
39
  end
39
40
  end
40
41
 
41
- def self.write_to_vault(vault_client, secrets)
42
+ def self.write_to_vault(vault_client, secrets, secrets_version="1")
42
43
  secrets.each do |k, v|
43
- vault_client.logical.write(k, v)
44
+ secrets_version == "2" ? vault_client.logical.write(k, data: v) : vault_client.logical.write(k, v)
44
45
  end
45
46
  end
46
47
 
@@ -56,5 +57,10 @@ module Sanctum
56
57
  !vault_client.logical.read(transit_key.to_path).nil?
57
58
  end
58
59
 
60
+ def self.create_path(path)
61
+ path = Pathname.new(path).parent.to_path
62
+ FileUtils.mkdir_p(path) unless File.directory?(path)
63
+ end
64
+
59
65
  end
60
66
  end
@@ -1,3 +1,3 @@
1
1
  module Sanctum
2
- VERSION = "0.8.2"
2
+ VERSION = "0.8.5.rc1"
3
3
  end
@@ -23,12 +23,14 @@ Gem::Specification.new do |spec|
23
23
  spec.executables = "sanctum"
24
24
  spec.require_paths = ["lib"]
25
25
 
26
- spec.add_dependency 'vault', '~> 0'
27
- spec.add_dependency 'hashdiff', '~> 0'
28
- spec.add_dependency 'gli', '~> 2'
26
+ spec.add_dependency 'vault', '~> 0.12'
27
+ spec.add_dependency 'hashdiff', '~> 0.3'
28
+ spec.add_dependency 'gli', '~> 2.18'
29
29
 
30
- spec.add_development_dependency 'bundler', '~> 1.16'
31
- spec.add_development_dependency 'pry', '~> 0'
32
- spec.add_development_dependency 'rake', '~> 10.0'
30
+ spec.add_development_dependency 'bundler', '~> 1.0'
31
+ spec.add_development_dependency 'pry', '~> 0.12.0'
32
+ spec.add_development_dependency 'rake', '~> 12.0'
33
33
  spec.add_development_dependency 'rspec', '~> 3.0'
34
+ spec.add_development_dependency 'rubocop', '~> 0.63.0'
35
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.32.0'
34
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sanctum
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.8.5.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Corban Raun
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-25 00:00:00.000000000 Z
11
+ date: 2019-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: vault
@@ -16,84 +16,84 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '0.12'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '0.12'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: hashdiff
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '0.3'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '0.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: gli
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '2'
47
+ version: '2.18'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '2'
54
+ version: '2.18'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1.16'
61
+ version: '1.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '1.16'
68
+ version: '1.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: pry
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: 0.12.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: 0.12.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rake
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '10.0'
89
+ version: '12.0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '10.0'
96
+ version: '12.0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rspec
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +108,34 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.63.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.63.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 1.32.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 1.32.0
111
139
  description: Syncs encrypted content from the filesystem to the Vault secrets store.
112
140
  email:
113
141
  - corban@raunco.co
@@ -117,7 +145,10 @@ extensions: []
117
145
  extra_rdoc_files: []
118
146
  files:
119
147
  - ".gitignore"
148
+ - ".gitlab-ci.yml"
120
149
  - ".rspec"
150
+ - ".rubocop.yml"
151
+ - Dockerfile
121
152
  - Gemfile
122
153
  - Gemfile.lock
123
154
  - LICENSE.txt
@@ -126,6 +157,9 @@ files:
126
157
  - bin/console
127
158
  - bin/sanctum
128
159
  - bin/setup
160
+ - docker-compose.override.yml_sample
161
+ - docker-compose.test.yml
162
+ - docker-compose.yml
129
163
  - lib/sanctum.rb
130
164
  - lib/sanctum/cli.rb
131
165
  - lib/sanctum/colorize_string.rb
@@ -141,6 +175,7 @@ files:
141
175
  - lib/sanctum/command/pull.rb
142
176
  - lib/sanctum/command/push.rb
143
177
  - lib/sanctum/command/sanctum.example.yaml
178
+ - lib/sanctum/command/update.rb
144
179
  - lib/sanctum/command/view.rb
145
180
  - lib/sanctum/get_config.rb
146
181
  - lib/sanctum/get_config/config_file.rb
@@ -169,9 +204,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
169
204
  version: 2.5.0
170
205
  required_rubygems_version: !ruby/object:Gem::Requirement
171
206
  requirements:
172
- - - ">="
207
+ - - ">"
173
208
  - !ruby/object:Gem::Version
174
- version: '0'
209
+ version: 1.3.1
175
210
  requirements: []
176
211
  rubyforge_project:
177
212
  rubygems_version: 2.7.6