Skip to content

Instantly share code, notes, and snippets.

@yacn
Last active February 24, 2023 18:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yacn/15256fbfd7a4ec19568f009e4a0255c7 to your computer and use it in GitHub Desktop.
Save yacn/15256fbfd7a4ec19568f009e4a0255c7 to your computer and use it in GitHub Desktop.
Patch for Ruby's Etc module to avoid using C-extensions on modern hosts in GCE with oslogin enabled. Provided as-is, without warranty, express or implied.
# monkey patch Etc module to work around the segfault that occurs using when
# oslogin is enabled on newer systems (Debian 11+, Ubuntu 20.04+)
# https://github.com/chef/chef/issues/12993
module PatchedEtc
def _getent(db, key, regex, schema, struct_klass, error_word = db)
info = `getent #{db} #{key}`.chomp
raise(ArgumentError, "can't find #{error_word} for #{key}") unless $?.success?
fn_schema = schema.map { |k, v| v.class == Class ? [k, _to_class_fn(v)] : [k, v] }.to_h
matches = info.match(regex)
init_vars = fn_schema.map { |k, fn| fn.call(matches[k]) }
struct_klass.new(*init_vars)
end
def _getent_passwd(key)
r = /(?<name>.+):(?<passwd>.+):(?<uid>\d+):(?<gid>\d+):(?<gecos>.+)?:(?<dir>.+):(?<shell>.+)/
schema = { name: String, passwd: String, uid: Integer, gid: Integer, gecos: String, dir: String, shell: String }
_getent('passwd', key, r, schema, Etc::Passwd, 'user')
end
def _to_class_fn(klass)
->(v) { v.is_a?(klass) ? v : Object.send(klass.to_s.to_sym, v) }
end
def _getent_group(key)
r = /(?<name>.+):(?<passwd>.+):(?<gid>\d+):(?<mem>.+)?/
schema = {
name: String, passwd: String, gid: Integer,
mem: ->(v) { v ? v.split(',') : [] },
}
_getent('group', key, r, schema, Etc::Group)
end
def getgrgid(group_id)
_getent_group(group_id)
end
def getgrnam(name)
_getent_group(name)
end
def getlogin
ENV['USER']
end
def getpwnam(name)
_getent_passwd(name)
end
def getpwuid(user_id)
_getent_passwd(user_id)
end
end
# gce_metadata is just a helper function to query the instance's metadata
# implementation is left as an exercise for the reader 😉
def oslogin_enabled?
r = gce_metadata('attributes/enable-oslogin')
return false unless r
r.downcase == 'true'
end
module Etc
class << self
prepend PatchedEtc if Kernel.send(:oslogin_enabled?)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment