linux-hub 0.0.1
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 +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:
|