This commit is contained in:
2023-12-08 12:36:24 +01:00
commit e09af15085
18 changed files with 1617 additions and 0 deletions

25
tasks/get_secrets.yml Normal file
View File

@@ -0,0 +1,25 @@
---
# This file extracts a secret from the config file. If it is not found, a new value is generated.
# In either case, the secret is made available in a variable for future reference.
# Needs:
# * _slurp_appini - base64-encoded contents of app.ini
# * secret - the name of the secret to handle
- name: "extract {{ secret }} from current config file"
set_fact:
_secret_value: "{{ _slurp_appini.content | b64decode | regex_search('(?<=' ~ secret ~ '\\s=\\s`).*(?=`)') }}"
no_log: true
when: "_slurp_appini is not skipped"
- name: "generate new {{ secret }}"
command: "gitea generate secret '{{ secret }}'"
environment:
PATH: "{{ ansible_facts['env']['PATH'] }}:/usr/local/bin"
register: _generate_secret
no_log: true
when: "not _secret_value | default(false)"
- name: "store new {{ secret }}"
set_fact:
'_{{ secret }}': "{{ _generate_secret.stdout | default(_secret_value) }}"
no_log: true

View File

@@ -0,0 +1,144 @@
---
# This file handles installing Gitea using the official binary release.
# This includes
# * installing dependencies using the package manager
# * getting the latest version
# * downloading the binary
# * validating the signature
# * installing the binary to the correct path
# * creating the necessary system user account
# * installing a systemd service file/OpenRC init script
- name: 'install dependencies'
package:
name: "{{ gitea_dependencies }}"
state: present
- block:
- name: 'get available versions of Gitea'
uri:
url: "{{ gitea_base_url }}"
return_content: true
register: _gitea_downloads
- name: 'determine latest version of Gitea'
set_fact:
gitea_version: "{{ _gitea_downloads.content | regex_findall('(?<=/gitea/)[0-9]+\\.[0-9]+\\.[0-9]+') | sort_versions | last }}"
when: "gitea_version is not defined"
- name: 'get currently installed version of Gitea'
command: '/usr/local/bin/gitea --version'
check_mode: false
changed_when: false
ignore_errors: true
register: _gitea_version
- name: 'determine version number'
set_fact:
_current_gitea_version: "{{ _gitea_version.stdout | default('') | regex_search('(?<=Gitea version )[0-9.]+(-rc[0-9]+)?(?= )') }}"
- name: "download and install Gitea {{ gitea_version }}"
block:
- name: 'create temporary directory'
tempfile:
state: directory
diff: false
register: _tmpdir
notify: 'gitea_remove_tmpdir'
- name: 'determine file name'
set_fact:
_filename: "gitea-{{ gitea_version }}-linux-{{ gitea_arch }}"
- name: 'download gitea'
get_url:
url: "{{ gitea_base_url | regex_replace('/*$', '') }}/{{ gitea_version }}/{{ filename }}"
dest: "{{ _tmpdir.path }}"
loop:
- "{{ _filename }}"
- "{{ _filename }}.asc"
loop_control:
loop_var: filename
- name: 'create temporary GnuPG directory'
file:
path: "{{ _tmpdir.path }}/.gnupg"
state: directory
owner: root
group: root
mode: 0700
diff: false
- name: 'get the PGP keys'
command: "gpg --keyserver hkps://keys.openpgp.org --no-default-keyring --keyring trustedkeys.kbx --recv-key {{ gitea_pgp_fingerprint | quote }}"
environment:
GNUPGHOME: "{{ _tmpdir.path }}/.gnupg"
register: '_gpg_recv_key'
changed_when: "_gpg_recv_key is not failed and 'imported:' in _gpg_recv_key.stderr"
- name: 'verify signature'
command: "gpgv '{{ _tmpdir.path }}/{{ _filename }}.asc' '{{ _tmpdir.path }}/{{ _filename }}'"
environment:
GNUPGHOME: "{{ _tmpdir.path }}/.gnupg"
changed_when: false
- name: 'install gitea'
copy:
dest: '/usr/local/bin/gitea'
src: "{{ _tmpdir.path }}/{{ _filename }}"
remote_src: true
owner: root
group: root
mode: 0755
notify: 'restart gitea'
when: "gitea_version != _current_gitea_version"
- name: 'create Gitea system user group'
group:
name: "{{ gitea_group }}"
system: true
state: present
- name: 'create Gitea system user account'
user:
name: "{{ gitea_user }}"
password: '*'
group: "{{ gitea_group }}"
home: "{{ gitea_data_path }}"
comment: 'Gitea service account'
shell: '/bin/sh'
system: true
state: present
- name: 'check for MariaDB (systemd)'
stat:
path: '/lib/systemd/system/mariadb.service'
register: _mariadb_service
when: "gitea_database_type == 'mysql' and ansible_facts['service_mgr'] == 'systemd'"
- name: 'check for MariaDB (non-systemd)'
stat:
path: '/etc/init.d/mariadb'
register: _initd_mariadb
when: "gitea_database_type == 'mysql' and ansible_facts['service_mgr'] != 'systemd'"
- name: 'install systemd service file'
template:
dest: '/etc/systemd/system/gitea.service'
src: 'gitea.service.j2'
owner: root
group: root
mode: 0644
when: "ansible_facts['service_mgr'] == 'systemd'"
- name: 'install OpenRC init script'
template:
dest: '/etc/init.d/gitea'
src: 'gitea.openrc.j2'
owner: root
group: root
mode: 0755
when: "ansible_facts['service_mgr'] == 'openrc'"

266
tasks/main.yml Normal file
View File

@@ -0,0 +1,266 @@
---
- name: 'gather os specific variables'
include_vars: "{{ vars_file }}"
loop:
- 'default.yml'
- "{{ ansible_facts['os_family'] | lower }}.yml"
- "{{ ansible_facts['distribution'] | lower }}.yml"
- "{{ ansible_facts['distribution'] | lower }}-{{ ansible_facts['distribution_major_version'] }}.yml"
- "{{ ansible_facts['distribution'] | lower }}-{{ ansible_facts['distribution_version'] }}.yml"
loop_control:
loop_var: vars_file
when: "(vars_file is abs and vars_file is file) or (vars_file is not abs and (role_path ~ '/vars/' ~ vars_file) is file)"
- name: 'check for bash'
stat:
path: '/bin/bash'
register: _bin_bash
- name: 'ensure configuration consistency'
set_fact:
gitea_use_pkg: "{{ gitea_has_pkg | bool and gitea_use_pkg | bool }}"
- name: 'set user name to distribution package value'
set_fact:
gitea_user: "{{ gitea_pkg_user }}"
gitea_group: "{{ gitea_pkg_group }}"
when: "gitea_use_pkg | bool"
- name: 'install Gitea using the package manager'
package:
name: "{{ gitea_pkg_name }}"
state: present
notify: 'restart gitea'
when: "gitea_use_pkg | bool"
- name: 'install Gitea from binary'
include_tasks: install_gitea_binary.yml
when: "not gitea_use_pkg | bool"
- name: 'install additional dependencies'
package:
name: "{{ pkg.pkg }}"
state: present
when: "pkg.when"
loop:
- pkg: 'git-lfs'
when: "{{ gitea_enable_lfs | bool }}"
- pkg: "{{ gitea_gnupg_package }}"
when: "{{ gitea_enable_signing | bool }}"
loop_control:
loop_var: 'pkg'
label: "{{ pkg.pkg }}"
- name: "add {{ gitea_user }} to extra groups"
user:
name: "{{ gitea_user }}"
groups: "{{ gitea_extra_groups }}"
append: true
notify: 'restart gitea'
when: "[gitea_extra_groups | default([])] | flatten | count"
- name: "harden gitea.service"
block:
- name: "create override directory for gitea.service"
file:
path: '/etc/systemd/system/gitea.service.d/'
state: directory
owner: root
group: root
mode: 0755
- name: "install override file for gitea.service"
template:
dest: '/etc/systemd/system/gitea.service.d/override.conf'
src: 'gitea_override.conf.j2'
owner: root
group: root
mode: 0644
notify:
- 'gitea_reload_service_files'
- 'restart gitea'
when: "ansible_facts['service_mgr'] == 'systemd'"
- name: 'allow non-root users to bind to low ports'
sysctl:
name: 'net.ipv4.ip_unprivileged_port_start'
value: '0'
sysctl_file: '/etc/sysctl.d/unprivileged_ports.conf'
state: present
when: "ansible_facts['service_mgr'] != 'systemd' and gitea_port | int < 1024"
- name: 'check if Gitea is already configured'
stat:
path: '/etc/gitea/app.ini'
register: _stat_appini
- name: 'read current config file'
slurp:
src: '/etc/gitea/app.ini'
register: _slurp_appini
when: "_stat_appini.stat.exists"
- include_tasks: get_secrets.yml
loop:
- 'SECRET_KEY'
- 'INTERNAL_TOKEN'
- 'JWT_SECRET'
- 'LFS_JWT_SECRET'
loop_control:
loop_var: secret
- name: 'combine default and custom options'
set_fact:
_gitea_options: "{{ gitea_default_options | combine(gitea_extra_options, recursive=True) }}"
- name: 'create required directories'
file:
path: "{{ directory.path }}"
state: directory
owner: "{{ directory.owner | default(gitea_user) }}"
group: "{{ directory.group | default(gitea_group) }}"
mode: "{{ directory.mode | default('0750') }}"
loop:
- path: '/etc/gitea'
owner: root
- path: "{{ gitea_data_path }}"
- path: "{{ _gitea_options['git']['HOME_PATH'] }}"
- path: "{{ gitea_custom_path }}"
owner: root
- path: "{{ gitea_log_path }}"
loop_control:
loop_var: directory
label: "{{ directory.path }}"
- name: 'configure Gitea'
template:
dest: '/etc/gitea/app.ini'
src: 'app.ini.j2'
owner: root
group: "{{ gitea_group }}"
mode: 0640
no_log: true
notify: 'restart gitea'
- name: 'unset secrets'
set_fact:
_slurp_appini:
_secret_value:
_generate_secret:
_SECRET_KEY:
_INTERNAL_TOKEN:
_JWT_SECRET:
_LFS_JWT_SECRET:
- name: 'create server-side commit signing key'
command: "su {{ gitea_user }} -c 'gpg --batch --generate-key'"
args:
warn: false # su is needed, otherwise Ansible might require a password to become the gitea user
creates: "{{ _gitea_options['git']['HOME_PATH'] }}/.gnupg/private-keys-v1.d/"
stdin: |
%no-protection
Key-Type: {{ gitea_signing_key_type }}
Key-Length: {{ gitea_signing_key_length }}
Key-Usage: sign
Name-Real: {{ gitea_committer_name }}
Name-Email: {{ gitea_committer_email }}
# Discard the time, use only the date as the creation timestamp
Creation-Date: {{ lookup('pipe', 'date +%Y-%m-%d') }}
when: "gitea_enable_signing | bool"
- name: 'configure git command line client'
ini_file:
path: "{{ _gitea_options['git']['HOME_PATH'] }}/.gitconfig"
section: "{{ item.section }}"
option: "{{ item.option }}"
value: "{{ item.value }}"
state: present
loop:
- section: 'commit'
option: 'gpgsign'
value: "{{ gitea_enable_signing | bool | string | lower }}"
- section: 'user'
option: 'name'
value: "{{ gitea_committer_name }}"
- section: 'user'
option: 'email'
value: "{{ gitea_committer_email }}"
loop_control:
label: "{{ item.section }}.{{ item.option }} = {{ item.value }}"
- name: 'initialise gitea database (this may take a long time)'
command: "su {{ gitea_user }} -c 'PATH=\"{{ ansible_facts['env']['PATH'] }}:/usr/local/bin\" gitea -c /etc/gitea/app.ini migrate'"
args:
chdir: "{{ gitea_data_path }}"
warn: false # su is needed, otherwise Ansible might require a password to become the gitea user
- name: 'create initial local user accounts'
command: "su {{ gitea_user }} -c 'PATH=\"{{ ansible_facts['env']['PATH'] }}:/usr/local/bin\" gitea -c /etc/gitea/app.ini admin user create --username {{ user.name | quote }} --password {{ user.password | quote }} --email {{ user.email | quote }} {{ user.admin | default(false) | bool | ternary('--admin', '') }}'"
args:
chdir: "{{ gitea_data_path }}"
warn: false # su is needed, otherwise Ansible might require a password to become the gitea user
register: _create_user
failed_when: "_create_user.rc > 0 and 'user already exists' not in _create_user.stdout"
changed_when: "'New user ''' ~ user.name ~ ''' has been successfully created' in _create_user.stdout"
no_log: true
loop: "{{ gitea_users }}"
loop_control:
loop_var: user
label: "{{ user.name }}"
- name: 'configure external authentication sources'
gitea_auth:
name: "{{ provider.name }}"
type: "{{ provider.type }}"
host: "{{ provider.host | default(omit) }}"
port: "{{ provider.port | default(omit) }}"
encryption: "{{ provider.encryption | default(omit) }}"
bind_dn: "{{ provider.bind_dn | default(omit) }}"
bind_password: "{{ provider.bind_password | default(omit) }}"
user_search_base: "{{ provider.user_search_base | default(omit) }}"
user_filter: "{{ provider.user_filter | default(omit) }}"
admin_filter: "{{ provider.admin_filter | default(omit) }}"
username_attribute: "{{ provider.username_attribute | default(omit) }}"
email_attribute: "{{ provider.email_attribute | default(omit) }}"
firstname_attribute: "{{ provider.firstname_attribute | default(omit) }}"
surname_attribute: "{{ provider.surname_attribute | default(omit) }}"
sshkey_attribute: "{{ provider.sshkey_attribute | default(omit) }}"
sync_users: "{{ provider.sync_users | default(omit) }}"
provider: "{{ provider.provider | default(omit) }}"
client_id: "{{ provider.client_id | default(omit) }}"
client_secret: "{{ provider.client_secret | default(omit) }}"
auto_discover_url: "{{ provider.auto_discover_url | default(omit) }}"
use_custom_urls: "{{ provider.use_custom_urls | default(omit) }}"
custom_tenant_id: "{{ provider.custom_tenant_id | default(omit) }}"
custom_auth_url: "{{ provider.custom_auth_url | default(omit) }}"
custom_email_url: "{{ provider.custom_email_url | default(omit) }}"
custom_profile_url: "{{ provider.custom_profile_url | default(omit) }}"
custom_token_url: "{{ provider.custom_token_url | default(omit) }}"
state: present
environment:
PATH: "{{ ansible_facts['env']['PATH'] }}:/usr/local/bin"
loop: "{{ gitea_auth_providers }}"
loop_control:
loop_var: provider
label: "{{ provider.name }}"
no_log: "{{ provider.bind_password is defined or provider.client_secret is defined }}"
- name: 'install custom files'
copy:
src: "{{ gitea_custom_files }}/"
dest: "{{ gitea_custom_path }}"
owner: root
group: root
directory_mode: 0755
when: "gitea_custom_files is defined"
notify: 'restart gitea'
# If the unit file changed, reload it now.
- meta: flush_handlers
- name: 'enable and start Gitea'
service:
name: 'gitea'
enabled: true
state: "{{ ansible_facts['is_chroot'] | ternary(omit, 'started') }}"