linux-hub 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +29 -0
- data/LICENSE +21 -0
- data/README.md +39 -0
- data/bin/linux-hub +6 -0
- data/lib/linux-hub.rb +42 -0
- data/lib/linux-hub/cli.rb +58 -0
- data/lib/linux-hub/github.rb +13 -0
- data/lib/linux-hub/github_team.rb +35 -0
- data/lib/linux-hub/github_user.rb +27 -0
- data/lib/linux-hub/linux_user.rb +106 -0
- data/linux-hub.gemspec +17 -0
- metadata +86 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1835a8518e08f8db5864552b7c9730ae9e152a0f
|
4
|
+
data.tar.gz: 9c17f6eef9941130b02201af019c1e3ff2b00d75
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: efa0448c9cc2a2b548bca6cc7616e62c0502424b6142dbc3357b71f53f01d8acc228264f279babd1ee4fdea3626eafceabea8c8cb262a8dff405c9729ddb5148
|
7
|
+
data.tar.gz: 3c45d0df7255e256f63d7461b42227e633228b342aae63ce394cde747b07e7af069ae417b75fce97cdfc925a40ac2962b78194a4414ee995f6fd1141c439b8ce
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
config/*
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
linux-hub (0.0.1)
|
5
|
+
octokit
|
6
|
+
trollop
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
addressable (2.4.0)
|
12
|
+
faraday (0.9.2)
|
13
|
+
multipart-post (>= 1.2, < 3)
|
14
|
+
multipart-post (2.0.0)
|
15
|
+
octokit (4.3.0)
|
16
|
+
sawyer (~> 0.7.0, >= 0.5.3)
|
17
|
+
sawyer (0.7.0)
|
18
|
+
addressable (>= 2.3.5, < 2.5)
|
19
|
+
faraday (~> 0.8, < 0.10)
|
20
|
+
trollop (2.1.2)
|
21
|
+
|
22
|
+
PLATFORMS
|
23
|
+
ruby
|
24
|
+
|
25
|
+
DEPENDENCIES
|
26
|
+
linux-hub!
|
27
|
+
|
28
|
+
BUNDLED WITH
|
29
|
+
1.11.2
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 Patrick Robinson
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# linux-hub
|
2
|
+
|
3
|
+
Create Linux users from Github Teams
|
4
|
+
|
5
|
+
## Challenges
|
6
|
+
|
7
|
+
If you want to use immutable AMIs it can be difficult to give users access,
|
8
|
+
you need to deploy a new version of your AMI.
|
9
|
+
|
10
|
+
Netflix [BLESS](https://github.com/Netflix/bless) serves to solve a similar problem, but is much more complicated (and feature rich).
|
11
|
+
|
12
|
+
## Solution
|
13
|
+
|
14
|
+
Github exposes all users public SSH keys via their API
|
15
|
+
We can associate users to a Github Team
|
16
|
+
We can query the members of a team via the Github API, provided we have an access key with 'read:org' permissions
|
17
|
+
|
18
|
+
So we can therefore use the Github API to provide authorization and authentication of users to systems.
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
The idea of this gem is to be run as a cron job, to synchronise users at a regular interval. You need to create a config file that specifies:
|
23
|
+
- The organisation to find the team in
|
24
|
+
- The team who are permitted access
|
25
|
+
- The access key to query team membership
|
26
|
+
|
27
|
+
Example config:
|
28
|
+
```yaml
|
29
|
+
---
|
30
|
+
organisation: github
|
31
|
+
team: sysadmins
|
32
|
+
access_key: baconfoobar
|
33
|
+
```
|
34
|
+
|
35
|
+
Example command:
|
36
|
+
|
37
|
+
```
|
38
|
+
linux-hub --config-file config/config.yaml --sync-users
|
39
|
+
```
|
data/bin/linux-hub
ADDED
data/lib/linux-hub.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'octokit'
|
3
|
+
require 'linux-hub/github_team'
|
4
|
+
require 'linux-hub/github_user'
|
5
|
+
require 'linux-hub/github'
|
6
|
+
require 'linux-hub/linux_user'
|
7
|
+
require 'linux-hub/cli'
|
8
|
+
|
9
|
+
module LinuxHub
|
10
|
+
ACTIONS = [:list, :create_users, :sync_users]
|
11
|
+
|
12
|
+
def self.invoke
|
13
|
+
options = Trollop::options do
|
14
|
+
opt :config_file, "The config file to read options from", type: :string, required: true
|
15
|
+
opt :list, "List users in the Github Team", type: :boolean
|
16
|
+
opt :create_users, "Create users in the Github Team", type: :boolean
|
17
|
+
opt :sync_users, "Manage all users in the Github Team", type: :boolean
|
18
|
+
end
|
19
|
+
|
20
|
+
config = load_config(options[:config_file])
|
21
|
+
if config["access_token"].nil?
|
22
|
+
puts "You need an access token with 'read:org' permissions for the organisation"
|
23
|
+
exit 1
|
24
|
+
elsif config["organisation"].nil? || config["team"].nil?
|
25
|
+
puts "Please provide the team and organisation in the relevant config file"
|
26
|
+
end
|
27
|
+
|
28
|
+
action = options.select { |k,v| ACTIONS.include?(k) && v == true }
|
29
|
+
|
30
|
+
unless action.length == 1
|
31
|
+
puts "Please specify one of the following action commands\n#{ACTIONS}"
|
32
|
+
exit 1
|
33
|
+
end
|
34
|
+
|
35
|
+
cli = CLI.new(config)
|
36
|
+
cli.send(action.keys.first)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.load_config(config_file)
|
40
|
+
YAML.load_file(config_file)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module LinuxHub
|
2
|
+
class CLI
|
3
|
+
def initialize(config)
|
4
|
+
@organization = config["organisation"]
|
5
|
+
@team = config["team"]
|
6
|
+
@groups = config["groups"]
|
7
|
+
|
8
|
+
Github.instance.access_token = config["access_token"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def list
|
12
|
+
puts github_users.collect(&:authorized_keys)
|
13
|
+
end
|
14
|
+
|
15
|
+
def sync_users
|
16
|
+
linux_users = LinuxUser.users_in_group
|
17
|
+
linux_usernames = linux_users.collect(&:username)
|
18
|
+
github_usernames = github_users.collect(&:username)
|
19
|
+
# Equivalent to github_users - linux_users
|
20
|
+
users_to_add = github_users.reject { |u| linux_usernames.include? u.username }
|
21
|
+
# Equivalent to linux_users - github_users
|
22
|
+
users_to_delete = linux_users.reject { |u| github_usernames.include? u.username }
|
23
|
+
add_users(users_to_add)
|
24
|
+
delete_users(users_to_delete)
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_users
|
28
|
+
add_users(github_users)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def add_users(users)
|
34
|
+
users.each do |user|
|
35
|
+
LinuxUser.new(
|
36
|
+
username: user.username,
|
37
|
+
groups: @groups,
|
38
|
+
ssh_keys: user.ssh_keys
|
39
|
+
).create
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete_users(users)
|
44
|
+
users.each do |user|
|
45
|
+
LinuxUser.new(
|
46
|
+
username: user.username
|
47
|
+
).delete
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def github_users
|
52
|
+
GithubTeam.new(
|
53
|
+
organisation: @organization,
|
54
|
+
team: @team,
|
55
|
+
).users
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module LinuxHub
|
2
|
+
class GithubTeam
|
3
|
+
def initialize(organisation:, team:)
|
4
|
+
@org = organisation
|
5
|
+
@team = team
|
6
|
+
end
|
7
|
+
|
8
|
+
def users
|
9
|
+
@users ||= fetch_users
|
10
|
+
end
|
11
|
+
|
12
|
+
def team_id
|
13
|
+
@team_id ||= fetch_team_id
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def fetch_users
|
19
|
+
client.team_members(team_id).collect do |member|
|
20
|
+
GithubUser.new(member.login)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch_team_id
|
25
|
+
client.auto_paginate = true
|
26
|
+
team = client.organization_teams(@org).find { |t| t[:name] == @team }
|
27
|
+
client.auto_paginate = false
|
28
|
+
team.id
|
29
|
+
end
|
30
|
+
|
31
|
+
def client
|
32
|
+
Github.instance.client
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module LinuxHub
|
2
|
+
class GithubUser
|
3
|
+
attr_reader :username, :email, :ssh_keys
|
4
|
+
|
5
|
+
def initialize(username)
|
6
|
+
@username = username
|
7
|
+
fetch_user_details
|
8
|
+
end
|
9
|
+
|
10
|
+
def authorized_keys
|
11
|
+
ssh_keys.map do |key|
|
12
|
+
"#{key} #{email}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def fetch_user_details
|
19
|
+
@email = client.user(@username).email || "#{@username}@github"
|
20
|
+
@ssh_keys = client.user_keys(@username).collect(&:key)
|
21
|
+
end
|
22
|
+
|
23
|
+
def client
|
24
|
+
Github.instance.client
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module LinuxHub
|
2
|
+
class LinuxUser
|
3
|
+
# The default group is used to keep track of members
|
4
|
+
# Members in this group not in the appropriate Github Team are purged
|
5
|
+
DEFAULT_GROUP = 'linuxhub'
|
6
|
+
|
7
|
+
def self.users_in_group
|
8
|
+
File.open("/etc/group") do |f|
|
9
|
+
f.each_line do |line|
|
10
|
+
user = line.split(":")
|
11
|
+
if user.first == DEFAULT_GROUP
|
12
|
+
return user[3].chomp.split(',').collect { |u| new(username: u) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :username
|
20
|
+
|
21
|
+
def initialize(username:, groups: [], ssh_keys: [])
|
22
|
+
@username = username
|
23
|
+
@groups = (groups || []) + [DEFAULT_GROUP]
|
24
|
+
@ssh_keys = ssh_keys
|
25
|
+
end
|
26
|
+
|
27
|
+
def create
|
28
|
+
create_default_group
|
29
|
+
create_user
|
30
|
+
create_user_keys
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete
|
34
|
+
delete_home
|
35
|
+
delete_user
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def create_default_group
|
41
|
+
return if group_exists?
|
42
|
+
%x(groupadd #{DEFAULT_GROUP})
|
43
|
+
end
|
44
|
+
|
45
|
+
def group_exists?
|
46
|
+
thing_exists? "/etc/group", DEFAULT_GROUP
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_user
|
50
|
+
return if user_exists?
|
51
|
+
# Create the user and assign them to some groups
|
52
|
+
# Don't create a group for this user
|
53
|
+
# Create the home directory for this user
|
54
|
+
%x(useradd -G #{@groups.join(',')} -N -m #{@username})
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete_user
|
58
|
+
return unless user_exists?
|
59
|
+
%x(userdel #{@username})
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete_home
|
63
|
+
FileUtils.rm_r(home_dir)
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_user_keys
|
67
|
+
ssh_dir = File.join(home_dir, ".ssh")
|
68
|
+
Dir.mkdir(ssh_dir, 0700) unless Dir.exist? ssh_dir
|
69
|
+
File.open(File.join(ssh_dir, "authorized_keys"), "w", 0600) do |f|
|
70
|
+
@ssh_keys.each do |key|
|
71
|
+
f.write "#{key}\n"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
FileUtils.chown_R(@username, nil, ssh_dir)
|
75
|
+
end
|
76
|
+
|
77
|
+
def home_dir
|
78
|
+
File.open("/etc/passwd") do |f|
|
79
|
+
f.each_line do |line|
|
80
|
+
user = line.split(":")
|
81
|
+
if user.first == @username
|
82
|
+
return user[5]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
fail "User not found!"
|
87
|
+
end
|
88
|
+
|
89
|
+
def user_exists?
|
90
|
+
thing_exists? "/etc/passwd", @username
|
91
|
+
end
|
92
|
+
|
93
|
+
def thing_exists?(file, value)
|
94
|
+
File.open(file) do |f|
|
95
|
+
f.each_line do |line|
|
96
|
+
user = line.split(":")
|
97
|
+
if user.first == value
|
98
|
+
f.close
|
99
|
+
return true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
false
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/linux-hub.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'linux-hub'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.summary = "Linux users from Github"
|
5
|
+
s.description = "Import linux users from Github Organisations and Teams"
|
6
|
+
s.authors = ["Patrick Robinson"]
|
7
|
+
s.email = 'therealpatrobinson@gmail.com'
|
8
|
+
s.files = `git ls-files`.split($/)
|
9
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
10
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
11
|
+
s.require_paths = ['lib']
|
12
|
+
s.homepage = 'https://github.com/patrobinson/linux-hub'
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.add_dependency 'octokit'
|
16
|
+
s.add_dependency 'trollop'
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: linux-hub
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Patrick Robinson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-08-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: octokit
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: trollop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Import linux users from Github Organisations and Teams
|
42
|
+
email: therealpatrobinson@gmail.com
|
43
|
+
executables:
|
44
|
+
- linux-hub
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- Gemfile
|
50
|
+
- Gemfile.lock
|
51
|
+
- LICENSE
|
52
|
+
- README.md
|
53
|
+
- bin/linux-hub
|
54
|
+
- lib/linux-hub.rb
|
55
|
+
- lib/linux-hub/cli.rb
|
56
|
+
- lib/linux-hub/github.rb
|
57
|
+
- lib/linux-hub/github_team.rb
|
58
|
+
- lib/linux-hub/github_user.rb
|
59
|
+
- lib/linux-hub/linux_user.rb
|
60
|
+
- linux-hub.gemspec
|
61
|
+
homepage: https://github.com/patrobinson/linux-hub
|
62
|
+
licenses:
|
63
|
+
- MIT
|
64
|
+
metadata: {}
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 2.4.5.1
|
82
|
+
signing_key:
|
83
|
+
specification_version: 4
|
84
|
+
summary: Linux users from Github
|
85
|
+
test_files: []
|
86
|
+
has_rdoc:
|