aboutsummaryrefslogtreecommitdiffstats
path: root/roles/gitolite
diff options
context:
space:
mode:
authorStonewall Jackson <stonewall@sacredheartsc.com>2023-02-04 01:23:43 -0500
committerStonewall Jackson <stonewall@sacredheartsc.com>2023-02-04 01:52:13 -0500
commit0261e875679f1bf63c8d689da7fc7e014597885d (patch)
tree3f19cd74a0c1070944f75437f30b098d6ef2ffcb /roles/gitolite
downloadselfhosted-0261e875679f1bf63c8d689da7fc7e014597885d.tar.gz
selfhosted-0261e875679f1bf63c8d689da7fc7e014597885d.zip
initial commit
Diffstat (limited to 'roles/gitolite')
-rw-r--r--roles/gitolite/defaults/main.yml7
-rw-r--r--roles/gitolite/handlers/main.yml4
-rw-r--r--roles/gitolite/meta/main.yml4
-rw-r--r--roles/gitolite/tasks/freeipa.yml49
-rw-r--r--roles/gitolite/tasks/main.yml119
-rw-r--r--roles/gitolite/tasks/sshd.yml24
-rw-r--r--roles/gitolite/templates/etc/ssh/sshd_config.d/gitolite.conf.j24
-rw-r--r--roles/gitolite/templates/usr/local/bin/gitolite-authorizedkeys.j237
-rw-r--r--roles/gitolite/templates/usr/local/bin/gitolite-grouplist.j242
-rw-r--r--roles/gitolite/templates/var/www/cgi-bin/gitolite-wrapper.j214
-rw-r--r--roles/gitolite/templates/var/www/git/.gitolite.rc.j228
-rw-r--r--roles/gitolite/templates/var/www/git/.gitolite/conf/gitolite.conf.j211
-rw-r--r--roles/gitolite/vars/main.yml40
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 }}" .