[arvados] created: 2.6.0-307-gee35d22df
git repository hosting
git at public.arvados.org
Thu Jun 22 15:39:46 UTC 2023
at ee35d22df94f1745f97c17f3171e8663fa2e375e (commit)
commit ee35d22df94f1745f97c17f3171e8663fa2e375e
Author: Brett Smith <brett.smith at curii.com>
Date: Thu Jun 22 11:10:29 2023 -0400
20663: Add configuration for arvados-login-sync
Arvados-DCO-1.1-Signed-off-by: Brett Smith <brett.smith at curii.com>
diff --git a/lib/config/config.default.yml b/lib/config/config.default.yml
index 0fb4a2bab..921a53578 100644
--- a/lib/config/config.default.yml
+++ b/lib/config/config.default.yml
@@ -412,6 +412,27 @@ Clusters:
# Use 0 to disable activity logging.
ActivityLoggingPeriod: 24h
+ # The SyncUser* options control what system resources are managed by
+ # arvados-login-sync on shell nodes. They correspond to:
+ # * SyncUserAccounts: The user's Unix account on the shell node
+ # * SyncUserGroups: The group memberships of that account
+ # * SyncUserSSHKeys: Whether to authorize the user's Arvados SSH keys
+ # * SyncUserAPITokens: Whether to set up the user's Arvados API token
+ # All default to true.
+ SyncUserAccounts: true
+ SyncUserGroups: true
+ SyncUserSSHKeys: true
+ SyncUserAPITokens: true
+
+ # If SyncUserGroups=true, then arvados-login-sync will ensure that all
+ # managed accounts are members of the Unix groups listed in
+ # SyncRequiredGroups, in addition to any groups listed in their Arvados
+ # login permission. The default list includes the "fuse" group so
+ # users can use arv-mount. You can require no groups by specifying an
+ # empty list (i.e., `SyncRequiredGroups: []`).
+ SyncRequiredGroups:
+ - fuse
+
AuditLogs:
# Time to keep audit logs, in seconds. (An audit log is a row added
# to the "logs" table in the PostgreSQL database each time an
diff --git a/lib/config/export.go b/lib/config/export.go
index 31ccc994b..d51b02d6c 100644
--- a/lib/config/export.go
+++ b/lib/config/export.go
@@ -247,6 +247,11 @@ var whitelist = map[string]bool{
"Users.NewUsersAreActive": false,
"Users.PreferDomainForUsername": false,
"Users.RoleGroupsVisibleToAll": false,
+ "Users.SyncRequiredGroups": true,
+ "Users.SyncUserAccounts": true,
+ "Users.SyncUserAPITokens": true,
+ "Users.SyncUserGroups": true,
+ "Users.SyncUserSSHKeys": true,
"Users.UserNotifierEmailBcc": false,
"Users.UserNotifierEmailFrom": false,
"Users.UserProfileNotificationAddress": false,
diff --git a/sdk/go/arvados/config.go b/sdk/go/arvados/config.go
index 0fafa41f9..62dfca45c 100644
--- a/sdk/go/arvados/config.go
+++ b/sdk/go/arvados/config.go
@@ -258,6 +258,11 @@ type Cluster struct {
RoleGroupsVisibleToAll bool
CanCreateRoleGroups bool
ActivityLoggingPeriod Duration
+ SyncRequiredGroups []string
+ SyncUserAccounts bool
+ SyncUserAPITokens bool
+ SyncUserGroups bool
+ SyncUserSSHKeys bool
}
StorageClasses map[string]StorageClassConfig
Volumes map[string]Volume
commit b0cbacbab436749a2a94e5bb7a8b9400641bed35
Author: Brett Smith <brett.smith at curii.com>
Date: Thu Jun 22 10:52:05 2023 -0400
20663: Clarify comment about what "safe" config means
I might've liked to reveal configuration to authenticated clients, but
not unauthenticated ones. Unfortunately the code doesn't currently
support that.
Arvados-DCO-1.1-Signed-off-by: Brett Smith <brett.smith at curii.com>
diff --git a/lib/config/export.go b/lib/config/export.go
index f46f5b6f8..31ccc994b 100644
--- a/lib/config/export.go
+++ b/lib/config/export.go
@@ -37,8 +37,8 @@ func ExportJSON(w io.Writer, cluster *arvados.Cluster) error {
return json.NewEncoder(w).Encode(m)
}
-// whitelist classifies configs as safe/unsafe to reveal to
-// unauthenticated clients.
+// whitelist classifies configs as safe/unsafe to reveal through the API
+// endpoint. Note that endpoint does not require authentication.
//
// Every config entry must either be listed explicitly here along with
// all of its parent keys (e.g., "API" + "API.RequestTimeout"), or
commit 354335e323305190b957c59c4be26c1231c2d115
Author: Brett Smith <brett.smith at curii.com>
Date: Thu Jun 22 10:43:36 2023 -0400
20663: Make arvados-login-sync actions configurable
Arvados-DCO-1.1-Signed-off-by: Brett Smith <brett.smith at curii.com>
diff --git a/services/login-sync/bin/arvados-login-sync b/services/login-sync/bin/arvados-login-sync
index df68a4030..1a825c90f 100755
--- a/services/login-sync/bin/arvados-login-sync
+++ b/services/login-sync/bin/arvados-login-sync
@@ -46,6 +46,15 @@ exclusive_banner = "############################################################
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"
+actions = {
+ # These names correspond to the names in the cluster Users configuration.
+ # Managing everything was the original behavior.
+ SyncUserAccounts: true,
+ SyncUserGroups: true,
+ SyncUserSSHKeys: true,
+ SyncUserAPITokens: true,
+}
+
keys = ''
begin
@@ -58,6 +67,9 @@ begin
logincluster_name = arv.cluster_config['Login']['LoginCluster'] or ''
# Requiring the fuse group was previous hardcoded behavior
minimum_groups = arv.cluster_config['Login']['SyncRequiredGroups'] || ['fuse']
+ actions.each_pair do |key, default|
+ actions[key] = arv.cluster_config['Login'].fetch(key.to_s, default)
+ end
if logincluster_name != '' and logincluster_name != arv.cluster_config['ClusterID']
logincluster_host = arv.cluster_config['RemoteClusters'][logincluster_name]['Host']
@@ -143,6 +155,10 @@ begin
username = l[:username]
unless pwnam[l[:username]]
+ unless actions[:SyncUserAccounts]
+ STDERR.puts "User #{username} does not exist and SyncUserAccounts=false. Skipping."
+ next
+ end
STDERR.puts "Creating account #{l[:username]}"
# Create new user
out, st = Open3.capture2e("useradd", "-m",
@@ -168,105 +184,111 @@ begin
next
end
- have_groups = current_user_groups[username]
- want_groups = l[:groups] || []
- want_groups |= minimum_groups
- want_groups &= all_groups
-
- (want_groups - have_groups).each do |addgroup|
- # User should be in group, but isn't, so add them.
- STDERR.puts "Add user #{username} to #{addgroup} group"
- out, st = Open3.capture2e("usermod", "-aG", addgroup, username)
- if st.exitstatus != 0
- STDERR.puts "Failed to add #{username} to #{addgroup} group:\n#{out}"
+ if actions[:SyncUserGroups]
+ have_groups = current_user_groups[username]
+ want_groups = l[:groups] || []
+ want_groups |= minimum_groups
+ want_groups &= all_groups
+
+ (want_groups - have_groups).each do |addgroup|
+ # User should be in group, but isn't, so add them.
+ STDERR.puts "Add user #{username} to #{addgroup} group"
+ out, st = Open3.capture2e("usermod", "-aG", addgroup, username)
+ if st.exitstatus != 0
+ STDERR.puts "Failed to add #{username} to #{addgroup} group:\n#{out}"
+ end
end
- end
- (have_groups - want_groups).each do |removegroup|
- # User is in a group, but shouldn't be, so remove them.
- STDERR.puts "Remove user #{username} from #{removegroup} group"
- out, st = Open3.capture2e("gpasswd", "-d", username, removegroup)
- if st.exitstatus != 0
- STDERR.puts "Failed to remove user #{username} from #{removegroup} group:\n#{out}"
+ (have_groups - want_groups).each do |removegroup|
+ # User is in a group, but shouldn't be, so remove them.
+ STDERR.puts "Remove user #{username} from #{removegroup} group"
+ out, st = Open3.capture2e("gpasswd", "-d", username, removegroup)
+ if st.exitstatus != 0
+ STDERR.puts "Failed to remove user #{username} from #{removegroup} group:\n#{out}"
+ end
end
end
- userdotssh = File.join(homedir, ".ssh")
- ensure_dir(userdotssh, 0700, username, user_gid)
+ if actions[:SyncUserSSHKeys]
+ userdotssh = File.join(homedir, ".ssh")
+ ensure_dir(userdotssh, 0700, username, user_gid)
- newkeys = "###\n###\n" + keys[l[:username]].join("\n") + "\n###\n###\n"
+ newkeys = "###\n###\n" + keys[l[:username]].join("\n") + "\n###\n###\n"
- keysfile = File.join(userdotssh, "authorized_keys")
- begin
- oldkeys = File.read(keysfile)
- rescue Errno::ENOENT
- oldkeys = ""
- end
+ keysfile = File.join(userdotssh, "authorized_keys")
+ begin
+ oldkeys = File.read(keysfile)
+ rescue Errno::ENOENT
+ oldkeys = ""
+ end
- if options[:exclusive]
- 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 options[:exclusive]
+ 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
- File.open(keysfile, 'w', 0600) do |f|
- f.write(newkeys)
+ if oldkeys != newkeys then
+ File.open(keysfile, 'w', 0600) do |f|
+ f.write(newkeys)
+ end
+ FileUtils.chown(username, user_gid, keysfile)
end
- FileUtils.chown(username, user_gid, keysfile)
end
- userdotconfig = File.join(homedir, ".config")
- ensure_dir(userdotconfig, 0755, username, user_gid)
- configarvados = File.join(userdotconfig, "arvados")
- ensure_dir(configarvados, 0700, username, user_gid)
-
- tokenfile = File.join(configarvados, "settings.conf")
-
- begin
- STDERR.puts "Processing #{tokenfile} ..." if debug
- newToken = false
- if File.exist?(tokenfile)
- # check if the token is still valid
- myToken = ENV["ARVADOS_API_TOKEN"]
- userEnv = File.read(tokenfile)
- if (m = /^ARVADOS_API_TOKEN=(.*?\n)/m.match(userEnv))
- begin
- tmp_arv = Arvados.new({ :api_host => logincluster_host,
- :api_token => (m[1]),
- :suppress_ssl_warnings => false })
- tmp_arv.user.current
- rescue Arvados::TransactionFailedError => e
- if e.to_s =~ /401 Unauthorized/
- STDERR.puts "Account #{l[:username]} token not valid, creating new token."
- newToken = true
- else
- raise
+ if actions[:SyncUserAPITokens]
+ userdotconfig = File.join(homedir, ".config")
+ ensure_dir(userdotconfig, 0755, username, user_gid)
+ configarvados = File.join(userdotconfig, "arvados")
+ ensure_dir(configarvados, 0700, username, user_gid)
+
+ tokenfile = File.join(configarvados, "settings.conf")
+
+ begin
+ STDERR.puts "Processing #{tokenfile} ..." if debug
+ newToken = false
+ if File.exist?(tokenfile)
+ # check if the token is still valid
+ myToken = ENV["ARVADOS_API_TOKEN"]
+ userEnv = File.read(tokenfile)
+ if (m = /^ARVADOS_API_TOKEN=(.*?\n)/m.match(userEnv))
+ begin
+ tmp_arv = Arvados.new({ :api_host => logincluster_host,
+ :api_token => (m[1]),
+ :suppress_ssl_warnings => false })
+ tmp_arv.user.current
+ rescue Arvados::TransactionFailedError => e
+ if e.to_s =~ /401 Unauthorized/
+ STDERR.puts "Account #{l[:username]} token not valid, creating new token."
+ newToken = true
+ else
+ raise
+ end
end
end
+ elsif !File.exist?(tokenfile) || options[:"rotate-tokens"]
+ STDERR.puts "Account #{l[:username]} token file not found, creating new token."
+ newToken = true
end
- elsif !File.exist?(tokenfile) || options[:"rotate-tokens"]
- STDERR.puts "Account #{l[:username]} token file not found, creating new token."
- newToken = true
- end
- if newToken
- aca_params = {owner_uuid: l[:user_uuid], api_client_id: 0}
- if options[:"token-lifetime"] && options[:"token-lifetime"] > 0
- aca_params.merge!(expires_at: (Time.now + options[:"token-lifetime"]))
- end
- user_token = logincluster_arv.api_client_authorization.create(api_client_authorization: aca_params)
- File.open(tokenfile, 'w', 0600) do |f|
- f.write("ARVADOS_API_HOST=#{ENV['ARVADOS_API_HOST']}\n")
- f.write("ARVADOS_API_TOKEN=v2/#{user_token[:uuid]}/#{user_token[:api_token]}\n")
+ if newToken
+ aca_params = {owner_uuid: l[:user_uuid], api_client_id: 0}
+ if options[:"token-lifetime"] && options[:"token-lifetime"] > 0
+ aca_params.merge!(expires_at: (Time.now + options[:"token-lifetime"]))
+ end
+ user_token = logincluster_arv.api_client_authorization.create(api_client_authorization: aca_params)
+ File.open(tokenfile, 'w', 0600) do |f|
+ f.write("ARVADOS_API_HOST=#{ENV['ARVADOS_API_HOST']}\n")
+ f.write("ARVADOS_API_TOKEN=v2/#{user_token[:uuid]}/#{user_token[:api_token]}\n")
+ end
+ FileUtils.chown(username, user_gid, tokenfile)
end
- FileUtils.chown(username, user_gid, tokenfile)
+ rescue => e
+ STDERR.puts "Error setting token for #{l[:username]}: #{e}"
end
- rescue => e
- STDERR.puts "Error setting token for #{l[:username]}: #{e}"
end
end
commit 62aae5c096dbee2a5a26c0865c1b05b2c0d646d9
Author: Brett Smith <brett.smith at curii.com>
Date: Thu Jun 22 10:18:02 2023 -0400
20663: Use set operations for group management
Make code more consistent for easier readability.
Arvados-DCO-1.1-Signed-off-by: Brett Smith <brett.smith at curii.com>
diff --git a/services/login-sync/bin/arvados-login-sync b/services/login-sync/bin/arvados-login-sync
index df986661f..df68a4030 100755
--- a/services/login-sync/bin/arvados-login-sync
+++ b/services/login-sync/bin/arvados-login-sync
@@ -126,11 +126,12 @@ begin
seen = Hash.new()
- current_user_groups = Hash.new
+ all_groups = []
+ current_user_groups = Hash.new { |hash, key| hash[key] = [] }
while (ent = Etc.getgrent()) do
+ all_groups << ent.name
ent.mem.each do |member|
- current_user_groups[member] ||= Array.new
- current_user_groups[member].push ent.name
+ current_user_groups[member] << ent.name
end
end
Etc.endgrent()
@@ -167,30 +168,26 @@ begin
next
end
- existing_groups = current_user_groups[username] || []
- groups = l[:groups] || []
- groups |= minimum_groups
- groups.select! { |g| Etc.getgrnam(g) rescue false }
-
- groups.each do |addgroup|
- if existing_groups.index(addgroup).nil?
- # User should be in group, but isn't, so add them.
- STDERR.puts "Add user #{username} to #{addgroup} group"
- out, st = Open3.capture2e("usermod", "-aG", addgroup, username)
- if st.exitstatus != 0
- STDERR.puts "Failed to add #{username} to #{addgroup} group:\n#{out}"
- end
+ have_groups = current_user_groups[username]
+ want_groups = l[:groups] || []
+ want_groups |= minimum_groups
+ want_groups &= all_groups
+
+ (want_groups - have_groups).each do |addgroup|
+ # User should be in group, but isn't, so add them.
+ STDERR.puts "Add user #{username} to #{addgroup} group"
+ out, st = Open3.capture2e("usermod", "-aG", addgroup, username)
+ if st.exitstatus != 0
+ STDERR.puts "Failed to add #{username} to #{addgroup} group:\n#{out}"
end
end
- existing_groups.each do |removegroup|
- if groups.index(removegroup).nil?
- # User is in a group, but shouldn't be, so remove them.
- STDERR.puts "Remove user #{username} from #{removegroup} group"
- out, st = Open3.capture2e("gpasswd", "-d", username, removegroup)
- if st.exitstatus != 0
- STDERR.puts "Failed to remove user #{username} from #{removegroup} group:\n#{out}"
- end
+ (have_groups - want_groups).each do |removegroup|
+ # User is in a group, but shouldn't be, so remove them.
+ STDERR.puts "Remove user #{username} from #{removegroup} group"
+ out, st = Open3.capture2e("gpasswd", "-d", username, removegroup)
+ if st.exitstatus != 0
+ STDERR.puts "Failed to remove user #{username} from #{removegroup} group:\n#{out}"
end
end
commit b3a5002baa8892eef46973a8e576003e8d588032
Author: Brett Smith <brett.smith at curii.com>
Date: Thu Jun 22 10:16:40 2023 -0400
20663: Make minimum groups configurable
Note this no longer adds the user's own group to the membership
list. The administrator can control that by adjusting USERGROUPS_ENAB
in /etc/login.defs.
Arvados-DCO-1.1-Signed-off-by: Brett Smith <brett.smith at curii.com>
diff --git a/services/login-sync/bin/arvados-login-sync b/services/login-sync/bin/arvados-login-sync
index a3150b8fd..df986661f 100755
--- a/services/login-sync/bin/arvados-login-sync
+++ b/services/login-sync/bin/arvados-login-sync
@@ -56,6 +56,8 @@ begin
arv = Arvados.new({ :suppress_ssl_warnings => false })
logincluster_host = ENV['ARVADOS_API_HOST']
logincluster_name = arv.cluster_config['Login']['LoginCluster'] or ''
+ # Requiring the fuse group was previous hardcoded behavior
+ minimum_groups = arv.cluster_config['Login']['SyncRequiredGroups'] || ['fuse']
if logincluster_name != '' and logincluster_name != arv.cluster_config['ClusterID']
logincluster_host = arv.cluster_config['RemoteClusters'][logincluster_name]['Host']
@@ -167,9 +169,7 @@ begin
existing_groups = current_user_groups[username] || []
groups = l[:groups] || []
- # Adding users to the FUSE group has long been hardcoded behavior.
- groups << "fuse"
- groups << username
+ groups |= minimum_groups
groups.select! { |g| Etc.getgrnam(g) rescue false }
groups.each do |addgroup|
commit c0442ca116662954ba21df6a1f35aafa4210ae7c
Author: Brett Smith <brett.smith at curii.com>
Date: Thu Jun 22 09:48:44 2023 -0400
20663: Improve permissions and ownership handling
* Set permissions of everything at creation time.
* Only change ownership for things we touch.
* Manage group ownership as well as user
(having things owned by user:root is weird).
* Modernize style.
This is preparation for allowing administrators to configure what
resources arvados-login-sync manages.
Note this means arvados-login-sync no longer changes permissions for a
user's home directory. The administrator can do that by setting UMASK
in /etc/login.defs.
Arvados-DCO-1.1-Signed-off-by: Brett Smith <brett.smith at curii.com>
diff --git a/services/login-sync/bin/arvados-login-sync b/services/login-sync/bin/arvados-login-sync
index 9bcb5bfa9..a3150b8fd 100755
--- a/services/login-sync/bin/arvados-login-sync
+++ b/services/login-sync/bin/arvados-login-sync
@@ -12,6 +12,18 @@ require 'yaml'
require 'optparse'
require 'open3'
+def ensure_dir(path, mode, owner, group)
+ begin
+ Dir.mkdir(path, mode)
+ rescue Errno::EEXIST
+ # No change needed
+ false
+ else
+ FileUtils.chown(owner, group, path)
+ true
+ end
+end
+
req_envs = %w(ARVADOS_API_HOST ARVADOS_API_TOKEN ARVADOS_VIRTUAL_MACHINE_UUID)
req_envs.each do |k|
unless ENV[k]
@@ -146,6 +158,7 @@ begin
end
end
+ user_gid = pwnam[username].gid
homedir = pwnam[l[:username]].dir
if !File.exist?(homedir)
STDERR.puts "Cannot set up user #{username} because their home directory #{homedir} does not exist. Skipping."
@@ -182,15 +195,14 @@ begin
end
userdotssh = File.join(homedir, ".ssh")
- Dir.mkdir(userdotssh) if !File.exist?(userdotssh)
+ ensure_dir(userdotssh, 0700, username, user_gid)
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
+ begin
+ oldkeys = File.read(keysfile)
+ rescue Errno::ENOENT
oldkeys = ""
end
@@ -205,18 +217,16 @@ begin
end
if oldkeys != newkeys then
- f = File.new(keysfile, 'w')
- f.write(newkeys)
- f.close()
+ File.open(keysfile, 'w', 0600) do |f|
+ f.write(newkeys)
+ end
+ FileUtils.chown(username, user_gid, keysfile)
end
userdotconfig = File.join(homedir, ".config")
- if !File.exist?(userdotconfig)
- Dir.mkdir(userdotconfig)
- end
-
+ ensure_dir(userdotconfig, 0755, username, user_gid)
configarvados = File.join(userdotconfig, "arvados")
- Dir.mkdir(configarvados) if !File.exist?(configarvados)
+ ensure_dir(configarvados, 0700, username, user_gid)
tokenfile = File.join(configarvados, "settings.conf")
@@ -226,7 +236,7 @@ begin
if File.exist?(tokenfile)
# check if the token is still valid
myToken = ENV["ARVADOS_API_TOKEN"]
- userEnv = IO::read(tokenfile)
+ userEnv = File.read(tokenfile)
if (m = /^ARVADOS_API_TOKEN=(.*?\n)/m.match(userEnv))
begin
tmp_arv = Arvados.new({ :api_host => logincluster_host,
@@ -252,25 +262,15 @@ begin
aca_params.merge!(expires_at: (Time.now + options[:"token-lifetime"]))
end
user_token = logincluster_arv.api_client_authorization.create(api_client_authorization: aca_params)
- f = File.new(tokenfile, 'w')
- f.write("ARVADOS_API_HOST=#{ENV['ARVADOS_API_HOST']}\n")
- f.write("ARVADOS_API_TOKEN=v2/#{user_token[:uuid]}/#{user_token[:api_token]}\n")
- f.close()
+ File.open(tokenfile, 'w', 0600) do |f|
+ f.write("ARVADOS_API_HOST=#{ENV['ARVADOS_API_HOST']}\n")
+ f.write("ARVADOS_API_TOKEN=v2/#{user_token[:uuid]}/#{user_token[:api_token]}\n")
+ end
+ FileUtils.chown(username, user_gid, tokenfile)
end
rescue => e
STDERR.puts "Error setting token for #{l[:username]}: #{e}"
end
-
- FileUtils.chown_R(l[:username], nil, userdotssh)
- FileUtils.chown_R(l[:username], nil, userdotconfig)
- File.chmod(0700, userdotssh)
- File.chmod(0700, userdotconfig)
- File.chmod(0700, configarvados)
- File.chmod(0750, homedir)
- File.chmod(0600, keysfile)
- if File.exist?(tokenfile)
- File.chmod(0600, tokenfile)
- end
end
rescue Exception => bang
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list