keyman 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,16 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ keyman (1.0.0)
5
+ net-ssh (~> 2.6.3)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ net-ssh (2.6.3)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ keyman!
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # Keyman
2
+
3
+ This simple little utility allows you to manage the authorized_keys files for a
4
+ number of servers & users. It is designed to provide easy access to ensure that
5
+ you can revoke & grant access to appropriate people on multiple servers.
6
+
7
+ **Please Note: this utility is somewhat un-tested and not currently used in any
8
+ production environment. Your mileage may vary and we recommend testing in a
9
+ non-production environment prior to use.**
10
+
11
+ ## Installation
12
+
13
+ To install, just install the Rubygem.
14
+
15
+ ```bash
16
+ $ gem install keyman
17
+ ```
18
+
19
+ Once installed, you will need to create yourself a **manifest directory**. This
20
+ directory will contain all your configuration for your key manager. You should
21
+ create an empty directory and add two files, a `servers.rb` and a `users.rb` file.
22
+
23
+ ## Example Users/Groups Manifest File
24
+
25
+ The below file is an example of a `users.rb` manifest file.
26
+
27
+ ```ruby
28
+ group :admins do
29
+ user :adam, 'ssh-rsa AAAAB3NzaC1yc2EAAAA[...]=='
30
+ user :charlie, 'ssh-rsa AAAAB3NzaC1yc2EAAAA[...]=='
31
+ user :nathan, 'ssh-rsa AAAAB3NzaC1yc2EAAAA[...]=='
32
+ end
33
+
34
+ group :staff do
35
+ user :jack, 'ssh-rsa AAAAB3NzaC1yc2EAAAA[...]=='
36
+ user :dan, 'ssh-rsa AAAAB3NzaC1yc2EAAAA[...]=='
37
+ end
38
+ ```
39
+
40
+ ## Example Server Manifest File
41
+
42
+ The below file is an example of a `servers.rb` file.
43
+
44
+ ```ruby
45
+ # An example configuration for a server where all admin users have
46
+ # access as 'root' and all staff users have access as 'app'.
47
+ server do
48
+ host 'app01.myapplication.com'
49
+ user 'root', :admins
50
+ user 'app', :admins, :staff
51
+ end
52
+
53
+ # An example configuration for a server where admins plus one other user
54
+ # have access as root only.
55
+ server do
56
+ host 'database01.myapplication.com'
57
+ user 'root', :admins, :dan
58
+ end
59
+ ```
60
+
61
+ ## Pushing files to servers
62
+
63
+ In order to push files to the server, you must already have YOUR key on the
64
+ machine in order to authenticate. If you do not, you will not have access
65
+ and will therefore be unable to push configuration.
66
+
67
+ ```bash
68
+ $ cd path/to/manifest
69
+ # to push configuration to all servers
70
+ $ keyman push
71
+ # to push configuration to a specific server
72
+ $ keyman push database01.myapplication.com
73
+ ```
74
+
75
+ There are other commands available within the app, you can view these by
76
+ viewing the inline help.
77
+
78
+ ```bash
79
+ $ keyman help
80
+ ```
data/bin/keyman ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ require 'keyman'
3
+ begin
4
+
5
+ # Convert command line arguments into the appropriate ruby objects
6
+ args = ARGV.dup
7
+ final_args = []
8
+ options = {}
9
+ until args.empty?
10
+ arg = args.shift
11
+ if arg =~ /\A\-/
12
+ options[arg.gsub(/\A\-+/, '').to_sym] = args.shift
13
+ else
14
+ final_args << arg
15
+ end
16
+ end
17
+
18
+ command = final_args.first
19
+ case command
20
+ when 'help', nil
21
+ puts "This is the Keyman utility. You can use this to execute your key manifest"
22
+ puts "commands. In order to use this, you must be currently within your"
23
+ puts "manifest directory."
24
+ puts
25
+ puts " keys {server} {user} - displays the authorized keys file for a server's user"
26
+ puts " push - pushes the latest files to all servers"
27
+ puts " push {server} - pushes the latest file to the specified server"
28
+ puts " servers - displays a list of all servers"
29
+ puts " permissions {server} - displays the permissions for given server"
30
+ puts " users - displays a list of all users & groups"
31
+ puts
32
+ else
33
+ Keyman.run(final_args, options)
34
+ end
35
+
36
+ rescue Keyman::Error => e
37
+ puts "\e[31m" + e.message + "\e[0m"
38
+ Process.exit(1)
39
+ end
data/keyman.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
2
+ require 'keyman'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'keyman'
6
+ s.version = Keyman::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.summary = "A simple library for managing distributed SSH keys"
9
+ s.description = s.summary
10
+ s.files = Dir["**/*"]
11
+ s.bindir = "bin"
12
+ s.executables << 'keyman'
13
+ s.require_path = 'lib'
14
+ s.has_rdoc = false
15
+ s.author = "Adam Cooke"
16
+ s.email = "adam@atechmedia.com"
17
+ s.homepage = "http://atechmedia.com"
18
+ s.add_dependency('net-ssh', '~> 2.6.3')
19
+ end
20
+
@@ -0,0 +1,24 @@
1
+ module Keyman
2
+ class Group
3
+
4
+ attr_accessor :name, :users
5
+
6
+ def initialize
7
+ @users = []
8
+ end
9
+
10
+ # Add a new group with the given name
11
+ def self.add(name, &block)
12
+ g = Group.new
13
+ g.name = name
14
+ g.instance_eval(&block)
15
+ Keyman.groups << g
16
+ end
17
+
18
+ # Add a new user to the group
19
+ def user(*args)
20
+ @users << User.add(*args)
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ module Keyman
2
+ class Keyfile
3
+
4
+ class << self
5
+ def group(name, &users_block)
6
+ Group.add(name, &users_block)
7
+ end
8
+
9
+ def server(&block)
10
+ Server.add(&block)
11
+ end
12
+
13
+ def user(username, key)
14
+ User.add(username, key)
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,81 @@
1
+ module Keyman
2
+ class Server
3
+
4
+ attr_accessor :host, :users, :location
5
+
6
+ def initialize
7
+ @users = {}
8
+ end
9
+
10
+ # Adds a new server by accepting a block of objects
11
+ def self.add(&block)
12
+ s = self.new
13
+ s.instance_eval(&block)
14
+ Keyman.servers << s
15
+ s
16
+ end
17
+
18
+ # Returns or sets the hostname of the server which should be used when connecting
19
+ # and identifying this server
20
+ def host(host = nil)
21
+ host ? @host = host : @host
22
+ end
23
+
24
+ # Sets a user on the server along with the access objects which should be
25
+ # granted access
26
+ def user(name, *access_objects)
27
+ @users[name] = access_objects
28
+ end
29
+
30
+ # Returns or sets the location of the server
31
+ def location(location = nil)
32
+ location ? @location = location : @location
33
+ end
34
+
35
+ # Returns an array of users who have access to this server. Includes
36
+ # all objects from within the server
37
+ def authorized_users(username)
38
+ @users[username].map do |k|
39
+ obj = Keyman.user_or_group_for(k)
40
+ obj.is_a?(Group) ? obj.users : obj
41
+ end.flatten.uniq
42
+ end
43
+
44
+ # Returns a full string output for the authorized_keys file. Passes
45
+ # the user who's file you wish to generate.
46
+ def authorized_keys(username)
47
+ Array.new.tap do |a|
48
+ a << "# SSH Authorized Keys file generated automatically by Keyman"
49
+ a << "# Generated at: #{Time.now.utc} for #{@host}"
50
+ a << nil
51
+ authorized_users(username).each do |u|
52
+ a << "# #{u.name}"
53
+ a << u.key + "\n"
54
+ end
55
+ end.join("\n")
56
+ end
57
+
58
+ # Push the authorized keys file to the appropriate server for the users
59
+ # configured here. This will not succeed if the current user does not
60
+ # already have a key on the server.
61
+ def push!
62
+ @users.each do |user, objects|
63
+ begin
64
+ Timeout.timeout(10) do |t|
65
+ Net::SSH.start(self.host, user) do |ssh|
66
+ ssh.exec!("mkdir -p ~/.ssh")
67
+ file = authorized_keys(user).gsub("\n", "\\n").gsub("\t", "\\t")
68
+ ssh.exec!("echo -e '#{file}' > ~/.ssh/authorized_keys")
69
+ end
70
+ end
71
+ puts "\e[32mPushed authorized_keys to #{user}@#{self.host}\e[0m"
72
+ rescue Timeout::Error
73
+ puts "\e[31mTimed out while uploading authorized_keys to #{user}@#{self.host}\e[0m"
74
+ rescue
75
+ puts "\e[31mFailed to upload authorized_keys to #{user}@#{self.host}\e[0m"
76
+ end
77
+ end
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,21 @@
1
+ module Keyman
2
+ class User
3
+
4
+ attr_accessor :name, :key
5
+
6
+ # Add a new user with the given details
7
+ def self.add(name, key)
8
+ existing = Keyman.users.select { |u| u.name == name.to_sym }.first
9
+ if existing
10
+ existing
11
+ else
12
+ u = self.new
13
+ u.name = name.to_sym
14
+ u.key = key
15
+ Keyman.users << u
16
+ u
17
+ end
18
+ end
19
+
20
+ end
21
+ end
data/lib/keyman.rb ADDED
@@ -0,0 +1,104 @@
1
+ require 'net/ssh'
2
+
3
+ require 'keyman/user'
4
+ require 'keyman/group'
5
+ require 'keyman/server'
6
+ require 'keyman/keyfile'
7
+
8
+
9
+ module Keyman
10
+
11
+ # The current version of the keyman system
12
+ VERSION = '1.0.0'
13
+
14
+ # An error which will be raised
15
+ class Error < StandardError; end
16
+
17
+ class << self
18
+ # Storage for all the users, groups & servers which are loaded
19
+ # from the manifest
20
+ attr_accessor :users
21
+ attr_accessor :groups
22
+ attr_accessor :servers
23
+
24
+ # Load a manifest from the given folder
25
+ def load(directory)
26
+ self.users = []
27
+ self.groups = []
28
+ self.servers = []
29
+ if File.directory?(directory)
30
+ ['groups.rb', 'servers.rb'].each do |file|
31
+ path = File.join(directory, file)
32
+ if File.exist?(path)
33
+ Keyman::Keyfile.class_eval(File.read(path))
34
+ else
35
+ raise Error, "No '#{file}' was found in your manifest directory. Abandoning..."
36
+ end
37
+ end
38
+ else
39
+ raise Error, "No folder found at '#{directory}'"
40
+ end
41
+ end
42
+
43
+ # Return a user or a group for the given name
44
+ def user_or_group_for(name)
45
+ self.users.select { |u| u.name == name.to_sym }.first || self.groups.select { |u| u.name == name.to_sym }.first
46
+ end
47
+
48
+ # Execute a CLI command
49
+ def run(args, options = {})
50
+ load('./')
51
+ case args.first
52
+ when 'keys'
53
+ if server = self.servers.select { |s| s.host == args[1] }.first
54
+ if server.users[args[2]]
55
+ puts server.authorized_keys(args[2])
56
+ else
57
+ raise Error, "'#{args[2]}' is not a valid user for this host"
58
+ end
59
+ else
60
+ raise Error, "No server found with the hostname '#{args[1]}'"
61
+ end
62
+ when 'permissions'
63
+ if server = self.servers.select { |s| s.host == args[1] }.first
64
+ server.users.each do |username, objects|
65
+ puts '-' * 80
66
+ puts "\e[32m#{username}\e[0m can be used by:"
67
+ puts '-' * 80
68
+ server.authorized_users(username).each do |o|
69
+ puts " * #{o.name}"
70
+ end
71
+ end
72
+ else
73
+ raise Error, "No server found with the hostname '#{args[1]}'"
74
+ end
75
+ when 'push'
76
+ if args[1]
77
+ # push single server
78
+ if server = self.servers.select { |s| s.host == args[1] }.first
79
+ server.push!
80
+ else
81
+ raise Error, "No server found with the hostname '#{args[1]}'"
82
+ end
83
+ else
84
+ self.servers.each(&:push!)
85
+ end
86
+ when 'servers'
87
+ self.servers.each do |server|
88
+ puts " * " + server.host
89
+ end
90
+ when 'users'
91
+ self.groups.each do |group|
92
+ puts "-" * 80
93
+ puts group.name
94
+ puts "-" * 80
95
+ group.users.each do |u|
96
+ puts "\e[32m#{u.name}\e[0m"
97
+ puts "\e[37m#{u.key}\e[0m"
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: keyman
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Adam Cooke
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: net-ssh
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.6.3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.6.3
30
+ description: A simple library for managing distributed SSH keys
31
+ email: adam@atechmedia.com
32
+ executables:
33
+ - keyman
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - bin/keyman
38
+ - Gemfile
39
+ - Gemfile.lock
40
+ - keyman.gemspec
41
+ - lib/keyman/group.rb
42
+ - lib/keyman/keyfile.rb
43
+ - lib/keyman/server.rb
44
+ - lib/keyman/user.rb
45
+ - lib/keyman.rb
46
+ - README.md
47
+ homepage: http://atechmedia.com
48
+ licenses: []
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ requirements: []
66
+ rubyforge_project:
67
+ rubygems_version: 1.8.23
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: A simple library for managing distributed SSH keys
71
+ test_files: []