keyble 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ gem "net-scp"
4
+
5
+ group :development do
6
+ gem "bundler"
7
+ gem "jeweler"
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Scott Tadman
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # keyble
2
+
3
+ SSH keychain management for servers.
4
+
5
+ ## Usage
6
+
7
+ ```
8
+ keyble --import keyfile
9
+ keyble --import keyfile server [server [...]]
10
+ keyble --add user@host server [server [...]]
11
+ keyble --remove user@host server [server [...]]
12
+ keyble --list
13
+ keyble --list server [server [...]]
14
+ ```
15
+
16
+ ## Copyright
17
+
18
+ Copyright (c) 2012 Scott Tadman, The Working Group Inc.
19
+ See LICENSE.txt for further details.
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+
6
+ begin
7
+ Bundler.setup(:default, :development)
8
+ rescue Bundler::BundlerError => e
9
+ $stderr.puts e.message
10
+ $stderr.puts "Run `bundle install` to install missing gems"
11
+ exit e.status_code
12
+ end
13
+
14
+ require 'rake'
15
+ require 'jeweler'
16
+
17
+ Jeweler::Tasks.new do |gem|
18
+ gem.name = "keyble"
19
+ gem.homepage = "http://github.com/twg/keyble"
20
+ gem.license = "MIT"
21
+ gem.summary = %Q{SSH Key Management}
22
+ gem.description = %Q{Unubtrusive SSH key management}
23
+ gem.email = "scott@twg.ca"
24
+ gem.authors = [ "Scott Tadman" ]
25
+ # Dependencies defined in Gemfile
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rake/testtask'
30
+
31
+ Rake::TestTask.new(:test) do |test|
32
+ test.libs << 'lib' << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+
37
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/keyble ADDED
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path('../lib', File.dirname(__FILE__))
4
+
5
+ require 'keyble'
6
+ require 'optparse'
7
+ require 'net/scp'
8
+
9
+ method = :list
10
+ key = nil
11
+
12
+ parser = OptionParser.new do |parser|
13
+ parser.on("-c", "--cache=s") do |s|
14
+ Keyble.cache_file_path = s
15
+ end
16
+ parser.on("-a", "--add=s") do |s|
17
+ method = :add
18
+ key = s
19
+ end
20
+ parser.on("-r", "--remove=s") do |s|
21
+ method = :remove
22
+ key = s
23
+ end
24
+ parser.on("-i", "--import") do |s|
25
+ method = :import
26
+ end
27
+ parser.on("-l", "--list") do
28
+ method = :list
29
+ end
30
+ end
31
+
32
+ args = parser.parse(*ARGV)
33
+
34
+ case (method)
35
+ when :add
36
+ if (File.exist?(key))
37
+ imported = Keyble.keys_import(key)
38
+
39
+ if (args.any?)
40
+ Keyble.keys_add(imported, args)
41
+ else
42
+ STDERR.puts "No servers specified."
43
+ exit(-11)
44
+ end
45
+ elsif (args.any?)
46
+ if (entry = Keyble.cache[key])
47
+ Keyble.keys_add({ key => entry }, args)
48
+ else
49
+ STDERR.puts "Could not locate key for #{key}"
50
+ end
51
+ else
52
+ puts parser
53
+ exit(-1)
54
+ end
55
+ when :remove
56
+ if (File.exist?(key))
57
+ imported = Keyble.keys_import(key)
58
+
59
+ if (args.any?)
60
+ Keyble.keys_remove(imported, args)
61
+ else
62
+ STDERR.puts "No servers specified."
63
+ exit(-11)
64
+ end
65
+ elsif (args.any?)
66
+ Keyble.keys_remove([ key ], args)
67
+ else
68
+ puts parser
69
+ exit(-1)
70
+ end
71
+ when :import
72
+ if (args.any?)
73
+ imported = { }
74
+
75
+ servers = args.reject do |path|
76
+ if (File.exist?(path))
77
+ imported.merge!(Keyble.read(path))
78
+
79
+ true
80
+ else
81
+ false
82
+ end
83
+ end
84
+
85
+ if (servers.any?)
86
+ server_keys = Keyble.keys_get(servers)
87
+
88
+ server_keys.each do |server, keys|
89
+ imported.merge!(keys)
90
+ end
91
+ end
92
+
93
+ Keyble.cache_merge!(imported)
94
+ Keyble.cache_save!
95
+
96
+ Keyble.keys_display(imported)
97
+ else
98
+ STDERR.puts "No servers specified"
99
+ exit(-11)
100
+ end
101
+ when :list
102
+ if (args.any?)
103
+ Keyble.keys_get(args).each do |server, keys|
104
+ if (args.length > 1)
105
+ puts server
106
+ puts '-' * 78
107
+ end
108
+
109
+ Keyble.keys_display(keys)
110
+ end
111
+ else
112
+ if (Keyble.cache.any?)
113
+ Keyble.keys_display(Keyble.cache)
114
+ else
115
+ puts "No keys in #{Keyble.cache_file_path}"
116
+ end
117
+ end
118
+ end
data/keyble.gemspec ADDED
@@ -0,0 +1,57 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "keyble"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Scott Tadman"]
12
+ s.date = "2013-02-27"
13
+ s.description = "Unubtrusive SSH key management"
14
+ s.email = "scott@twg.ca"
15
+ s.executables = ["keyble"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.md"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ "Gemfile",
23
+ "LICENSE.txt",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "bin/keyble",
28
+ "keyble.gemspec",
29
+ "lib/keyble.rb",
30
+ "test/helper.rb",
31
+ "test/test_keyble.rb"
32
+ ]
33
+ s.homepage = "http://github.com/twg/keyble"
34
+ s.licenses = ["MIT"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = "1.8.23"
37
+ s.summary = "SSH Key Management"
38
+
39
+ if s.respond_to? :specification_version then
40
+ s.specification_version = 3
41
+
42
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
43
+ s.add_runtime_dependency(%q<net-scp>, [">= 0"])
44
+ s.add_development_dependency(%q<bundler>, [">= 0"])
45
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
46
+ else
47
+ s.add_dependency(%q<net-scp>, [">= 0"])
48
+ s.add_dependency(%q<bundler>, [">= 0"])
49
+ s.add_dependency(%q<jeweler>, [">= 0"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<net-scp>, [">= 0"])
53
+ s.add_dependency(%q<bundler>, [">= 0"])
54
+ s.add_dependency(%q<jeweler>, [">= 0"])
55
+ end
56
+ end
57
+
data/lib/keyble.rb ADDED
@@ -0,0 +1,133 @@
1
+ module Keyble
2
+ def parse(data)
3
+ result = { }
4
+
5
+ data.split(/\r?\n/).collect do |line|
6
+ type, key, comment = line.split(/\s+/)
7
+
8
+ if (comment and !comment.empty? and comment.match(/\S+\@\S+/))
9
+ result[comment] = line
10
+ end
11
+ end
12
+
13
+ result
14
+ end
15
+
16
+ def read(path)
17
+ result = { }
18
+
19
+ File.open(path) do |f|
20
+ result = parse(f.read)
21
+ end
22
+
23
+ result
24
+ end
25
+
26
+ def write(path, data)
27
+ File.open(path, 'w') do |f|
28
+ data.each do |key, line|
29
+ f.puts(line)
30
+ end
31
+ end
32
+ end
33
+
34
+ def ssh_authorized_keys_path
35
+ ".ssh/authorized_keys"
36
+ end
37
+
38
+ def cache_file_path=(value)
39
+ @cache_file_path = value
40
+ end
41
+
42
+ def cache_file_path
43
+ @cache_file_path or File.expand_path(".keyble", ENV['HOME'])
44
+ end
45
+
46
+ def cache
47
+ @cache ||=
48
+ if (File.exist?(cache_file_path))
49
+ read(cache_file_path)
50
+ else
51
+ { }
52
+ end
53
+ end
54
+
55
+ def cache_merge!(data)
56
+ cache.merge!(data)
57
+
58
+ cache
59
+ end
60
+
61
+ def cache_save!
62
+ return unless (@cache)
63
+
64
+ write(cache_file_path, @cache)
65
+ end
66
+
67
+ def keys_get(servers)
68
+ result = { }
69
+
70
+ servers.each do |server|
71
+ Net::SCP.start(server, nil) do |scp|
72
+ result[server] = parse(scp.download!(ssh_authorized_keys_path))
73
+ end
74
+ end
75
+
76
+ result
77
+ end
78
+
79
+ def keys_reassign(servers)
80
+ servers.each do |server|
81
+ Net::SCP.start(server, nil) do |scp|
82
+ existing = parse(scp.download!(ssh_authorized_keys_path))
83
+
84
+ merged = yield(server, existing.dup)
85
+
86
+ if (merged != existing)
87
+ authorized_keys = StringIO.new(merged.values.join("\n"))
88
+
89
+ scp.upload!(authorized_keys, ssh_authorized_keys_path)
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ def keys_add(keys, servers)
96
+ keys_reassign(servers) do |server, existing_keys|
97
+ existing_keys.merge(keys)
98
+ end
99
+ end
100
+
101
+ def keys_remove(keys, servers)
102
+ if (keys.respond_to?(:keys))
103
+ keys = keys.keys
104
+ end
105
+
106
+ keys_reassign(servers) do |server, existing_keys|
107
+ keys.each do |key|
108
+ existing_keys.delete(key)
109
+ end
110
+
111
+ existing_keys
112
+ end
113
+ end
114
+
115
+ def keys_import(file)
116
+ imported = read(file)
117
+
118
+ cache_merge!(imported)
119
+ cache_save!
120
+
121
+ imported
122
+ end
123
+
124
+ def keys_display(keys)
125
+ longest_key = keys.keys.collect { |k| k.to_s.length }.sort[-1]
126
+
127
+ keys.sort.each do |key, line|
128
+ puts "%-#{longest_key}s %s" % [ key, line.split(/\s+/)[1][-20, 20] ]
129
+ end
130
+ end
131
+
132
+ extend self
133
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'keyble'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,4 @@
1
+ require File.expand_path('helper', File.dirname(__FILE__))
2
+
3
+ class TestKeyble < Test::Unit::TestCase
4
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: keyble
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Scott Tadman
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: net-scp
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
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: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: jeweler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Unubtrusive SSH key management
63
+ email: scott@twg.ca
64
+ executables:
65
+ - keyble
66
+ extensions: []
67
+ extra_rdoc_files:
68
+ - LICENSE.txt
69
+ - README.md
70
+ files:
71
+ - .document
72
+ - Gemfile
73
+ - LICENSE.txt
74
+ - README.md
75
+ - Rakefile
76
+ - VERSION
77
+ - bin/keyble
78
+ - keyble.gemspec
79
+ - lib/keyble.rb
80
+ - test/helper.rb
81
+ - test/test_keyble.rb
82
+ homepage: http://github.com/twg/keyble
83
+ licenses:
84
+ - MIT
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 1.8.23
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: SSH Key Management
107
+ test_files: []