[Change Request] Add script/cron job for checking git repo perms
Mike McGrath
mmcgrath at redhat.com
Fri Aug 21 00:07:32 UTC 2009
On Thu, 20 Aug 2009, Todd Zullinger wrote:
> The git::check-perms class includes a script for checking that the
> permissions of git repositories are generally proper for shared
> repositories. It also runs this script each day via a cron job.
>
> This is included on the hosted1 node.
> ---
>
> The intent of this script is to check that git repos on hosted don't
> end up with permissions that will cause problems when folks try to
> push to them. This shouldn't happen too often anymore since we fixed
> a git bug and have better scripts for creating the repositories, but
> it can still crop up.
>
> This script won't catch something like the a repo having the wrong
> group, unless we want to standardize on group naming and fix up
> existing repositories that don't follow that convention. (Which
> doesn't seem worth the effort.)
>
> I just picked the time for the cron job at random. If there is a
> better time for it to run, I can change it before pushing this.
>
> The change should be very low risk and easy to fix should it cause any
> problems. If I weren't likely to forget about it, it could wait until
> after the freeze it over. :)
>
> manifests/nodes/hosted1.fedoraproject.org.pp | 4 +
> modules/git/README | 4 +
> modules/git/files/check-perms.py | 148 ++++++++++++++++++++++++++
> modules/git/manifests/init.pp | 31 ++++++
> 4 files changed, 187 insertions(+), 0 deletions(-)
> create mode 100755 modules/git/files/check-perms.py
>
> diff --git a/manifests/nodes/hosted1.fedoraproject.org.pp b/manifests/nodes/hosted1.fedoraproject.org.pp
> index e94c151..a6c86c0 100644
> --- a/manifests/nodes/hosted1.fedoraproject.org.pp
> +++ b/manifests/nodes/hosted1.fedoraproject.org.pp
> @@ -5,6 +5,10 @@ node hosted1 {
> include openvpn::client
> include spamassassin::server
>
> + $git_check_perms_gitroot = "/git"
> + $git_check_perms_mailto = "sysadmin-hosted-members at fedoraproject.org"
> + include git::check-perms
> +
> $mailman_default_url_proto = "https"
> $mailman_default_url_host = "fedorahosted.org"
> $mailman_default_email_host = "lists.fedorahosted.org"
> diff --git a/modules/git/README b/modules/git/README
> index e9a5e99..100a560 100644
> --- a/modules/git/README
> +++ b/modules/git/README
> @@ -14,6 +14,10 @@ The git rpm installs the core tools with minimal dependencies. To
> install all git packages, including tools for integrating with other
> SCMs, install the git-all meta-package.
>
> +The git::check-perms class includes a script for checking that the
> +permissions of git repositories are generally proper for shared
> +repositories. It also runs this script each day via a cron job.
> +
> The git::mail-hooks class installs some convenient tools for use as
> post-receive hooks, courtesy of the gnome.org sysadmins.
>
> diff --git a/modules/git/files/check-perms.py b/modules/git/files/check-perms.py
> new file mode 100755
> index 0000000..88d7bff
> --- /dev/null
> +++ b/modules/git/files/check-perms.py
> @@ -0,0 +1,148 @@
> +#!/usr/bin/python -tt
> +"""Check permissions of a tree of git repositories, optionally fixing any
> +problems found.
> +"""
> +
> +import os
> +import re
> +import sys
> +import optparse
> +from stat import *
> +from subprocess import call, PIPE, Popen
> +
> +usage = '%prog [options] [gitroot]'
> +parser = optparse.OptionParser(usage=usage)
> +parser.add_option('-f', '--fix', dest='fix',
> + action='store_true', default=False,
> + help='Correct any problems [%default]')
> +opts, args = parser.parse_args()
> +
> +if args:
> + gitroot = args[0]
> +else:
> + gitroot = '/git'
> +
> +object_re = re.compile('[0-9a-z]{40}')
> +
> +def is_object(path):
> + """Check if a path is a git object."""
> + parts = path.split(os.path.sep)
> + if 'objects' in parts and len(parts) > 2 and \
> + object_re.match(''.join(path.split(os.path.sep)[-2:])):
> + return True
> + return False
> +
> +def is_shared_repo(gitdir):
> + """Check if a git repository is shared."""
> + cmd = ['git', '--git-dir', gitdir, 'config', 'core.sharedRepository']
> + p = Popen(cmd, stdout=PIPE, stderr=PIPE)
> + shared, error = p.communicate()
> + sharedmodes = ['1', 'group', 'true', '2', 'all', 'world', 'everybody']
> + if shared.rstrip() not in sharedmodes or p.returncode:
> + return False
> + return True
> +
> +def set_shared_repo(gitdir, value='group'):
> + """Set core.sharedRepository for a git repository."""
> + mode_re = re.compile('06[0-7]{2}')
> + if value in [0, 'false', 'umask']:
> + value = 'umask'
> + elif value in [1, 'true', 'group']:
> + value = 'group'
> + elif value in [2, 'all', 'world', 'everybody']:
> + value = 'all'
> + elif mode_re.match(value):
> + pass
> + else:
> + raise SystemExit('Bogus core.sharedRepository value "%s"' % value)
> + cmd = ['git', '--git-dir', gitdir, 'config', 'core.sharedRepository',
> + value]
> + ret = call(cmd)
> + if ret:
> + return False
> + return True
> +
> +def check_git_perms(path, fix=False):
> + """Check if permissions on a git repo are correct.
> +
> + If fix is true, problems found are corrected.
> + """
> + object_mode = S_IRUSR | S_IRGRP | S_IROTH
> + oldmode = mode = S_IMODE(os.lstat(path)[ST_MODE])
> + errors = []
> + if os.path.isdir(path):
> + newmode = mode | S_ISGID
> + if mode != newmode:
> + msg = 'Not SETGID (should be "%s")' % oct(newmode)
> + errors.append(msg)
> + mode = newmode
> + elif is_object(path) and mode ^ object_mode:
> + msg = 'Wrong object mode "%s" (should be "%s")' % (
> + oct(mode), oct(object_mode))
> + errors.append(msg)
> + mode = object_mode
> + if mode & S_IWUSR and not is_object(path):
> + newmode = mode | S_IWGRP
> + if mode != newmode:
> + msg = 'Not group writable (should be "%s")' % oct(newmode)
> + errors.append(msg)
> + mode = newmode
> + if mode != oldmode and not os.path.islink(path):
> + print >> sys.stderr, '%s:' % path,
> + print >> sys.stderr, ', '.join(['%s' % e for e in errors])
> + if not fix:
> + return False
> + try:
> + os.chmod(path, mode)
> + return True
> + except Exception, e:
> + error = hasattr(e, 'strerror') and e.strerror or e
> + mode = oct(mode)
> + print >> sys.stderr, 'Error setting "%s" mode on %s: %s' % (
> + mode, path, error)
> + return False
> + return True
> +
> +def main():
> + if not os.path.isdir(gitroot):
> + raise SystemExit('%s does not exist or is not a directory' % gitroot)
> +
> + gitdirs = []
> + for path, dirs, files in os.walk(gitroot):
> + if path in gitdirs:
> + continue
> + if 'description' in os.listdir(path):
> + gitdirs.append(path)
> +
> + problems = []
> + for gitdir in sorted(gitdirs):
> + if not is_shared_repo(gitdir):
> + print >> sys.stderr, '%s: core.sharedRepository not set' % gitdir
> + if not opts.fix or not set_shared_repo(gitdir):
> + problems.append(gitdir)
> + continue
> + paths = []
> + for path, dirs, files in os.walk(gitdir):
> + for d in dirs:
> + d = os.path.join(path, d)
> + if d not in paths:
> + paths.append(d)
> + for f in files:
> + f = os.path.join(path, f)
> + if f not in paths:
> + paths.append(f)
> + for path in paths:
> + if not check_git_perms(path, fix=opts.fix):
> + if path not in problems:
> + problems.append(path)
> +
> + if problems:
> + raise SystemExit('%d paths remain unfixed' % len(problems))
> +
> + raise SystemExit()
> +
> +if __name__ == '__main__':
> + try:
> + main()
> + except KeyboardInterrupt:
> + raise SystemExit('\nExiting on user cancel (Ctrl-C)')
> diff --git a/modules/git/manifests/init.pp b/modules/git/manifests/init.pp
> index 87282b5..ab1abec 100644
> --- a/modules/git/manifests/init.pp
> +++ b/modules/git/manifests/init.pp
> @@ -35,3 +35,34 @@ class git::mail-hooks {
> require => [File["$mailhooks/git.py"], File["$mailhooks/util.py"]];
> }
> }
> +
> +class git::check-perms {
> + include git::package
> +
> + file { '/usr/local/bin/git-check-perms':
> + owner => 'root',
> + group => 'root',
> + mode => 0755,
> + source => 'puppet:///git/check-perms.py',
> + require => Package['git'],
> + }
> +
> + $gitroot = $git_check_perms_gitroot ? {
> + '' => '/git',
> + default => $git_check_perms_gitroot,
> + }
> +
> + $mailto = $git_check_perms_mailto ? {
> + '' => 'root',
> + default => $git_check_perms_mailto,
> + }
> +
> + cron { 'git-check-perms':
> + command => "git check-perms $gitroot",
> + user => 'nobody',
> + hour => 0,
> + minute => 10,
> + environment => ["MAILTO=$mailto", 'PATH=/usr/bin:/usr/local/bin'],
> + require => File['/usr/local/bin/git-check-perms'],
> + }
> +}
> --
> 1.6.4
>
Just to be clear, we've run this several times already. We're just
puppetizing it and adding a cron job?
-Mike
More information about the Fedora-infrastructure-list
mailing list