From 77f520c3df385f9a28004f41985bd038de10fd68 Mon Sep 17 00:00:00 2001 From: Jon Bracy Date: Tue, 20 Aug 2013 10:31:22 -0700 Subject: [PATCH] moved toward using github as the source for ssh keys --- Makefile | 8 +--- README.md | 36 ++++++---------- permissions.yml | 10 +++++ ssh-extract | 4 -- ssh-install | 76 ---------------------------------- ssh-package | 3 -- sync-accounts | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 132 insertions(+), 112 deletions(-) create mode 100644 permissions.yml delete mode 100755 ssh-extract delete mode 100755 ssh-install delete mode 100755 ssh-package create mode 100755 sync-accounts diff --git a/Makefile b/Makefile index ae9ac92..f218a6f 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,5 @@ install: - cp ssh-install /usr/local/bin/ - cp ssh-package /usr/local/bin/ - cp ssh-extract /usr/local/bin/ + cp sync-accounts /usr/local/bin/ uninstall: - rm /usr/local/bin/ssh-install - rm /usr/local/bin/ssh-package - rm /usr/local/bin/ssh-extract + rm /usr/local/bin/sync-accounts diff --git a/README.md b/README.md index aa46d1b..0d45775 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,29 @@ SSH Key Managment ================= -These scripts allow you to manage ssh keys for users and groups of users +These scripts allow you to manage ssh keys for users and apps ### Users -User keys are placed in a file with the users name in the `users` -directory. This file contains ssh keys for that user. Each line is one -SSH key and blank lines are ignored. +Users are listed in `permissions.yml`. Users are created and deleted on servers +match the list in `permissions.yml`. SSH keys are retreived from github (user +names are assumed to be the github usernames). -### Groups +### Apps -Group files are placed in file with the group name in the `groups` -directory. This file contains the names of users belonging to the -group. Each line contains the one username. Blank lines are ignored. +If an app user exist on a server the keys for the users listed under that app +are copied over allowing the users to SSH and deploy the app on that server. Commands ======== -### Package - -To package the `users` and `groups` for distribution run the `ssh-package -outputfile` script. This will generate a file that can be placed at a -location where other clients can download. - -### Extract - -`ssh-extract inputfile outputdir` will extract the keys to outputdir. - ### Install -`ssh-install http://url/file` will download the file produced by `package` and -go through all the system users. If the username matches one of the user files -then the ssh keys will be placed into authorized_keys. If the username matches -a group name all the ssh keys for that group will be added to authorized_keys +`sync-accounts https://url/to/permissions.yml` will download the permissions +file and sync the users. If a user is in `permissions.yml` that is not on the +system that user will be created. If a user exist on the system but no in +`permissions.yml` that user will be deleated. If an user with the name of an app +exist the keys will be copied to that users `authorized_keys` file. Installing ========== @@ -48,4 +38,4 @@ Installing You can set up cron to auto updated the ssh keys. Eg: -`0 * * * * /usr/local/bin/ssh-install http://domain.com/keysfile` +`0 * * * * /usr/local/bin/sync-accounts http://domain.com/permissions.yml` diff --git a/permissions.yml b/permissions.yml new file mode 100644 index 0000000..7521e96 --- /dev/null +++ b/permissions.yml @@ -0,0 +1,10 @@ +users: + - github_user_name_1 + - github_user_name_2 + +apps: + app1-staging: + - github_user_name_1 + - github_user_name_2 + app1-production: + - github_user_name_1 \ No newline at end of file diff --git a/ssh-extract b/ssh-extract deleted file mode 100755 index f80e3b9..0000000 --- a/ssh-extract +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -mkdir $2 -tar -zxvf $1 -C $2 diff --git a/ssh-install b/ssh-install deleted file mode 100755 index 7976194..0000000 --- a/ssh-install +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env ruby - -require 'uri' -require 'net/http' -require 'tmpdir' - -def download(url, filename) - url = URI.parse(url) - Net::HTTP.start(url.host) do |http| - resp = http.get(url.path) - open(filename, "wb") do |file| - file.write(resp.body) - end - end -end - -def read_users(dir) - users = {} - dir = File.join(dir, 'users') - Dir.entries(dir).select{|f| File.file?(File.join(dir, f)) }.each do |u| - users[u] = File.readlines(File.join(dir, u)).map{|l| l.strip}.select{|l| !l.empty? } - end - users -end - -def read_groups(dir, users) - groups = {} - dir = File.join(dir, 'groups') - Dir.entries(dir).select{|f| File.file?(File.join(dir, f)) }.each do |g| - members = File.readlines(File.join(dir, g)).map{|l| l.strip}.select{|l| !l.empty? } - groups[g] = members.map{ |u| users[u] }.flatten - end - groups -end - -def system_users - users = `cat /etc/passwd | grep -v root | grep '/bin/bash'`.split.map do |u| - u =~ /([^\s]+):.*:.*:.*:.*:([^\s]+):\/bin\/bash$/ - {:name => $1, :home => $2} - end - users = users.select{|u| u[:home] && u[:name] } - users.select{|u| u[:home].index('/srv') == 0 || u[:home].index('/home') == 0 } -end - -def write_authorized_keys(user, homedir, keys) - ssh_dir = File.join(homedir, '.ssh') - FileUtils.mkdir_p(ssh_dir) - FileUtils.chown_R(user, user, ssh_dir) - FileUtils.chmod(0700, ssh_dir) - - filename = File.join(ssh_dir, 'authorized_keys') - open(filename, 'w') do |file| - keys.each {|k| file.puts(k) } - end - FileUtils.chown(user, user, filename) - FileUtils.chmod(0600, filename) -end - -Dir.mktmpdir do |dir| - keyfile = File.join(dir, 'keys.tar.gz') - download(ARGV[0], keyfile) - `tar -zxf #{keyfile} -C #{dir}` - raise 'fail' if $?.exitstatus != 0 - - users = read_users(dir) - groups = read_groups(dir, users) - system_users.each do |su| - if users[su[:name]] - puts "writing authorized_keys for #{su[:name]}" - write_authorized_keys(su[:name], su[:home], users[su[:name]]) - elsif groups[su[:name]] - puts "writing authorized_keys for #{su[:name]}" - write_authorized_keys(su[:name], su[:home], groups[su[:name]]) - end - end -end diff --git a/ssh-package b/ssh-package deleted file mode 100755 index 0615384..0000000 --- a/ssh-package +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -tar -zcf $1 users groups diff --git a/sync-accounts b/sync-accounts new file mode 100755 index 0000000..e38fb5b --- /dev/null +++ b/sync-accounts @@ -0,0 +1,107 @@ +#!/usr/bin/env ruby +require 'uri' +require 'yaml' +require 'net/http' +require 'net/https' +require 'fileutils' + +def github_keys(user) + http = Net::HTTP.new('github.com', 443) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + + response = http.request(Net::HTTP::Get.new("/#{user}.keys")) + raise 'failure' unless response.is_a?(Net::HTTPOK) + response.body.split("\n") +end + +def read_permissions(url) + url = URI.parse(url) + permissions = { 'apps' => {}, 'users' => {} } + + yaml = Net::HTTP.start(url.host) do |http| + resp = http.get(url.path) + YAML.load(resp.body) + end + + # Add keys to users and apps + yaml['users'].each do |user| + permissions['users'][user] = github_keys(user) + end + yaml['apps'].each do |app, users| + permissions['apps'][app] = users.map{|u| permissions['users'][u] }.flatten + end + + permissions +end + +def system_users + users = `cat /etc/passwd | grep -v root | grep '/bin/bash'`.split.map do |u| + u =~ /([^\s]+):.*:.*:.*:.*:([^\s]+):\/bin\/bash$/ + {:name => $1, :home => $2} + end + users = users.select{|u| u[:home] && u[:name] } + users.select{|u| u[:home].index('/home') == 0 } +end + +def system_apps + users = `cat /etc/passwd | grep -v root | grep '/bin/bash'`.split.map do |u| + u =~ /([^\s]+):.*:.*:.*:.*:([^\s]+):\/bin\/bash$/ + {:name => $1, :home => $2} + end + users = users.select{|u| u[:home] && u[:name] } + users.select{|u| u[:home].index('/srv') == 0 } +end + +def add_system_user(name) + raise "invalid user name '#{name}'" unless name =~ /^[a-z][-a-z0-9]*$/ + + `adduser #{name} --gecos "" --disabled-password` + `usermod -a -G sudo #{name}` +end + +def remove_system_user(name) + `deluser --remove-home #{name}` +end + +def write_authorized_keys(user, homedir, keys) + ssh_dir = File.join(homedir, '.ssh') + FileUtils.mkdir_p(ssh_dir) + FileUtils.chown_R(user, user, ssh_dir) + FileUtils.chmod(0700, ssh_dir) + + filename = File.join(ssh_dir, 'authorized_keys') + open(filename, 'w') do |file| + keys.each {|k| file.puts(k) } + end + FileUtils.chown(user, user, filename) + FileUtils.chmod(0600, filename) +end + + +permissions = read_permissions(ARGV[0]) + +# Create missing users +(permissions['users'].keys - system_users.map{|su| su[:name]}).each do |user| + puts "adding user #{user}" + add_system_user(user) +end + +# Removed old users +(system_users.map{|su| su[:name]} - permissions['users'].keys).each do |user| + puts "removing user #{user}" + remove_system_user(user) +end + +# Write user ssh keys +system_users.each do |su| + puts "writing authorized_keys for #{su[:name]}" + write_authorized_keys(su[:name], su[:home], permissions['users'][su[:name]]) +end + +# Write app ssh keys +system_apps.each do |su| + next if !permissions['apps'][su[:name]] + puts "writing authorized_keys for #{su[:name]}" + write_authorized_keys(su[:name], su[:home], permissions['apps'][su[:name]]) +end \ No newline at end of file