aws-s3crets 0.0.2

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZTQ2MjViNjUxY2Q4MmVlZmE4MGYzNDEzOWYyODczNmNkYWRiMDgyYQ==
5
+ data.tar.gz: !binary |-
6
+ OGRkMzRkOWNkOTAyZWQzNDcyMjYyNTZhOGZjNTVjYjQ2ODJhN2I2Zg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ OWFjNWMwNWQwMWIwMWExOTM5YmVhY2M0M2NmNmJlNWVhNzNlODlhOGMxNDIw
10
+ YjdjZTJjZjMxZWFkODczYTk1NGFjMWUwZTgwNGJmZTlhNDkzN2U3MjdkNWYx
11
+ NDgzODNhZDMxMWY1OWM4YTBkYjg5ZmIzMDRjMDIwOThiZTQ1NzM=
12
+ data.tar.gz: !binary |-
13
+ OTZhZWM0MzEyZGE1ZTdhNDk1NGVhMTNiYWM5ODBkMzRiZDVlZGVhZmJhNWZm
14
+ MTdkYjg0MmRiZjE2MDFjZWNkNTllY2NmMmIxOGVjZDRhNTA3NGU3N2I3MTIw
15
+ ODI4YWUwNjNjOWQwMDA1MDgyNDI0YTgwYmFmZDczNWU1NmY4NGM=
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,11 @@
1
+ # Configuration parameters: AllowURI, URISchemes.
2
+ Metrics/LineLength:
3
+ Max: 110
4
+
5
+ # Offense count: 2
6
+ Style/Documentation:
7
+ Enabled: false
8
+
9
+ # Offense count: 2
10
+ Style/RegexpLiteral:
11
+ MaxSlashes: 0
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - 2.0.0
5
+ deploy:
6
+ provider: rubygems
7
+ gem: aws-s3crets
8
+ on:
9
+ tags: true
10
+ all_branches: true
11
+ api_key:
12
+ secure: crwL3RJgh7X9hJK3+3gJQ3ucn9uJjJNytEGqgmnVHvAw2WNcg3oHLnro/529wfMc961ZtwMSQ75Zc4jAeDLlxR6AdMTrUJ7SOmvaIkrvmJCUaadu3C8JXY/EoIx/8tJ4V+4u5hkDvcZUoZ9g9kI68r1NtBopJMaSCgrBbYCoCZA=
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in s3crets.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Norm MacLennan
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # S3crets
2
+
3
+ [![Build Status](https://travis-ci.org/maclennann/s3crets.svg?branch=master)](https://travis-ci.org/maclennann/s3crets)
4
+
5
+ `s3crets` is a gem that allows you to fetch secret files (password, certs, keys,
6
+ etc) from an S3 bucket via the command-line, rake, or ruby script.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'aws-s3crets'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install s3crets
23
+
24
+ ## Usage
25
+
26
+ ### Setup
27
+
28
+ The most-common use-case for `s3crets` involves the use of a `Secretfile`.
29
+
30
+ This is a yaml file that contains S3 location information (region/bucket)
31
+ as well as the key/path to all of your required secrets.
32
+
33
+ You can generate a sample `Secretfile` by running `s3crets init`. Now you can
34
+ fill in your secrets. A completed `Secretfile` looks something like:
35
+
36
+ ```yaml
37
+ ---
38
+ settings:
39
+ bucket: 'secrets_bucket'
40
+ region: 'us-west-2'
41
+ secret_dir: secrets/dev
42
+ secrets:
43
+ aws_key: 'AWS/Keys/ec2-myteam-write'
44
+ ssh_key: 'SSH/myteam/myserver/server-priv'
45
+ cloud_config: 'AWS/cloudinit/myserver.yaml'
46
+ ```
47
+
48
+ This `Secretfile` describes 3 secrets stored in the `secrets_bucket` bucket.
49
+ In this example, the files are 3 secrets required to provision a new EC2 instance -
50
+ an AWS credential file, an SSH private key, and a cloud-init config.
51
+
52
+ It will download these secrets to `secrets/dev/[filename]`.
53
+
54
+ ### Fetching Secrets
55
+
56
+ Once you have your `Secretfile` ready, there are two ways you can actually fetch
57
+ the secrets. Both ways, assume you have you [AWS credentials set up](http://docs.aws.amazon.com/sdkforruby/api/#Credentials).
58
+
59
+ #### Command Line
60
+
61
+ Just type `s3crets bundle` to download all of the secrets. Secrets that already exist
62
+ in the target directory will not be re-downloaded.
63
+
64
+ #### Rake
65
+
66
+ `s3crets` comes with default rake tasks. Simply `require 's3crets/default_tasks'`
67
+ somewhere in your Rakefile and it will construct tasks based on your folder
68
+ structure and the location of your `Secretfile`(s).
69
+
70
+ For example, the following directory hierarchy:
71
+
72
+ ```
73
+ Rakefile
74
+ secrets/
75
+ production/
76
+ Secretfile
77
+ development/
78
+ Secretfile
79
+ ```
80
+
81
+ Will create the following rake tasks:
82
+
83
+ ```
84
+ rake secrets:development # Fetch secrets for development
85
+ rake secrets:production # Fetch secrets for production
86
+ ```
87
+
88
+ The following configuration can be applied to the default tasks:
89
+
90
+ * `ENV['S3CRETS_ENVIRONMENT_GLOB']` - The directory glob that is used to identify
91
+ your environments (default: `secrets/**/Secretfile`)
92
+
93
+ ### `Secretfile.resolved`
94
+
95
+ Once you have fetched your secrets, a `Secretsfile.resolved` will be created in
96
+ the directory. This file contains the name and hash of the files that were
97
+ downloaded.
98
+
99
+ If you have a file locally that doesn't match the hash in your `resolved` file,
100
+ it will be redownloaded the next time you fetch secrets. Then the resolved file
101
+ will be updated.
102
+
103
+ ## Contributing
104
+
105
+ 1. Fork it ( https://github.com/maclennann/s3crets/fork )
106
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
107
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
108
+ 4. Push to the branch (`git push origin my-new-feature`)
109
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rubocop/rake_task'
3
+ require 'rspec/core/rake_task'
4
+
5
+ task default: [:rubocop, :spec]
6
+
7
+ # Ensure default rake tasks load
8
+ require 's3crets/default_tasks'
9
+
10
+ RSpec::Core::RakeTask.new(:spec)
11
+ RuboCop::RakeTask.new(:rubocop)
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 's3crets/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'aws-s3crets'
8
+ spec.version = S3crets::VERSION
9
+ spec.authors = ['Norm MacLennan']
10
+ spec.email = ['norm.maclennan@gmail.com']
11
+ spec.summary = 'Fetch secret files from AWS S3 buckets.'
12
+ spec.description = 'Fetch secret files from AWS S3 buckets.'
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rubocop', '~> 0.29'
24
+ spec.add_development_dependency 'rspec', '~> 3.2'
25
+ spec.add_development_dependency 'fakefs', '~> 0.6'
26
+ spec.add_development_dependency 'gem-path', '~> 0.6'
27
+
28
+ spec.add_dependency 'aws-sdk', '~> 2.0'
29
+ spec.add_dependency 'thor'
30
+ end
data/bin/s3crets ADDED
@@ -0,0 +1,4 @@
1
+ #! ruby
2
+ require 's3crets/cli'
3
+
4
+ S3crets::Cli.start(ARGV)
data/lib/s3crets.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 's3crets/version'
2
+
3
+ module S3crets
4
+ end
@@ -0,0 +1,3 @@
1
+ require 's3crets/actions/init'
2
+ require 's3crets/actions/bundle'
3
+ require 's3crets/actions/get'
@@ -0,0 +1,27 @@
1
+ module S3crets
2
+ module Actions
3
+ # Basic plumbing for all actions
4
+ class Base
5
+ attr_accessor :options
6
+ private :options=
7
+
8
+ attr_accessor :logger
9
+ private :logger=
10
+
11
+ def initialize(logger, options = {})
12
+ self.logger = logger
13
+ self.options = options
14
+ end
15
+
16
+ private
17
+
18
+ def debug(*args, &block)
19
+ logger.debug(*args, &block)
20
+ end
21
+
22
+ def source_root
23
+ @source_root ||= Pathname.new(File.expand_path('../../../../', __FILE__))
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,120 @@
1
+ require 's3crets/actions/base'
2
+ require 's3crets/error'
3
+ require 'aws-sdk'
4
+ require 'yaml'
5
+
6
+ module S3crets
7
+ module Actions
8
+ # Download or verify the secrets in a Secretfile
9
+ class Bundle < Base
10
+ attr_accessor :settings
11
+ attr_accessor :remote_secrets
12
+ attr_accessor :local_secrets
13
+ attr_accessor :update
14
+
15
+ def initialize(ui, options, args = {})
16
+ super(ui, options)
17
+ @update = args[:update]
18
+ end
19
+
20
+ def run
21
+ ensure_secretfile
22
+ load_required_secrets
23
+ ensure_secrets_path
24
+ validate_environment
25
+ load_resolved_secrets if resolved_file?
26
+ validate_local_secrets unless @update
27
+ run!
28
+ end
29
+
30
+ private
31
+
32
+ def run!
33
+ if need_secrets?
34
+ new_secrets = download_remote_secrets
35
+ update_resolved_file new_secrets
36
+ else
37
+ debug { 'No secrets to download...' }
38
+ end
39
+ end
40
+
41
+ def ensure_secretfile
42
+ fail Error, NO_SECRETFILE_ERROR unless File.exist? SECRETFILE_NAME
43
+ debug { "#{SECRETFILE_NAME} located..." }
44
+ end
45
+
46
+ def ensure_secrets_path
47
+ FileUtils.mkdir_p(@settings['secret_dir']) unless @settings['secret_dir'].nil?
48
+ end
49
+
50
+ def resolved_file?
51
+ File.exist? RESOLVED_NAME
52
+ end
53
+
54
+ def load_required_secrets
55
+ secretfile = YAML.load_file(SECRETFILE_NAME)
56
+ @settings = secretfile['settings'] || {}
57
+ @remote_secrets = secretfile['secrets'] || {}
58
+ @local_secrets = {}
59
+ end
60
+
61
+ def validate_environment
62
+ return unless @settings.empty? || @remote_secrets.empty?
63
+ fail Error, INVALID_SECRETFILE_ERROR
64
+ end
65
+
66
+ def load_resolved_secrets
67
+ @local_secrets = YAML.load_file RESOLVED_NAME
68
+ end
69
+
70
+ def need_secrets?
71
+ !@remote_secrets.empty?
72
+ end
73
+
74
+ def validate_local_secrets
75
+ @local_secrets.each do |key, secret|
76
+ if File.exist?(secret[:path]) && secret[:hash] == Digest::MD5.file(secret[:path]).hexdigest
77
+ debug { "#{key} found locally, skipping download..." }
78
+ @remote_secrets.delete key
79
+ end
80
+ end
81
+ end
82
+
83
+ def download_remote_secrets
84
+ downloaded_secrets = {}
85
+ remote_secrets.each do |key, secret|
86
+ downloaded_secrets[key] = download_one_secret(secret,
87
+ @settings['secret_dir'] || '.')
88
+
89
+ debug { "Downloaded secret: #{key} to #{@settings['secret_dir']}..." }
90
+ end
91
+
92
+ downloaded_secrets
93
+ end
94
+
95
+ def download_one_secret(remote_path, local_path)
96
+ path = File.join(local_path, File.basename(remote_path))
97
+
98
+ resp = s3.get_object(bucket: @settings['bucket'],
99
+ key: remote_path)
100
+
101
+ File.write(path, resp.body.read)
102
+ { path: path, hash: Digest::MD5.file(path).hexdigest }
103
+ end
104
+
105
+ def update_resolved_file(new_secrets)
106
+ @local_secrets.merge! new_secrets
107
+
108
+ File.open(RESOLVED_NAME, 'w') do |out|
109
+ YAML.dump(@local_secrets, out)
110
+ end
111
+
112
+ debug { 'Updated resolved file...' }
113
+ end
114
+
115
+ def s3
116
+ @s3 ||= ::Aws::S3::Client.new(region: @settings['region'])
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,32 @@
1
+ require 's3crets/actions/base'
2
+ require 's3crets/error'
3
+ require 'aws-sdk'
4
+ require 's3crets/defaults'
5
+
6
+ module S3crets
7
+ module Actions
8
+ # Fetch a single secret
9
+ class Get < Base
10
+ attr_accessor :path
11
+
12
+ def initialize(ui, options, path)
13
+ super(ui, options)
14
+ @path = path
15
+ end
16
+
17
+ def run
18
+ fetch_secret(@path)
19
+ end
20
+
21
+ def fetch_secret(path)
22
+ filename = File.basename(path)
23
+
24
+ s3 = ::Aws::S3::Client.new(region: options['region'])
25
+ resp = s3.get_object(bucket: options['bucket'],
26
+ key: path)
27
+
28
+ File.write(filename, resp.body.read)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ require 's3crets/actions/base'
2
+ require 's3crets/error'
3
+ require 'ostruct'
4
+ require 'erb'
5
+ require 's3crets/defaults'
6
+
7
+ module S3crets
8
+ module Actions
9
+ # Create a new Zanzifile
10
+ class Init < Base
11
+ def run
12
+ check_for_secretfile
13
+ write_template
14
+ end
15
+
16
+ private
17
+
18
+ def check_for_secretfile
19
+ return unless File.exist?(SECRETFILE_NAME) && !options['force']
20
+ fail Error, ALREADY_EXISTS_ERROR
21
+ end
22
+
23
+ def write_template
24
+ template = TemplateRenderer.new(options)
25
+
26
+ File.open(SECRETFILE_NAME, 'w') do |f|
27
+ f.write template.render(File.read(source_root.join(TEMPLATE_NAME)))
28
+ end
29
+ end
30
+
31
+ # Allows us to easily feed our options hash
32
+ # to an ERB
33
+ class TemplateRenderer < OpenStruct
34
+ def render(template)
35
+ ERB.new(template).result(binding)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,118 @@
1
+ require 'thor'
2
+ require 'thor/actions'
3
+ require 's3crets/version'
4
+ require 's3crets/ui'
5
+ require 's3crets/actions'
6
+ require 's3crets/error'
7
+ require 's3crets/defaults'
8
+
9
+ module S3crets
10
+ # The `s3crets` binay/thor application main class
11
+ class Cli < Thor
12
+ include Thor::Actions
13
+
14
+ attr_accessor :ui
15
+
16
+ def initialize(*)
17
+ super
18
+ the_shell = (options['no-color'] ? Thor::Shell::Basic.new : shell)
19
+ @ui = Shell.new(the_shell)
20
+ @ui.be_quiet! if options['quiet']
21
+ @ui.debug! if options['verbose']
22
+
23
+ debug_header
24
+ end
25
+
26
+ desc 'version', 'Display your Zanzibar verion'
27
+ def version
28
+ say "#{APPLICATION_NAME} Version: #{VERSION}"
29
+ end
30
+
31
+ desc 'init', "Create an empty #{SECRETFILE_NAME} in the current directory."
32
+ option 'verbose', type: :boolean, default: false, aliases: :v
33
+ option 'force', type: :boolean, default: false
34
+ option 'bucket', type: :string, aliases: :b, default: 'my_bucket',
35
+ desc: 'The S3 bucket to connect to.'
36
+ option 'region', type: :string, aliases: :r, default: 'us-west-2',
37
+ desc: 'The AWS region to use.'
38
+ option 'credential_file', type: :string, aliases: :f,
39
+ default: '~/.aws/credential',
40
+ desc: 'The AWS credential file'
41
+ def init
42
+ run_action { init! }
43
+ end
44
+
45
+ desc 'bundle', "Fetch secrets declared in your #{SECRETFILE_NAME}"
46
+ option 'verbose', type: :boolean, default: false, aliases: :v
47
+ def bundle
48
+ run_action { bundle! }
49
+ end
50
+
51
+ desc 'plunder', "Alias to `#{APPLICATION_NAME} bundle`", hide: true
52
+ option 'verbose', type: :boolean, default: false, aliases: :v
53
+ alias_method :plunder, :bundle
54
+
55
+ desc 'install', "Alias to `#{APPLICATION_NAME} bundle`"
56
+ alias_method :install, :bundle
57
+
58
+ desc 'update', "Redownload all secrets in your #{SECRETFILE_NAME}"
59
+ option 'verbose', type: :boolean, default: false, aliases: :v
60
+ def update
61
+ run_action { update! }
62
+ end
63
+
64
+ desc 'get KEY', 'Fetch a single KEY from S3'
65
+ option 'bucket', type: :string, aliases: :b,
66
+ desc: 'The S3 bucket to connect to.'
67
+ option 'region', type: :string, aliases: :r,
68
+ desc: 'The AWS region to use.'
69
+ option 'credential_file', type: :string, aliases: :f,
70
+ default: '~/.aws/credential',
71
+ desc: 'The AWS credential file'
72
+ def get(key)
73
+ run_action { get! key }
74
+ end
75
+
76
+ private
77
+
78
+ def debug_header
79
+ @ui.debug { "Running #{APPLICATION_NAME} in debug mode..." }
80
+ @ui.debug { "Ruby Version: #{RUBY_VERSION}" }
81
+ @ui.debug { "Ruby Platform: #{RUBY_PLATFORM}" }
82
+ @ui.debug { "#{APPLICATION_NAME} Version: #{VERSION}" }
83
+ end
84
+
85
+ # Run the specified action and rescue errors we
86
+ # explicitly send back to format them
87
+ def run_action(&_block)
88
+ yield
89
+ rescue ::S3crets::Error => e
90
+ @ui.error e
91
+ abort "Fatal error: #{e.message}"
92
+ end
93
+
94
+ def init!
95
+ say "Initializing a new #{SECRETFILE_NAME} in the current directory..."
96
+ Actions::Init.new(@ui, options).run
97
+ say "Your #{SECRETFILE_NAME} has been created!"
98
+ say 'You should check the settings and add your secrets.'
99
+ say "Then run `#{APPLICATION_NAME} bundle` to fetch them."
100
+ end
101
+
102
+ def bundle!
103
+ say "Checking for secrets declared in your #{SECRETFILE_NAME}..."
104
+ Actions::Bundle.new(@ui, options).run
105
+ say 'Finished downloading secrets!'
106
+ end
107
+
108
+ def update!
109
+ say "Redownloading all secrets declared in your #{SECRETFILE_NAME}..."
110
+ Actions::Bundle.new(@ui, options, update: true).run
111
+ say 'Finished downloading secrets!'
112
+ end
113
+
114
+ def get!(path)
115
+ say Actions::Get.new(@ui, options, path).run
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,18 @@
1
+ require 's3crets/cli'
2
+
3
+ namespace :secrets do
4
+ env_glob = ENV['S3CRETS_ENVIRONMENT_GLOB'] || 'secrets/**/Secretfile'
5
+ environments = (Dir.glob env_glob).map { |f| File.dirname f }.uniq
6
+
7
+ environments.each do |env|
8
+ short_name = File.basename env
9
+
10
+ desc "Fetch secrets for #{short_name}"
11
+ task "#{short_name}" do
12
+ puts "CHDIR #{env}"
13
+ Dir.chdir env do
14
+ (S3crets::Cli.new).install
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ require 'pathname'
2
+
3
+ # Definitions for various strings used throughout the gem
4
+ module S3crets
5
+ APPLICATION_NAME = Pathname.new($PROGRAM_NAME).basename
6
+ SECRETFILE_NAME = 'Secretfile'
7
+ RESOLVED_NAME = 'Secretfile.resolved'
8
+ TEMPLATE_NAME = 'templates/Secretfile.erb'
9
+ DEFAULT_REGION = 'us-west-2'
10
+ DEFAULT_BUCKET = 'bucketname'
11
+
12
+ ALREADY_EXISTS_ERROR = "#{SECRETFILE_NAME} already exists! Aborting..."
13
+ NO_BUCKET_ERROR = 'Could not identify S3 bucket.'
14
+ NO_SECRETFILE_ERROR = "You don't have a #{SECRETFILE_NAME}! Run `#{APPLICATION_NAME} init` first!"
15
+ INVALID_SECRETFILE_ERROR = "Unable to load your #{SECRETFILE_NAME}. Please ensure it is valid YAML."
16
+ end
@@ -0,0 +1,6 @@
1
+ module S3crets
2
+ # A standard error with a different name
3
+ # for identifying errors internal to S3crets
4
+ class Error < StandardError
5
+ end
6
+ end
data/lib/s3crets/ui.rb ADDED
@@ -0,0 +1,42 @@
1
+ require 'rubygems/user_interaction'
2
+
3
+ module S3crets
4
+ # Prints messages out to stdout
5
+ class Shell
6
+ attr_writer :shell
7
+
8
+ def initialize(shell)
9
+ @shell = shell
10
+ @quiet = false
11
+ @debug = ENV['DEBUG']
12
+ end
13
+
14
+ def debug(message = nil)
15
+ @shell.say(message || yield) if @debug && !@quiet
16
+ end
17
+
18
+ def info(message = nil)
19
+ @shell.say(message || yield) unless @quiet
20
+ end
21
+
22
+ def confirm(message = nil)
23
+ @shell.say(message || yield, :green) unless @quiet
24
+ end
25
+
26
+ def warn(message = nil)
27
+ @shell.say(message || yield, :yellow)
28
+ end
29
+
30
+ def error(message = nil)
31
+ @shell.say(message || yield, :red)
32
+ end
33
+
34
+ def be_quiet!
35
+ @quiet = true
36
+ end
37
+
38
+ def debug!
39
+ @debug = true
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ module S3crets
2
+ VERSION = '0.0.2'
3
+ end
@@ -0,0 +1,8 @@
1
+ ---
2
+ settings:
3
+ bucket: 'testbucket'
4
+ region: 'us-west-2'
5
+ secret_dir: 'secrets/'
6
+ secrets:
7
+ tst1: 'this/is/secret/1.txt'
8
+ tst2: 'this/is/secret/2'
@@ -0,0 +1,55 @@
1
+ require 's3crets/cli'
2
+ require 's3crets/defaults'
3
+ require 'rspec'
4
+ require 'fakefs/spec_helpers'
5
+ require 'aws-sdk'
6
+
7
+ describe S3crets::Cli do
8
+ include FakeFS::SpecHelpers
9
+
10
+ describe '#bundle' do
11
+ context 'when Secretfile already exists' do
12
+ before(:each) do
13
+ spec_root = File.join(source_root, 'spec')
14
+ files = File.join(spec_root, 'files')
15
+ FakeFS::FileSystem.clone files
16
+
17
+ path = `gem path aws-sdk`.chomp
18
+ FakeFS::FileSystem.clone path, path
19
+ Dir.chdir File.join(source_root, 'spec', 'files')
20
+ end
21
+
22
+ before(:all) do
23
+ Aws.config[:stub_responses] = true
24
+ end
25
+
26
+ it 'should have a Secretfile' do
27
+ expect(FakeFS::FileTest.file? S3crets::SECRETFILE_NAME).to be(true)
28
+ expect(File.read(S3crets::SECRETFILE_NAME)).to include('testbucket')
29
+ end
30
+
31
+ xit 'should download a file' do
32
+ expect(FakeFS::FileTest.file? File.join('secrets', '1.txt')).to be(false)
33
+ expect { subject.bundle }.to output(/Finished downloading secrets/).to_stdout
34
+ expect(FakeFS::FileTest.file? File.join('secrets', '1.txt')).to be(true)
35
+ end
36
+
37
+ xit 'should create a resolved file' do
38
+ expect(FakeFS::FileTest.file? S3crets::RESOLVED_NAME).to be(false)
39
+ expect { subject.bundle }.to output(/Finished downloading secrets/).to_stdout
40
+ expect(FakeFS::FileTest.file? S3crets::RESOLVED_NAME).to be(true)
41
+ end
42
+
43
+ it 'should reject a malformed Secretfile' do
44
+ File.write('Secretfile', 'broken YAML')
45
+ expect { subject.bundle }.to raise_error.with_message(/#{S3crets::INVALID_SECRETFILE_ERROR}/)
46
+ end
47
+ end
48
+
49
+ context 'when Secretfile does not exist' do
50
+ it 'should return an error' do
51
+ expect { subject.bundle }.to raise_error.with_message(/#{S3crets::NO_SECRETFILE_ERROR}/)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,51 @@
1
+ require 's3crets/cli'
2
+ require 's3crets/defaults'
3
+ require 'rspec'
4
+ require 'fakefs/spec_helpers'
5
+
6
+ describe S3crets::Cli do
7
+ include FakeFS::SpecHelpers
8
+
9
+ describe '#init' do
10
+ before(:each) do
11
+ templates_root = File.join(source_root, 'templates')
12
+ FakeFS::FileSystem.clone templates_root
13
+ end
14
+
15
+ context 'when a file does not yet exist' do
16
+ it 'should create a template file' do
17
+ expect { subject.init }.to output(/has been created/).to_stdout
18
+ expect(FakeFS::FileTest.file? S3crets::SECRETFILE_NAME).to be(true)
19
+ expect(File.read S3crets::SECRETFILE_NAME).to match(/Fill in secrets/)
20
+ end
21
+
22
+ it 'should accept settings as options' do
23
+ subject.options = { 'region' => 'us-east-1',
24
+ 'bucket' => 'an_example_bucket',
25
+ 'secretdir' => '.' }
26
+
27
+ expect { subject.init }.to output(/has been created/).to_stdout
28
+ contents = File.read S3crets::SECRETFILE_NAME
29
+ expect(contents).to include('region: us-east-1')
30
+ expect(contents).to include('bucket: an_example_bucket')
31
+ expect(contents).to include('secret_dir: .')
32
+ end
33
+ end
34
+
35
+ context 'when a file already exists' do
36
+ before(:each) { File.write(S3crets::SECRETFILE_NAME, 'test value') }
37
+
38
+ it 'should not overwrite an existing file' do
39
+ expect { subject.init }.to raise_error.with_message(/#{S3crets::ALREADY_EXISTS_ERROR}/)
40
+ expect(File.read S3crets::SECRETFILE_NAME).to eq('test value')
41
+ end
42
+
43
+ it 'should obey the force flag' do
44
+ subject.options = { 'force' => true }
45
+
46
+ expect { subject.init }.to output(/has been created/).to_stdout
47
+ expect(File.read S3crets::SECRETFILE_NAME).to match('Fill in secrets')
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,14 @@
1
+
2
+ RSpec.configure do |config|
3
+ config.expect_with :rspec do |expectations|
4
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
5
+ end
6
+
7
+ config.mock_with :rspec do |mocks|
8
+ mocks.verify_partial_doubles = true
9
+ end
10
+ end
11
+
12
+ def source_root
13
+ File.expand_path('../../', __FILE__)
14
+ end
@@ -0,0 +1,9 @@
1
+ ---
2
+ settings:
3
+ bucket: <%= bucket %>
4
+ region: <%= region %>
5
+ credentials: <%= credential_file %>
6
+ secret_dir: .
7
+ secrets:
8
+ # TODO: Fill in secrets like so:
9
+ # my_secret: 'path/to/your/secret/file.ext'
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aws-s3crets
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Norm MacLennan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.29'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.29'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '3.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: fakefs
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '0.6'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '0.6'
83
+ - !ruby/object:Gem::Dependency
84
+ name: gem-path
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '0.6'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '0.6'
97
+ - !ruby/object:Gem::Dependency
98
+ name: aws-sdk
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '2.0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: '2.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: thor
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Fetch secret files from AWS S3 buckets.
126
+ email:
127
+ - norm.maclennan@gmail.com
128
+ executables:
129
+ - s3crets
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - .gitignore
134
+ - .rspec
135
+ - .rubocop.yml
136
+ - .travis.yml
137
+ - Gemfile
138
+ - LICENSE.txt
139
+ - README.md
140
+ - Rakefile
141
+ - aws-s3crets.gemspec
142
+ - bin/s3crets
143
+ - lib/s3crets.rb
144
+ - lib/s3crets/actions.rb
145
+ - lib/s3crets/actions/base.rb
146
+ - lib/s3crets/actions/bundle.rb
147
+ - lib/s3crets/actions/get.rb
148
+ - lib/s3crets/actions/init.rb
149
+ - lib/s3crets/cli.rb
150
+ - lib/s3crets/default_tasks.rb
151
+ - lib/s3crets/defaults.rb
152
+ - lib/s3crets/error.rb
153
+ - lib/s3crets/ui.rb
154
+ - lib/s3crets/version.rb
155
+ - spec/files/Secretfile
156
+ - spec/lib/s3crets/actions/bundle_spec.rb
157
+ - spec/lib/s3crets/actions/init_spec.rb
158
+ - spec/spec_helper.rb
159
+ - templates/Secretfile.erb
160
+ homepage: ''
161
+ licenses:
162
+ - MIT
163
+ metadata: {}
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ! '>='
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ! '>='
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ requirements: []
179
+ rubyforge_project:
180
+ rubygems_version: 2.4.5
181
+ signing_key:
182
+ specification_version: 4
183
+ summary: Fetch secret files from AWS S3 buckets.
184
+ test_files:
185
+ - spec/files/Secretfile
186
+ - spec/lib/s3crets/actions/bundle_spec.rb
187
+ - spec/lib/s3crets/actions/init_spec.rb
188
+ - spec/spec_helper.rb