X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/1ca973446ddf0094cd563a7af63f4e3078f5b772..b3d5254ce24ca82904b13d61012b2d8d676a30d8:/services/login-sync/bin/arvados-login-sync diff --git a/services/login-sync/bin/arvados-login-sync b/services/login-sync/bin/arvados-login-sync index 641c510251..e00495c04d 100755 --- a/services/login-sync/bin/arvados-login-sync +++ b/services/login-sync/bin/arvados-login-sync @@ -1,4 +1,7 @@ #!/usr/bin/env ruby +# Copyright (C) The Arvados Authors. All rights reserved. +# +# SPDX-License-Identifier: AGPL-3.0 require 'rubygems' require 'pp' @@ -7,50 +10,33 @@ require 'etc' require 'fileutils' require 'yaml' -# This script does the actual account/key management on disk for the shell machine(s). -# -# Ward Vandewege - -# Default is development -production = ARGV[0] == "production" - -ENV["RAILS_ENV"] = "development" -ENV["RAILS_ENV"] = "production" if production - -DEBUG = 1 - -# load and merge in the environment-specific application config info -# if present, overriding base config parameters as specified -path = File.dirname(__FILE__) + '/config/arvados-clients.yml' -if File.exists?(path) then - cp_config = YAML.load_file(path)[ENV['RAILS_ENV']] -else - puts "Please create a\n " + File.dirname(__FILE__) + "/config/arvados-clients.yml\n file" - exit 1 +req_envs = %w(ARVADOS_API_HOST ARVADOS_API_TOKEN ARVADOS_VIRTUAL_MACHINE_UUID) +req_envs.each do |k| + unless ENV[k] + abort "Fatal: These environment vars must be set: #{req_envs}" + end end -shell_hostname = cp_config['arvados_shell_hostname'] +exclusive_mode = ARGV.index("--exclusive") +exclusive_banner = "####################################################################################### +# THIS FILE IS MANAGED BY #{$0} -- CHANGES WILL BE OVERWRITTEN # +#######################################################################################\n\n" +start_banner = "### BEGIN Arvados-managed keys -- changes between markers will be overwritten\n" +end_banner = "### END Arvados-managed keys -- changes between markers will be overwritten\n" -ENV['ARVADOS_API_HOST'] = cp_config['arvados_api_host'] -ENV['ARVADOS_API_TOKEN'] = cp_config['arvados_api_token'] +# Don't try to create any local accounts +skip_missing_users = ARGV.index("--skip-missing-users") keys = '' -seen = Hash.new - begin + arv = Arvados.new({ :suppress_ssl_warnings => false }) - uids = Hash[Etc.to_enum(:passwd).map { |ent| [ent.name, ent.uid] }] - gids = Hash[Etc.to_enum(:group).map { |ent| [ent.name, ent.gid] }] - arv = Arvados.new( { :suppress_ssl_warnings => false } ) + vm_uuid = ENV['ARVADOS_VIRTUAL_MACHINE_UUID'] - begin - logins = arv.virtual_machine.get_all_logins(limit: 10000, uuid: cp_config['vm_uuid'])[:items] - rescue - logins = arv.virtual_machine.logins(:uuid => cp_config['vm_uuid'])[:items] - end + logins = arv.virtual_machine.logins(:uuid => vm_uuid)[:items] logins = [] if logins.nil? - logins = logins.reject { |l| l[:username].nil? or l[:hostname].nil? or l[:public_key].nil? or l[:hostname] != shell_hostname } + logins = logins.reject { |l| l[:username].nil? or l[:hostname].nil? or l[:public_key].nil? or l[:virtual_machine_uuid] != vm_uuid } # No system users uid_min = 1000 @@ -68,8 +54,25 @@ begin uid_min = new_uid_min if (new_uid_min > 0) end end - logins.reject! { |l| (uids[l[:username]] || 65535) < uid_min } + pwnam = Hash.new() + logins.reject! do |l| + if not pwnam[l[:username]] + begin + pwnam[l[:username]] = Etc.getpwnam(l[:username]) + rescue + if skip_missing_users + STDERR.puts "Account #{l[:username]} not found. Skipping" + true + end + else + if pwnam[l[:username]].uid < uid_min + STDERR.puts "Account #{l[:username]} uid #{pwnam[l[:username]].uid} < uid_min #{uid_min}. Skipping" + true + end + end + end + end keys = Hash.new() # Collect all keys @@ -90,39 +93,65 @@ begin logins.each do |l| next if seen[l[:username]] - seen[l[:username]] = true if not seen.has_key?(l[:username]) - @homedir = "/home/#{l[:username]}" + seen[l[:username]] = true - unless uids[l[:username]] + unless pwnam[l[:username]] STDERR.puts "Creating account #{l[:username]}" groups = l[:groups] || [] # Adding users to the FUSE group has long been hardcoded behavior. groups << "fuse" - groups.select! { |name| gids[name] } + groups.select! { |g| Etc.getgrnam(g) rescue false } # Create new user - next unless system("/usr/sbin/useradd", "-m", - "-c", l[:username], - "-s", "/bin/bash", - "-G", groups.join(","), - l[:username], - out: devnull) + unless system("useradd", "-m", + "-c", l[:username], + "-s", "/bin/bash", + "-G", groups.join(","), + l[:username], + out: devnull) + STDERR.puts "Account creation failed for #{l[:username]}: #{$?}" + next + end + begin + pwnam[l[:username]] = Etc.getpwnam(l[:username]) + rescue => e + STDERR.puts "Created account but then getpwnam() failed for #{l[:username]}: #{e}" + raise + end end - # Create .ssh directory if necessary + + @homedir = pwnam[l[:username]].dir userdotssh = File.join(@homedir, ".ssh") - Dir.mkdir(userdotssh) if !File.exists?(userdotssh) - @key = "####################################################################################### -# THIS FILE IS MANAGED BY #{$0} -- CHANGES WILL BE OVERWRITTEN # -#######################################################################################\n\n" - @key += keys[l[:username]].join("\n") + "\n" - userauthkeys = File.join(userdotssh, "authorized_keys") - if !File.exists?(userauthkeys) or IO::read(userauthkeys) != @key then - f = File.new(userauthkeys, 'w') - f.write(@key) + Dir.mkdir(userdotssh) if !File.exist?(userdotssh) + + newkeys = "###\n###\n" + keys[l[:username]].join("\n") + "\n###\n###\n" + + keysfile = File.join(userdotssh, "authorized_keys") + + if File.exist?(keysfile) + oldkeys = IO::read(keysfile) + else + oldkeys = "" + end + + if exclusive_mode + newkeys = exclusive_banner + newkeys + elsif oldkeys.start_with?(exclusive_banner) + newkeys = start_banner + newkeys + end_banner + elsif (m = /^(.*?\n|)#{start_banner}(.*?\n|)#{end_banner}(.*)/m.match(oldkeys)) + newkeys = m[1] + start_banner + newkeys + end_banner + m[3] + else + newkeys = start_banner + newkeys + end_banner + oldkeys + end + + if oldkeys != newkeys then + f = File.new(keysfile, 'w') + f.write(newkeys) f.close() end - FileUtils.chown_R(l[:username], l[:username], userdotssh) + FileUtils.chown_R(l[:username], nil, userdotssh) File.chmod(0700, userdotssh) File.chmod(0750, @homedir) + File.chmod(0600, keysfile) end devnull.close @@ -131,4 +160,3 @@ rescue Exception => bang puts bang.backtrace.join("\n") exit 1 end -