sanctum 0.8.2 → 0.8.5.rc1

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 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