aws-s3crets 0.0.2

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