diff options
author | Stonewall Jackson <stonewall@sacredheartsc.com> | 2023-02-04 01:23:43 -0500 |
---|---|---|
committer | Stonewall Jackson <stonewall@sacredheartsc.com> | 2023-02-04 01:52:13 -0500 |
commit | 0261e875679f1bf63c8d689da7fc7e014597885d (patch) | |
tree | 3f19cd74a0c1070944f75437f30b098d6ef2ffcb /roles/gitolite | |
download | selfhosted-0261e875679f1bf63c8d689da7fc7e014597885d.tar.gz selfhosted-0261e875679f1bf63c8d689da7fc7e014597885d.zip |
initial commit
Diffstat (limited to 'roles/gitolite')
-rw-r--r-- | roles/gitolite/defaults/main.yml | 7 | ||||
-rw-r--r-- | roles/gitolite/handlers/main.yml | 4 | ||||
-rw-r--r-- | roles/gitolite/meta/main.yml | 4 | ||||
-rw-r--r-- | roles/gitolite/tasks/freeipa.yml | 49 | ||||
-rw-r--r-- | roles/gitolite/tasks/main.yml | 119 | ||||
-rw-r--r-- | roles/gitolite/tasks/sshd.yml | 24 | ||||
-rw-r--r-- | roles/gitolite/templates/etc/ssh/sshd_config.d/gitolite.conf.j2 | 4 | ||||
-rw-r--r-- | roles/gitolite/templates/usr/local/bin/gitolite-authorizedkeys.j2 | 37 | ||||
-rw-r--r-- | roles/gitolite/templates/usr/local/bin/gitolite-grouplist.j2 | 42 | ||||
-rw-r--r-- | roles/gitolite/templates/var/www/cgi-bin/gitolite-wrapper.j2 | 14 | ||||
-rw-r--r-- | roles/gitolite/templates/var/www/git/.gitolite.rc.j2 | 28 | ||||
-rw-r--r-- | roles/gitolite/templates/var/www/git/.gitolite/conf/gitolite.conf.j2 | 11 | ||||
-rw-r--r-- | roles/gitolite/vars/main.yml | 40 |
13 files changed, 383 insertions, 0 deletions
diff --git a/roles/gitolite/defaults/main.yml b/roles/gitolite/defaults/main.yml new file mode 100644 index 0000000..3c50916 --- /dev/null +++ b/roles/gitolite/defaults/main.yml @@ -0,0 +1,7 @@ +gitolite_ssh_user: git +gitolite_admin_group: role-git-admin +gitolite_access_group: role-git-access +gitolite_anon_user: nobody +gitolite_freeipa_user: s-gitolite +gitolite_uid: 1993 +gitolite_archive_on_calendar: weekly diff --git a/roles/gitolite/handlers/main.yml b/roles/gitolite/handlers/main.yml new file mode 100644 index 0000000..18c505e --- /dev/null +++ b/roles/gitolite/handlers/main.yml @@ -0,0 +1,4 @@ +- name: restart sshd + systemd: + name: sshd + state: restarted diff --git a/roles/gitolite/meta/main.yml b/roles/gitolite/meta/main.yml new file mode 100644 index 0000000..29230f9 --- /dev/null +++ b/roles/gitolite/meta/main.yml @@ -0,0 +1,4 @@ +dependencies: + - role: yum + yum_repositories: epel + tags: yum diff --git a/roles/gitolite/tasks/freeipa.yml b/roles/gitolite/tasks/freeipa.yml new file mode 100644 index 0000000..f94b9e0 --- /dev/null +++ b/roles/gitolite/tasks/freeipa.yml @@ -0,0 +1,49 @@ +- name: create service account + ipauser: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + name: '{{ gitolite_freeipa_user }}' + loginshell: /sbin/nologin + homedir: '{{ gitolite_home }}' + givenname: Gitolite + sn: Service Account + state: present + run_once: True + +- name: retrieve user keytab + include_role: + name: freeipa_keytab + vars: + keytab_principal: '{{ gitolite_freeipa_user }}' + keytab_path: '{{ gitolite_keytab }}' + +- name: configure gssproxy for kerberized LDAP + include_role: + name: gssproxy_client + vars: + gssproxy_priority: 51 + gssproxy_name: gitolite + gssproxy_section: service/gitolite + gssproxy_client_keytab: '{{ gitolite_keytab }}' + gssproxy_cred_usage: initiate + gssproxy_euid: '{{ gitolite_user }}' + +- name: create admin group + ipagroup: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + name: '{{ gitolite_admin_group }}' + description: gitolite admins + nonposix: yes + state: present + run_once: True + +- name: create access group + ipagroup: + ipaadmin_principal: '{{ ipa_user }}' + ipaadmin_password: '{{ ipa_pass }}' + name: '{{ gitolite_access_group }}' + description: gitolite users + nonposix: yes + state: present + run_once: True diff --git a/roles/gitolite/tasks/main.yml b/roles/gitolite/tasks/main.yml new file mode 100644 index 0000000..8226557 --- /dev/null +++ b/roles/gitolite/tasks/main.yml @@ -0,0 +1,119 @@ +- name: install gitolite + dnf: + name: '{{ gitolite_packages }}' + state: present + +- import_tasks: freeipa.yml + +- name: disable gitolite user + user: + name: gitolite3 + shell: /sbin/nologin + +- name: get apache uid + getent: + database: passwd + key: '{{ gitolite_user }}' + +- name: create git ssh user + user: + name: '{{ gitolite_ssh_user }}' + comment: Git Pseudo-User + uid: '{{ ansible_facts.getent_passwd[gitolite_user][1] }}' + group: '{{ gitolite_user }}' + home: '{{ gitolite_home }}' + create_home: no + non_unique: yes + shell: '{{ gitolite_shell }}' + +- name: create git home + file: + path: '{{ gitolite_home }}' + mode: 0750 + owner: '{{ gitolite_user }}' + group: '{{ gitolite_user }}' + state: directory + setype: _default + +- name: copy gitolite wrapper script + template: + src: '{{ gitolite_cgi_script[1:] }}.j2' + dest: '{{ gitolite_cgi_script }}' + mode: 0555 + setype: httpd_unconfined_script_exec_t + tags: selinux + +- name: set unconfined selinux context on gitolite wrapper + sefcontext: + target: '{{ gitolite_cgi_script }}' + setype: httpd_unconfined_script_exec_t + state: present + tags: selinux + register: gitolite_cgi_sefcontext + +- name: apply selinux context to gitolite wrapper + command: 'restorecon -R {{ gitolite_cgi_script }}' + when: gitolite_cgi_sefcontext.changed + tags: selinux + +- name: generate gitolite scripts + template: + src: '{{ item[1:] }}.j2' + dest: '{{ item }}' + mode: 0555 + loop: + - '{{ gitolite_groups_script }}' + - '{{ gitolite_authorizedkeys_script }}' + +- import_tasks: sshd.yml + +- name: create SELinux policy for gitolite + include_role: + name: selinux_policy + apply: + tags: selinux + vars: + selinux_policy_name: gitolite_sshd_httpd + selinux_policy_te: '{{ gitolite_selinux_policy_te }}' + tags: selinux + +- name: generate gitolite.rc + template: + src: '{{ gitolite_home[1:] }}/.gitolite.rc.j2' + dest: '{{ gitolite_home }}/.gitolite.rc' + owner: '{{ gitolite_user }}' + group: '{{ gitolite_user }}' + mode: 0600 + setype: _default + +- name: create gitolite config directories + file: + path: '{{ gitolite_home }}/{{ item }}' + state: directory + owner: '{{ gitolite_user }}' + group: '{{ gitolite_user }}' + mode: 0750 + setype: _default + loop: + - .gitolite + - .gitolite/conf + - .gitolite/logs + +- name: create initial gitolite.conf + template: + src: '{{ gitolite_home[1:] }}/.gitolite/conf/gitolite.conf.j2' + dest: '{{ gitolite_home }}/.gitolite/conf/gitolite.conf' + owner: '{{ gitolite_user }}' + group: '{{ gitolite_user }}' + mode: 0640 + force: no + +- name: initialize gitolite + command: + cmd: gitolite setup + chdir: '{{ gitolite_home }}' + creates: '{{ gitolite_home }}/.gitolite/conf/gitolite.conf-compiled.pm' + environment: + HOME: '{{ gitolite_home }}' + become: yes + become_user: '{{ gitolite_user }}' diff --git a/roles/gitolite/tasks/sshd.yml b/roles/gitolite/tasks/sshd.yml new file mode 100644 index 0000000..37a74e4 --- /dev/null +++ b/roles/gitolite/tasks/sshd.yml @@ -0,0 +1,24 @@ +# TODO: ssh_config.d is included by default starting with EL9 +- name: create sshd config directory + file: + path: /etc/ssh/sshd_config.d + state: directory + +- name: add sshd include directive + lineinfile: + path: /etc/ssh/sshd_config + line: Include sshd_config.d/* + insertafter: EOF + +- name: generate sshd configuration for gitolite + template: + src: etc/ssh/sshd_config.d/gitolite.conf.j2 + dest: /etc/ssh/sshd_config.d/gitolite.conf + notify: restart sshd + +- name: allow sshd to query ldap + seboolean: + name: authlogin_nsswitch_use_ldap + state: yes + persistent: yes + tags: selinux diff --git a/roles/gitolite/templates/etc/ssh/sshd_config.d/gitolite.conf.j2 b/roles/gitolite/templates/etc/ssh/sshd_config.d/gitolite.conf.j2 new file mode 100644 index 0000000..38da41f --- /dev/null +++ b/roles/gitolite/templates/etc/ssh/sshd_config.d/gitolite.conf.j2 @@ -0,0 +1,4 @@ +Match User {{ gitolite_ssh_user }} + AuthenticationMethods "publickey" + AuthorizedKeysCommand {{ gitolite_authorizedkeys_script }} + AuthorizedKeysCommandUser {{ gitolite_user }} diff --git a/roles/gitolite/templates/usr/local/bin/gitolite-authorizedkeys.j2 b/roles/gitolite/templates/usr/local/bin/gitolite-authorizedkeys.j2 new file mode 100644 index 0000000..23bfee9 --- /dev/null +++ b/roles/gitolite/templates/usr/local/bin/gitolite-authorizedkeys.j2 @@ -0,0 +1,37 @@ +#!/usr/libexec/platform-python + +import os +import ldap +import ldap.sasl +import ldap.filter + +GITOLITE_ACCESS_GROUP = '{{ gitolite_access_group }}' +GITOLITE_ADMIN_GROUP = '{{ gitolite_admin_group }}' +GITOLITE_SHELL = '{{ gitolite_shell }}' + +LDAP_URI = '{{ freeipa_ldap_uri }}' +USER_BASEDN = '{{ freeipa_user_basedn }}' +GROUP_BASEDN = '{{ freeipa_group_basedn }}' + +GITOLITE_KEY_TEMPLATE = 'command="{shell} {uid}",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty {pubkey}' + +os.environ['GSS_USE_PROXY'] = 'yes' +conn = ldap.initialize(LDAP_URI) +conn.protocol_version = ldap.VERSION3 +conn.sasl_interactive_bind_s('', ldap.sasl.sasl({}, 'GSSAPI')) + +filter = ldap.filter.filter_format( + '(&(ipaSshPubKey=*)(|(memberOf=cn=%s,%s)(memberOf=cn=%s,%s)))', + [GITOLITE_ADMIN_GROUP, GROUP_BASEDN, GITOLITE_ACCESS_GROUP, GROUP_BASEDN]) + +results = conn.search_s( + USER_BASEDN, + ldap.SCOPE_SUBTREE, + filter, + ['uid', 'ipaSshPubKey']) + +for (dn, attributes) in results: + uid = attributes['uid'][0].decode('utf-8') + for pubkey in [pk.decode('utf-8') for pk in attributes['ipaSshPubKey']]: + if pubkey.startswith('ssh-'): + print(GITOLITE_KEY_TEMPLATE.format(shell=GITOLITE_SHELL, uid=uid, pubkey=pubkey)) diff --git a/roles/gitolite/templates/usr/local/bin/gitolite-grouplist.j2 b/roles/gitolite/templates/usr/local/bin/gitolite-grouplist.j2 new file mode 100644 index 0000000..2060620 --- /dev/null +++ b/roles/gitolite/templates/usr/local/bin/gitolite-grouplist.j2 @@ -0,0 +1,42 @@ +#!/usr/libexec/platform-python + +import os +import sys +import ldap +import ldap.sasl +import ldap.filter + +LDAP_URI = '{{ freeipa_ldap_uri }}' +USER_BASEDN = '{{ freeipa_user_basedn }}' +GROUP_BASEDN = '{{ freeipa_group_basedn }}' + +if len(sys.argv) != 2: + sys.exit('must specify one username') + +if sys.argv[1] == 'nobody': + exit(0) + +os.environ['GSS_USE_PROXY'] = 'yes' +conn = ldap.initialize(LDAP_URI) +conn.protocol_version = ldap.VERSION3 +conn.sasl_interactive_bind_s('', ldap.sasl.sasl({}, 'GSSAPI')) + +user = conn.search_s( + USER_BASEDN, + ldap.SCOPE_SUBTREE, + ldap.filter.filter_format('uid=%s', [sys.argv[1]]), + ['memberOf']) + +if not user: + exit(1) + +groups = [] + +for group_dn in [ldap.dn.explode_dn(dn) for dn in user[0][1]['memberOf']]: + if ','.join(group_dn[1:]) == GROUP_BASEDN: + rdn = ldap.dn.str2dn(group_dn[0])[0][0] + if rdn[0] == 'cn': + # replace whitespace with underscore + groups.append('_'.join(rdn[1].split())) + +print(' '.join(groups)) diff --git a/roles/gitolite/templates/var/www/cgi-bin/gitolite-wrapper.j2 b/roles/gitolite/templates/var/www/cgi-bin/gitolite-wrapper.j2 new file mode 100644 index 0000000..38dc426 --- /dev/null +++ b/roles/gitolite/templates/var/www/cgi-bin/gitolite-wrapper.j2 @@ -0,0 +1,14 @@ +#!/bin/bash + +# Strip realm from REMOTE_USER. +# This is a hack around GssapiLocalName not working on RHEL 8: +# https://bugzilla.redhat.com/show_bug.cgi?id=1787630 +if [ -v REMOTE_USER ]; then + export REMOTE_USER=${REMOTE_USER%@*} +fi + +export GIT_PROJECT_ROOT='{{ gitolite_home }}/repositories' +export GITOLITE_HTTP_HOME='{{ gitolite_home }}' +export GIT_HTTP_EXPORT_ALL=1 + +exec {{ gitolite_shell }} diff --git a/roles/gitolite/templates/var/www/git/.gitolite.rc.j2 b/roles/gitolite/templates/var/www/git/.gitolite.rc.j2 new file mode 100644 index 0000000..b78ca08 --- /dev/null +++ b/roles/gitolite/templates/var/www/git/.gitolite.rc.j2 @@ -0,0 +1,28 @@ +$ENV{PATH} .= ":{{ gitolite_home }}/bin"; + +%RC = ( + UMASK => 0027, + GIT_CONFIG_KEYS => '.*', + LOG_DEST => 'syslog', + ROLES => { + READERS => 1, + WRITERS => 1, + }, + ENABLE => [ + 'help', + 'desc', + 'info', + 'perms', + 'writable', + 'D', + 'git-config', + 'gitweb', + 'set-default-roles', + 'upstream', + 'cgit', + ], + GROUPLIST_PGM => '{{ gitolite_groups_script }}', + HTTP_ANON_USER => '{{ gitolite_anon_user }}', +); + +1; diff --git a/roles/gitolite/templates/var/www/git/.gitolite/conf/gitolite.conf.j2 b/roles/gitolite/templates/var/www/git/.gitolite/conf/gitolite.conf.j2 new file mode 100644 index 0000000..7fc1d59 --- /dev/null +++ b/roles/gitolite/templates/var/www/git/.gitolite/conf/gitolite.conf.j2 @@ -0,0 +1,11 @@ +repo gitolite-admin + RW+ = @{{ gitolite_admin_group }} + +repo CREATOR/[A-Za-z0-9/_-]+ + C = @{{ gitolite_admin_group }} @{{ gitolite_access_group }} + RW+ = CREATOR + RW = WRITERS + R = READERS + option default.roles-1 = READERS @all + config gitweb.owner = %GL_CREATOR + config gitweb.category = user repositories diff --git a/roles/gitolite/vars/main.yml b/roles/gitolite/vars/main.yml new file mode 100644 index 0000000..4c3058a --- /dev/null +++ b/roles/gitolite/vars/main.yml @@ -0,0 +1,40 @@ +gitolite_packages: + - httpd + - gitolite3 + - perl-Sys-Syslog + +gitolite_user: apache +gitolite_home: /var/www/git + +gitolite_shell: /usr/share/gitolite3/gitolite-shell +gitolite_cgi_script: /var/www/cgi-bin/gitolite-wrapper +gitolite_groups_script: /usr/local/bin/gitolite-grouplist +gitolite_authorizedkeys_script: /usr/local/bin/gitolite-authorizedkeys + +gitolite_keytab: /var/lib/gssproxy/clients/{{ gitolite_freeipa_user }}.keytab + +gitolite_selinux_policy_te: | + require { + type gssproxy_t; + type gssproxy_var_lib_t; + type sshd_t; + type httpd_t; + type httpd_unconfined_script_t; + class key { read view write }; + class sock_file write; + class unix_stream_socket { connectto }; + } + + #============= sshd_t ============== + allow sshd_t gssproxy_t:unix_stream_socket connectto; + allow sshd_t gssproxy_var_lib_t:sock_file write; + + #============= httpd_t ============== + allow httpd_t httpd_unconfined_script_t:key { read view }; + allow httpd_t sshd_t:key { read view write }; + +gitolite_archive_shell: >- + TIMESTAMP=$(date +%Y%m%d%H%M%S); + tar czf "gitolite-${TIMESTAMP}.tar.gz" + --transform "s|^\.|gitolite-${TIMESTAMP}|" + -C "{{ gitolite_home }}" . |