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

328
README.md Normal file
View File

@ -0,0 +1,328 @@
Gitea
=====
This role sets up and configures a [Gitea](https://gitea.io/) instance.
It supports official binaries from https://gitea.io/ or distribution-provided packages.
Local user accounts can be created on deployment.
It is also possible to configure external authentication sources.
Requirements
------------
Gitea versions older than 1.18.0 are not (fully) supported.
Depending on the exact configuration, they may or may not work.
If TLS encryption (i.e. HTTPS) is desired, the target system needs to have a suitable X.509 certificate.
This roles does not handle deploying certificates.
Gitea needs a database server, unless it is configured to use SQLite.
This role does not handle database configuration.
Gitea's (optional) email system requires a SMTP server or a working `sendmail` program.
This is not set up by this role either.
Role Variables
--------------
* `gitea_use_pkg`
Whether to prefer the distribution's package of Gitea.
Defaults to `true` but is set to `false` if the distribution is not known provide a package.
* `gitea_version`
What version of Gitea to install from <https://gitea.io/>.
If left unset, the latest version (not including release candidates) is chosen.
This setting is ignored when using a distribution package (cf. `gitea_use_pkg`).
* `gitea_bind_address`
The IP address to bind to.
Set to `0.0.0.0` to listen on all IP addresses.
Defaults to `127.0.0.1`.
* `gitea_port`
The TCP port to listen on.
Defaults to `443` if `gitea_tls_cert` is set and `80` if it is not.
* `gitea_tls_cert`
Path to a PEM-encoded X.509 certificate for Gitea to use.
The file needs to exist and be readable by the Gitea user.
Default is unset, which disables TLS support.
* `gitea_tls_cert_key`
Path to the PEM-encoded private key file for the certificate.
The file needs to exist and be readable by the Gitea user.
Default is unset.
* `gitea_user`, `gitea_group`
The system user account and system group to run Gitea as.
`gitea_user` defaults to `git`; `gitea_group` defaults to the user name.
When using a distribution package (cf. `gitea_use_pkg`), these settings are ignored.
* `gitea_data_path`
The path where repositories, user avatars and similar data is stored.
Defaults to `/var/lib/gitea`.
* `gitea_log_path`
The directory where Gitea's log files are stored.
Gitea creates a number of log files for different purposes.
Defaults to `/var/log/gitea`.
* `gitea_loglevel`
The log level Gitea's various loggers.
Valid values are `Trace`, `Debug`, `Info`, `Warn`, `Error`, `Critical`, `Fatal` and `None` in decreasing order of verbosity.
Fine tuning individual loggers is possible using `gitea_extra_options`.
Defaults to `Info`.
* `gitea_custom_path`
The path where custom files can be placed.
These files allow [customizing Gitea](https://docs.gitea.io/en-us/customizing-gitea/).
`gitea_custom_files` can be used to deploy files to this path.
Note: For security reasons, setting this to a directory within `gitea_data_path` is not recommended.
Defaults to `/etc/gitea/custom`.
* `gitea_custom_files`
Path to a directory on the Ansible controller that contains files that should be deployed to `gitea_custom_path`.
Refer to the [Gitea documentation](https://docs.gitea.io/en-us/customizing-gitea/) for details.
Optional.
* `gitea_database_type`
The type of database that Gitea should use to store user information, repository metadata, issues, etc.
Valid values are `mysql`, `postgres`, `mssql` and `sqlite3`.
Note that this role does not set up a database for Gitea.
This should be done by another role, unless using SQLite, which does not need any setup.
Mandatory.
* `gitea_database_host`
The host name (and optionally port) of the database system.
This can also be an absolute path to a UNIX socket, if the database runs on the same system as Gitea.
Mandatory, unless `gitea_database_type` is `sqlite3`.
* `gitea_database_name`
The name of the database to use.
When using SQLite, this is the path to the database file.
If not set, Gitea's internal default value is used.
* `gitea_database_user`
User account to use when connecting to the database.
Mandatory, unless `gitea_database_type` is `sqlite3`.
* `gitea_database_password`
The password for `gitea_database_user`.
Omit, if the database does not require a password.
* `gitea_enable_mailer`
Whether to enable the mailer (for password resets, etc.) and email notifications.
Requires are working SMTP server, somewhere.
Setting up an SMTP server is outside the scope of this role.
Defaults to `false` unless `gitea_mailer_host` is set.
* `gitea_mailer_host`
The host name (and optionally port) of a SMTP server to use for sending email.
If the mailer is enabled without setting this option, the system's `sendmail` command is used.
Optional.
* `gitea_mailer_from`
The sender address for mail generated by Gitea in RFC 5322 format.
Mandatory if the mailer is enabled.
* `gitea_mailer_user`
User account to use when connecting to the SMTP server.
Optional.
* `gitea_mailer_password`
The password of `gitea_mailer_user`.
Optional.
* `gitea_enable_indexer`
Whether to enable the repository indexer.
The indexer provides code search, but is known to use a fairly large amount of disk space.
Defaults to `true`.
* `gitea_enable_lfs`
Whether to enable git-lfs support for storing large file more efficiently.
Defaults to `false`.
* `gitea_enable_signing`
Whether to enable automatic singing of commits that are created via the web interface (e.g. on merges or repository initialisation).
For this purpose a PGP key is generated and stored on the remote system.
Note that a key is generated only once, and not regenerated, e.g. when the configured identity or other key parameters are changed.
The exact conditions on when a signature is made can be fine tuned using `gitea_extra_options`.
Defaults to `true`.
* `gitea_signing_key_type`
The type of PGP key to generate.
Valid values depend on the capabilities of the `gpg` program on the remote system.
Defaults to `RSA`.
* `gitea_signing_key_length`
Length of the PGP key in bits.
Defaults to `4096`.
* `gitea_committer_name`
If `gitea_enable_signing` is `true`, this is the name in the signing PGP key.
The value may be used elsewhere as well.
Defaults to `Gitea Bot`.
* `gitea_committer_email`
If `gitea_enable_signing` is `true`, this is the email address in the signing PGP key.
The value may be used elsewhere as well.
The email address does not need to exist.
Defaults to `invalid`.
Another generically useful value might be `{{ gitea_user }}@{{ ansible_facts['hostname'] }}`.
* `gitea_users`
A list of local user account to set up within Gitea.
Note that this only allows creating users, but not modifying existing users.
Each list item is in turn a dictionary with the following keys:
* `name`
The user's name.
Needs to be unique within the Gitea installation.
Mandatory.
* `email`
The user's email address.
Needs to be unique within the Gitea installation.
Mandatory.
* `password`
The user's password.
Mandatory.
* `admin`
Whether to assign administrative privileges to the user.
Defaults to `false`.
Note: `gitea_users` only works on Gitea 1.14.0 or newer.
* `gitea_auth_providers`
A list of [external authentication](https://docs.gitea.io/en-us/authentication/) sources to set up within Gitea.
Each list item is in turn a dictionary with the following keys:
* `name`
The name of the authentication source.
Needs to be unique within the Gitea installation.
Mandatory.
* `type`
The type of external authentication source to configure.
Valid values are `oauth`, `ldap` and `ldap-simple`.
Mandatory.
If `type` is `oauth`, the following keys are used:
* `provider`
The name of the OAuth2 provider.
Valid values are the names of providers supported by Gitea, such as `github`, `gitlab` or `twitter`.
Mandatory.
* `client_id`
The client ID for use with the OAuth2 provider.
Mandatory.
* `client_secret`
The client secret for use with the OAuth2 provider.
Mandatory.
* `auto_discover_url`
The OpenID auto discovery URL.
Optional.
* `use_custom_urls`
Whether to use custom URLs if `provider` is `github`, `gitlab` or `gitea`.
Defaults to `false`.
* `custom_tenant_id`
A custom Tenant ID for OAuth2 endpoint (cf. `use_custom_urls`).
Optional.
Only works on Gitea 1.19.0 an newer.
* `custom_auth_url`
A custom Authorization URL (cf. `use_custom_urls`).
Optional.
* `custom_email_url`
A custom Email URL (cf. `use_custom_urls`).
Optional.
* `custom_profile_url`
A custom Profile URL (cf. `use_custom_urls`).
Optional.
* `custom_token_url`
A custom Token URL (cf. `use_custom_urls`).
Optional.
If `type` is `ldap` or `ldap-simple`, the following keys are used:
* `host`
The host name of the LDAP server to connect to.
Mandatory.
* `port`
The TCP port the LDAP service runs on.
Defaults to `389` or, if `encryption` is `ldaps`, to `636`.
* `encryption`
How connections to the LDAP server should be encrypted.
Valid values are `disable`, `starttls` and `ldaps`.
Mandatory.
* `bind_dn`
If `type` is `ldap`: The DN to bind to the LDAP server with when searching for the user. Omit to perform an anonymous search.
If `type` is `ldap-simple`: A template to use as the user's DN. `%s` is substituted with the login name given on sign-in form.
Mandatory if `type` is `ldap-simple`.
* `bind_password`
The password for the user in `bind_dn`.
Only used when `type` is `ldap`.
* `user_search_base`
The LDAP base at which user accounts will be searched for.
Mandatory if `type` is `ldap`.
* `user_filter`
An LDAP filter declaring when a user should be allowed to log in.
`%s` is substituted with login name given on sign-in form.
Mandatory.
* `admin_filter`
An LDAP filter specifying if a user should be given administrator privileges.
If a user account passes the filter, the user will be privileged as an administrator.
Optional.
* `username_attribute`
The attribute of the user's LDAP record containing the user name.
The attribute value will be used for new Gitea accounts' user name after the first successful sign-in.
Leave empty to use the login name given on sign-in form.
This is useful when the supplied login name is matched against multiple attributes, but only a single specific attribute should be used for the Gitea account name.
Optional.
* `email_attribute`
The attribute of the user's LDAP record containing the user's email address.
Defaults to `mail`.
* `firstname_attribute`
The attribute of the user's LDAP record containing the user's first name.
Optional.
* `surname_attribute`
The attribute of the user's LDAP record containing the user's surname.
Optional.
* `sshkey_attribute`
The attribute of the user's LDAP record containing the user's public SSH key.
Optional.
* `sync_users`
This option enables a periodic task that synchronizes the Gitea users with the LDAP server.
Defaults to `false`.
Note: `gitea_auth_providers` only works on Gitea 1.12.0 or newer.
* `gitea_extra_options`
Additional configuration options for Gitea.
This variable is a dictionary where the keys are section names in `app.ini`.
The values are in turn dictionaries where keys are Gitea configuration options for the appropriate section and values are the corresponding configuration values.
Refer to the [Gitea documentation](https://docs.gitea.io/en-us/config-cheat-sheet/) for options and their meaning.
Optional.
* `gitea_extra_groups`
A list of groups that the Gitea system user is added to.
This allows granting access to additional resources, such as the private key file.
All groups need to exist on the target system; this role does not create them.
Empty by default.
* `gitea_inaccessible_paths`
If the target system uses systemd, this option takes a list of paths, that should not be accessible at all for Gitea.
Regardless of this option, home directories are made inaccessible.
Optional.
Dependencies
------------
This role does not set up TLS certificates and therefore depends on a role that generates and deploys them, if TLS support is desired.
It also depends on a role to set up a MySQL/MariaDB, PostgreSQL or Microsoft SQL Server, respectively (possibly on a different system), if using a "full" DBMS is desired.
Alternatively, SQLite can be used, which does not require any further setup.
Example Configuration
---------------------
The following is a short example for some of the configuration options this role provides:
```yaml
gitea_bind_address: '0.0.0.0'
gitea_database_type: 'mysql'
gitea_database_host: '/run/mysqld/mysqld.sock'
gitea_database_name: 'gitea'
gitea_database_user: 'git'
gitea_inaccessible_paths:
- '/var/lib/mysql'
gitea_enable_mailer: true
gitea_mailer_from: "{{ gitea_user }}@{{ ansible_facts['fqdn'] }}"
gitea_extra_options:
server:
LANDING_PAGE: 'explore'
service:
DISABLE_REGISTRATION: true
gitea_users:
- name: 'admin user'
email: 'admin@my.domain'
password: 'admin_password'
admin: true
- name: 'ordinary user'
email: 'user@my.domain'
password: 'user_password'
gitea_auth_providers:
- name: 'OpenLDAP'
type: 'ldap'
host: 'localhost'
encryption: 'disable'
bind_dn: 'cn=gitea,ou=machines,dc=my,dc=domain'
bind_password: 'some_password'
user_search_base: 'ou=people,dc=my,dc=domain'
user_filter: '(&(objectClass=posixAccount)(uid=%s))'
admin_filter: '(memberOf=cn=Gitea Admins,ou=groups,dc=my,dc=domain)'
username_attribute: 'uid'
email_attribute: 'mail'
sshkey_attribute: 'sshPublicKey'
sync_users: true
```
License
-------
MIT

23
defaults/main.yml Normal file
View File

@ -0,0 +1,23 @@
---
gitea_use_pkg: true
gitea_user: 'git'
gitea_group: "{{ gitea_user }}"
gitea_extra_groups: []
gitea_data_path: '/var/lib/gitea'
gitea_log_path: '/var/log/gitea'
gitea_custom_path: "/etc/gitea/custom"
gitea_inaccessible_paths: []
gitea_bind_address: '127.0.0.1'
gitea_port: "{{ (gitea_tls_cert is defined) | ternary(443, 80) }}"
gitea_loglevel: 'Info'
gitea_enable_mailer: "{{ gitea_mailer_host is defined }}"
gitea_enable_lfs: false
gitea_enable_indexer: true
gitea_enable_signing: true
gitea_committer_name: 'Gitea Bot'
gitea_committer_email: 'invalid'
gitea_signing_key_type: 'RSA'
gitea_signing_key_length: 4096
gitea_extra_options: {}
gitea_users: []
gitea_auth_providers: []

15
filter_plugins/filters.py Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env python
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
class FilterModule(object):
def filters(self):
return {
'sort_versions': self.sort_versions,
}
def sort_versions(self, value):
"""This is a somewhat naive approach to version sorting. It only
supports versions that consist of numbers separated by dots."""
return sorted(value, key=lambda s: list(map(int, s.split('.'))))

19
handlers/main.yml Normal file
View File

@ -0,0 +1,19 @@
---
- name: 'reload service files'
systemd:
daemon_reload: true
listen: gitea_reload_service_files
when: "ansible_facts['service_mgr'] == 'systemd'"
- name: 'remove temporary directory'
file:
path: "{{ _tmpdir.path }}"
state: absent
diff: false
listen: 'gitea_remove_tmpdir'
- name: 'restart gitea'
service:
name: 'gitea'
enabled: true
state: "{{ ansible_facts['is_chroot'] | ternary(omit, 'restarted') }}"

547
library/gitea_auth.py Executable file
View File

@ -0,0 +1,547 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2020, Sebastian Hamann
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
---
module: gitea_auth
short_description: Manage external authentication sources in Gitea
version_added: none
description:
- "The `gitea_auth` module allows adding, updating and removing external
authentication sources in an instance of Gitea."
requirements:
- Gitea >= 1.12.0
notes:
- Many options are required when adding new authentication sources. If the authentication source named as in I(name) already exists, the required options can be omitted.
- If I(state) is C(present), this module always reports a changed result, since Gitea does not currently provide full information about configured authentication sources.
options:
admin_filter:
description:
- An LDAP filter specifying if a user should be given administrator privileges. If a user account passes the filter, the user will be privileged as an administrator.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
type: str
required: False
auto_discover_url:
description:
- OpenID Connect auto discovery URL
- Only used if I(type) is C(oauth) and I(state) is C(present).
type: str
required: False
bind_dn:
description:
- If I(type) is C(ldap): The DN to bind to the LDAP server with when searching for the user. Omit to perform an anonymous search.
- If I(type) is C(ldap-simple): A template to use as the user's DN. The %s matching parameter will be substituted with the login name given on sign-in form.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
- Required if I(type) is C(ldap-simple) and I(state) is C(present).
type: str
required: False
bind_password:
description:
- The password for the Bind DN specified above, if any.
- Note: The password is stored in plaintext on the server. As such, ensure that the Bind DN has as few privileges as possible.
- Only used if I(type) is C(ldap) and I(state) is C(present).
type: str
required: False
client_id:
description:
- OAuth2 Client ID
- Only used if I(type) is C(oauth) and I(state) is C(present).
- Required in this case.
type: str
required: False
client_secret:
description:
- OAuth2 Client secret
- Only used if I(type) is C(oauth) and I(state) is C(present).
- Required in this case.
type: str
required: False
config:
description:
- Path to the Gitea config file (C(app.ini)).
- The config file must contain the C(RUN_USER) setting.
type: str
required: False
default: /etc/gitea/app.ini
custom_tenant_id:
description:
- Use custom Tenant ID for OAuth endpoints
- Only used if I(type) is C(oauth) and I(state) is C(present).
type: str
required: False
custom_auth_url:
description:
- Use a custom Authorization URL (option for GitLab/GitHub).
- Only used if I(type) is C(oauth) and I(state) is C(present).
type: str
required: False
custom_email_url:
description:
- Use a custom Email URL (option for GitHub).
- Only used if I(type) is C(oauth) and I(state) is C(present).
type: str
required: False
custom_profile_url:
description:
- Use a custom Profile URL (option for GitLab/GitHub).
- Only used if I(type) is C(oauth) and I(state) is C(present).
type: str
required: False
custom_token_url:
description:
- Use a custom Token URL (option for GitLab/GitHub).
- Only used if I(type) is C(oauth) and I(state) is C(present).
type: str
required: False
email_attribute:
description:
- The attribute of the user's LDAP record containing the user's email address.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
type: str
required: False
encryption:
description:
- Whether and how to use TLS when connecting to the LDAP server.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
- Required in this case.
type: str
required: False
choices: ['disable', 'starttls', 'ldaps']
firstname_attribute:
description:
- The attribute of the user's LDAP record containing the user's first name.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
type: str
required: False
host:
description:
- The host name of the LDAP server.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
- Required in this case.
type: str
required: False
name:
description:
- The name of the external authentication source.
- The name needs to be unique in the Gitea installation.
type: str
required: True
port:
description:
- The port to use when connecting to the server.
- Default is 636 if I(encryption) is C(ldaps) and otherwise 389.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
type: int
required: False
provider:
description:
- The name of an OAuth2 provider supported by Gitea. Valid names include "github", "gitlab" or "twitter", for instance.
- Only used if I(type) is C(oauth) and I(state) is C(present).
- Required in this case.
type: str
required: False
sshkey_attribute:
description:
- The attribute of the user's LDAP record containing the user's public SSH key.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
type: str
required: False
state:
description:
- Whether the authentication source should exist or not, taking action if the state is different from what is stated.
type: str
required: False
default: 'present'
choices: ['present', 'absent']
surname_attribute:
description:
- The attribute of the user's LDAP record containing the user's surname.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
type: str
required: False
sync_users:
description:
- This option enables a periodic task that synchronizes the Gitea users with the LDAP server.
- Only used if I(type) is C(ldap) and I(state) is C(present).
type: bool
required: False
default: False
type:
description:
- The type of external authentication provider to set up.
- Only used if I(state) is C(present).
type: str
required: False
choices: ['oauth', 'ldap', 'ldap-simple']
use_custom_urls:
description:
- Whether to use custom URLs for GitLab/GitHub OAuth endpoints.
- Only used if I(type) is C(oauth) and I(state) is C(present).
type: bool
required: False
user_filter:
description:
- An LDAP filter declaring when a user should be allowed to log in. The %s matching parameter will be substituted with login name given on sign-in form.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
- Required in this case.
type: str
required: False
user_search_base:
description:
- The LDAP base at which user accounts will be searched for.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
- Required if I(type) is C(ldap) and I(state) is C(present).
type: str
required: False
username_attribute:
description:
- The attribute of the user's LDAP record containing the user name. The attribute value will be used for new Gitea accounts' user name after the first successful sign-in. Leave empty to use the login name given on sign-in form.
- This is useful when the supplied login name is matched against multiple attributes, but only a single specific attribute should be used for the Gitea account name.
- Only used if I(type) is C(ldap) or C(ldap-simple) and I(state) is C(present).
type: str
required: False
author:
- Sebastian Hamann (@s-hamann)
'''
EXAMPLES = '''
# Create an OAuth2 authentication source
- name: Enable login with GitHub
gitea_auth:
name: GitHub
type: oauth
provider: github
client_id: gitea
client_secret: some_token
# Create an LDAP authentication source
- name: Enable LDAP login
gitea_auth:
name: OpenLDAP
type: ldap
host: ldap.my.domain
encryption: starttls
bind_dn: uid=gitea,ou=machines,dc=my,dc=domain
bind_password: some_password
user_search_base: ou=people,dc=my,dc=domain
user_filter: '(&(objectClass=posixAccount)(uid=%s)(memberOf=cn=Gitea Users,ou=groups,dc=my,dc=domain))'
admin_filter: '(memberOf=cn=Gitea Admins,ou=groups,dc=my,dc=domain)'
username_attribute: uid
firstname_attribute: givenName
surname_attribute: sn
email_attribute: mail
sshkey_attribute: sshPublicKey
sync_users: true
# Create an LDAP authentication source
- name: Enable Active Directory login
gitea_auth:
name: Active Directory
type: ldap
host: dc.my.domain
encryption: ldaps
bind_dn: uid=gitea,ou=machines,dc=my,dc=domain
bind_password: some_password
user_search_base: ou=people,dc=my,dc=domain
user_filter: '(&(objectCategory=Person)(memberOf=cn=Gitea Users,ou=groups,dc=my,dc=domain)(sAMAccountName=%s)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))'
admin_filter: '(memberOf=cn=Gitea Admins,ou=groups,dc=my,dc=domain)'
username_attribute: sAMAccountName
firstname_attribute: givenName
surname_attribute: sn
email_attribute: mail
sync_users: true
# Delete an authentication source
- name: Remove login with GitHub
gitea_auth:
name: GitHub
state: absent
'''
RETURN = '''
'''
import os
import pwd
import subprocess
from ansible.module_utils.basic import AnsibleModule
from collections import namedtuple
AuthSrc = namedtuple('AuthSrc', ['id', 'name', 'type', 'enabled'])
CommandResult = namedtuple('CommandResult', ['stdout', 'stderr', 'returncode'])
def gitea_cmd(command, app_ini_path):
"""Run the given Gitea auth command and return the output.
:command: The auth command to run, as a list (e.g. ['delete', '--id', '1'])
:app_ini_path: The absolute path to the configuration file (app.ini)
:returns: The output and return code of the given command as a named tuple
(stdout, stderr, returncode)
"""
def become_gitea(uid, gid):
"""Return a function that changes the uid and gid to the given
user and group."""
def result():
os.setgroups([gid])
os.setgid(gid)
os.setuid(uid)
return result
import configparser
app_ini = configparser.ConfigParser()
app_ini.read(app_ini_path)
user = app_ini['DEFAULT']['RUN_USER']
userinfo = pwd.getpwnam(user)
uid = userinfo.pw_uid
gid = userinfo.pw_gid
home = userinfo.pw_dir
env = os.environ.copy()
env['HOME'] = home
cmd = subprocess.Popen(['gitea', '--config', app_ini_path, 'admin', 'auth'] + command,
preexec_fn=become_gitea(uid, gid), cwd=home, env=env,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdout, stderr) = cmd.communicate()
return CommandResult(stdout, stderr, cmd.returncode)
def run_module():
# define available arguments/parameters a user can pass to the module
module_args = dict(
admin_filter=dict(type='str'),
auto_discover_url=dict(type='str'),
bind_dn=dict(type='str'),
bind_password=dict(type='str', no_log=True),
client_id=dict(type='str'),
client_secret=dict(type='str', no_log=True),
config=dict(type='str', default='/etc/gitea/app.ini'),
custom_tenant_id=dict(type='str'),
custom_auth_url=dict(type='str'),
custom_email_url=dict(type='str'),
custom_profile_url=dict(type='str'),
custom_token_url=dict(type='str'),
email_attribute=dict(type='str'),
encryption=dict(type='str', choices=['disable', 'starttls', 'ldaps']),
firstname_attribute=dict(type='str'),
host=dict(type='str'),
name=dict(type='str', required=True),
port=dict(type='int'),
provider=dict(type='str'),
sshkey_attribute=dict(type='str'),
state=dict(type='str', default='present', choices=['present', 'absent']),
surname_attribute=dict(type='str'),
sync_users=dict(type='bool', default=False),
type=dict(type='str', choices=['oauth', 'ldap', 'ldap-simple']),
use_custom_urls=dict(type='bool'),
user_filter=dict(type='str'),
user_search_base=dict(type='str'),
username_attribute=dict(type='str')
)
# seed the result dict in the object
result = dict(
changed=False,
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)
auth_providers = []
# Get the currently configured authentication sources.
header_pos = {}
for line in gitea_cmd(['list'], module.params['config'])[0].splitlines():
# Gitea may print random cruft before the actual information, i.e. the
# header may not be in the first line. Search it.
line = line.decode().split()
if not header_pos:
if line[0] == 'ID':
header_pos['id'] = line.index('ID')
header_pos['name'] = line.index('Name')
header_pos['type'] = line.index('Type')
header_pos['enabled'] = line.index('Enabled')
continue
else:
a = AuthSrc(id=int(line[header_pos['id']]),
name=line[header_pos['name']],
type=line[header_pos['type']],
enabled=line[header_pos['enabled']].lower() == 'true'
)
auth_providers.append(a)
# Set `id` to the ID of the authentication source with the given name, if any.
for p in auth_providers:
if p.name == module.params['name']:
id = p.id
break
else:
id = None
# Sanity checks on the parameters.
if module.params['state'] == 'present' and not module.params['type']:
module.fail_json(rc=256, msg='type is required with state=present')
if module.params['state'] == 'present' and id is None:
if module.params['type'] == 'oauth':
required_params = ['provider', 'client_id', 'client_secret']
elif module.params['type'] == 'ldap':
required_params = ['host', 'encryption', 'user_filter', 'user_search_base']
elif module.params['type'] == 'ldap-simple':
required_params = ['host', 'encryption', 'user_filter', 'bind_dn']
missing_params = []
for p in required_params:
if module.params[p] is None:
missing_params.append(p)
if missing_params:
msg = ('The following parameters are required: {lst}'.
format(lst=', '.join(missing_params)))
module.fail_json(rc=256, msg=msg)
if module.params['state'] == 'absent' and id is not None:
# Delete an authentication source.
if not module.check_mode:
retval = gitea_cmd(['delete', '--id', str(id)], module.params['config'])
if retval.returncode > 0:
msg = ('Could not delete authentication source {name}'.
format(name=module.params['name']))
module.fail_json(msg=msg, stdout=retval.stdout, rc=retval.returncode, **result)
result['changed'] = True
elif module.params['state'] == 'present':
# Add/update an authentication source.
if module.params['type'] == 'oauth':
if id is None:
cmd = ['add-oauth']
else:
cmd = ['update-oauth', '--id', str(id)]
cmd += ['--name', module.params['name']]
if module.params['provider']:
cmd += ['--provider', module.params['provider']]
if module.params['client_id']:
cmd += ['--key', module.params['client_id']]
if module.params['client_secret']:
cmd += ['--secret', module.params['client_secret']]
if module.params['auto_discover_url']:
cmd += ['--auto-discover-url', module.params['auto_discover_url']]
if module.params['use_custom_urls']:
cmd += ['--use-custom-urls', str(module.params['use_custom_urls'])]
if module.params['custom_tenant_id']:
cmd += ['--custom-tenant-id', module.params['custom_tenant_id']]
if module.params['custom_auth_url']:
cmd += ['--custom-auth-url', module.params['custom_auth_url']]
if module.params['custom_token_url']:
cmd += ['--custom-token-url', module.params['custom_token_url']]
if module.params['custom_profile_url']:
cmd += ['--custom-profile-url', module.params['custom_profile_url']]
if module.params['custom_email_url']:
cmd += ['--custom-email-url', module.params['custom_email_url']]
elif module.params['type'] == 'ldap' or module.params['type'] == 'ldap-simple':
if module.params['type'] == 'ldap':
if id is None:
cmd = ['add-ldap']
else:
cmd = ['update-ldap', '--id', str(id)]
if module.params['bind_dn']:
cmd += ['--bind-dn', module.params['bind_dn']]
cmd += ['--attributes-in-bind']
if module.params['bind_password']:
cmd += ['--bind-password', module.params['bind_password']]
if module.params['sync_users']:
cmd += ['--synchronize-users']
elif module.params['type'] == 'ldap-simple':
if id is None:
cmd = ['add-ldap-simple']
else:
cmd = ['update-ldap-simple', '--id', str(id)]
if module.params['bind_dn']:
cmd += ['--user-dn', module.params['bind_dn']]
cmd += ['--name', module.params['name']]
if module.params['host']:
cmd += ['--host', module.params['host']]
if module.params['port']:
cmd += ['--port', module.params['port']]
elif id is None:
if module.params['encryption'] == 'ldaps':
port = '636'
else:
port = '389'
cmd += ['--port', port]
if module.params['encryption']:
if module.params['encryption'] == 'disable':
encryption = 'unencrypted'
elif module.params['encryption'] == 'starttls':
encryption = 'StartTLS'
elif module.params['encryption'] == 'ldaps':
encryption = 'LDAPS'
cmd += ['--security-protocol', encryption]
if module.params['user_search_base']:
cmd += ['--user-search-base', module.params['user_search_base']]
if module.params['user_filter']:
cmd += ['--user-filter', module.params['user_filter']]
if module.params['admin_filter']:
cmd += ['--admin-filter', module.params['admin_filter']]
if module.params['username_attribute']:
cmd += ['--username-attribute', module.params['username_attribute']]
if module.params['firstname_attribute']:
cmd += ['--firstname-attribute', module.params['firstname_attribute']]
if module.params['surname_attribute']:
cmd += ['--surname-attribute', module.params['surname_attribute']]
if module.params['email_attribute'] or id is None:
email_attribute = module.params['email_attribute']
if not email_attribute:
email_attribute = 'mail'
cmd += ['--email-attribute', email_attribute]
if module.params['sshkey_attribute']:
cmd += ['--public-ssh-key-attribute', module.params['sshkey_attribute']]
if not module.check_mode:
retval = gitea_cmd(cmd, module.params['config'])
if retval.returncode > 0:
if id is None:
verb = 'add'
else:
verb = 'update'
msg = ('Could not {verb} authentication source {name}'.
format(verb=verb, name=module.params['name']))
module.fail_json(msg=msg, stdout=retval.stdout, rc=retval.returncode, **result)
# We can not know if anything was changed, since we can not get the
# full configuration of an authentication source out of Gitea.
result['changed'] = True
module.exit_json(**result)
def main():
run_module()
if __name__ == '__main__':
main()

28
meta/main.yml Normal file
View File

@ -0,0 +1,28 @@
galaxy_info:
author: Sebastian Hamann
description: Install and configure Gitea
license: MIT
min_ansible_version: 2.8
platforms:
- name: Gentoo
versions:
- any
- name: Debian
versions:
- buster
- name: Alpine
versions:
- any
- name: GenericLinux
versions:
- any
galaxy_tags:
- development
- git
- gitea
dependencies: []

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') }}"

10
templates/app.ini.j2 Normal file
View File

@ -0,0 +1,10 @@
{# vim: filetype=ini.jinja2 #}
{{ ansible_managed | comment }}
{% for key, options in _gitea_options.items() %}
[{{ key }}]
{% for k, v in options.items() if v != omit %}
{{ k }} = {{ v }}
{% endfor %}
{% endfor %}

45
templates/gitea.openrc.j2 Normal file
View File

@ -0,0 +1,45 @@
{# vim: filetype=gentoo-init-d.jinja2 #}
#!/sbin/openrc-run
name=gitea
description="Gitea, a self-hosted Git service"
: ${GITEA_CONF:=/etc/gitea/app.ini}
: ${GITEA_USER:={{ gitea_user }}}
: ${GITEA_GROUP:={{ gitea_group }}}
: ${GITEA_WORK_DIR:={{ gitea_data_path }}}
: ${GITEA_CUSTOM:={{ gitea_custom_path }}}
: ${GITEA_LOG_FILE:={{ gitea_log_path }}/http.log}
supervisor=supervise-daemon
command="/usr/local/bin/gitea"
command_args="--config '${GITEA_CONF}' web"
command_background="true"
command_user="${GITEA_USER}:${GITEA_GROUP}"
error_log="/var/log/${RC_SVCNAME}/${RC_SVCNAME}.err"
pidfile="/run/${RC_SVCNAME}.pid"
required_files="${GITEA_CONF}"
supervise_daemon_args="--env GITEA_WORK_DIR='${GITEA_WORK_DIR}' \
--chdir '${GITEA_WORK_DIR}' \
--stdout '${GITEA_LOG_FILE}' --stderr '${GITEA_LOG_FILE}'"
depend() {
use logger dns
need net
{%- if gitea_database_host.startswith('127.') or gitea_database_host.startswith('localhost') or gitea_database_host.startswith('/') %}
{% if gitea_database_type == 'postgres' %}
postgresql
{% elif gitea_database_type == 'mysql' %}
{% if _initd_mariadb.stat.exists %}
mariadb
{% else %}
mysql
{% endif %}
{% else %}
{% endif %}
{% else %}
{% endif %}
after firewall
}

View File

@ -0,0 +1,32 @@
{# vim: filetype=systemd.jinja2 #}
[Unit]
Description=Gitea (Git with a cup of tea)
After=network.target
Requires=network.target
{% if gitea_database_host.startswith('127.') or gitea_database_host.startswith('localhost') or gitea_database_host.startswith('/') %}
{% if gitea_database_type == 'postgres' %}
Requires=postgresql.service
{% elif gitea_database_type == 'mysql' %}
{% if _mariadb_service.stat.exists %}
Requires=mariadb.service
{% else %}
Requires=mysql.service
{% endif %}
{% endif %}
{% endif %}
[Service]
Type=simple
Restart=always
RestartSec=2s
User={{ gitea_user }}
Group={{ gitea_group }}
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
WorkingDirectory={{ gitea_data_path | quote }}
RuntimeDirectory=gitea
Environment="GITEA_WORK_DIR={{ gitea_data_path }}" "GITEA_CUSTOM={{ gitea_custom_path }}"
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,38 @@
{# vim: filetype=systemd.jinja2 #}
[Service]
# Apply principle of least privilege
NoNewPrivileges=true
CapabilityBoundingSet=
{% if gitea_port | int < 1024 %}
# Allow binding to low ports
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
{% endif %}
ProtectSystem=strict
ProtectHome=true
ReadWritePaths={{ [gitea_data_path, gitea_log_path] | map('quote') | join(' ') }}
{% if gitea_inaccessible_paths | count %}
InaccessiblePaths={{ [gitea_inaccessible_paths] | flatten | unique | map('quote') | join(' ') }}
{% endif %}
PrivateTmp=true
PrivateDevices=true
{% if [gitea_extra_groups | default([])] | flatten | count == 0 %}
PrivateUsers=true
{% endif %}
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectProc=invisible
ProtectClock=true
ProtectHostname=true
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
SystemCallFilter=@system-service @basic-io @io-event @network-io madvise
SystemCallFilter=~@aio @chown @keyring @memlock @privileged @resources @setuid
SystemCallArchitectures=native
RemoveIPC=true

8
vars/alpine.yml Normal file
View File

@ -0,0 +1,8 @@
---
gitea_has_pkg: true
gitea_pkg_name:
- 'gitea'
- 'gitea-openrc'
gitea_pkg_user: 'gitea'
gitea_pkg_group: 'www-data'
gitea_gnupg_package: 'gnupg'

3
vars/archlinux.yml Normal file
View File

@ -0,0 +1,3 @@
---
gitea_has_pkg: true
gitea_gnupg_package: 'gnupg'

9
vars/default.yml Normal file
View File

@ -0,0 +1,9 @@
---
gitea_has_pkg: false
gitea_pkg_name: 'gitea'
gitea_pkg_user: 'git'
gitea_pkg_group: "{{ gitea_pkg_user }}"
gitea_gnupg_package: 'gnupg2'
gitea_dependencies:
- 'git'
- "{{ gitea_gnupg_package }}"

3
vars/gentoo.yml Normal file
View File

@ -0,0 +1,3 @@
---
gitea_has_pkg: true
gitea_gnupg_package: 'app-crypt/gnupg'

74
vars/main.yml Normal file
View File

@ -0,0 +1,74 @@
---
gitea_arch: "{{ 'amd64' if ansible_facts['architecture'] == 'x86_64' else ansible_facts['architecture'] }}"
gitea_base_url: 'https://dl.gitea.io/gitea/'
gitea_pgp_fingerprint: '7C9E68152594688862D62AF62D9AE806EC1592E2'
gitea_default_options:
DEFAULT:
RUN_USER: "{{ gitea_user }}"
RUN_MODE: 'prod'
repository:
ROOT: "{{ gitea_data_path }}/repos"
SCRIPT_TYPE: "{{ _bin_bash.stat.exists | ternary('bash', 'sh') }}"
git:
HOME_PATH: "{{ _gitea_options['server']['APP_DATA_PATH'] | default(gitea_data_path) ~ '/data/' }}/home"
repository.signing:
SIGNING_KEY: "{{ gitea_enable_signing | bool | ternary('default', 'none') }}"
server:
APP_DATA_PATH: "{{ gitea_data_path }}"
PROTOCOL: "{{ (gitea_tls_cert is defined) | ternary('https', 'http') }}"
DOMAIN: "{{ ansible_facts['fqdn'] }}"
HTTP_ADDR: "{{ gitea_bind_address }}"
HTTP_PORT: "{{ gitea_port }}"
SSH_LISTEN_HOST: "{{ gitea_bind_address }}"
OFFLINE_MODE: true
CERT_FILE: "{{ gitea_tls_cert | default(omit) }}"
KEY_FILE: "{{ gitea_tls_cert_key | default(omit) }}"
LFS_START_SERVER: "{{ gitea_enable_lfs }}"
LFS_JWT_SECRET: '`{{ _LFS_JWT_SECRET }}`'
database:
DB_TYPE: "{{ gitea_database_type | mandatory }}"
HOST: "{{ gitea_database_host | default(omit) }}"
NAME: "{{ (gitea_database_type != 'sqlite3') | ternary(gitea_database_name | default(omit), omit) }}"
USER: "{{ gitea_database_user | default(omit) }}"
PASSWD: "{{ '`' ~ gitea_database_password ~ '`' if gitea_database_password is defined else omit }}"
CHARSET: 'utf8mb4'
PATH: "{{ (gitea_database_type == 'sqlite3') | ternary(gitea_database_name | default(omit), omit) }}"
LOG_SQL: "{{ gitea_loglevel | lower in ['trace', 'debug'] }}"
indexer:
REPO_INDEXER_ENABLED: "{{ gitea_enable_indexer | bool }}"
security:
INSTALL_LOCK: true
SECRET_KEY: '`{{ _SECRET_KEY }}`'
INTERNAL_TOKEN: '`{{ _INTERNAL_TOKEN }}`'
COOKIE_USERNAME: 'gitea_user'
COOKIE_REMEMBER_NAME: 'gitea_persistent'
PASSWORD_HASH_ALGO: 'argon2'
service:
ENABLE_NOTIFY_MAIL: "{{ gitea_enable_mailer }}"
mailer:
ENABLED: "{{ gitea_enable_mailer }}"
SMTP_ADDR: "{% if ':' in gitea_mailer_host | default('') %}{{ gitea_mailer_host.split(':')[0] }}{% else %}{{ gitea_mailer_host | default(omit) }}{% endif %}"
SMTP_PORT: "{% if ':' in gitea_mailer_host | default('') %}{{ gitea_mailer_host.split(':')[1] }}{% else %}{{ omit }}{% endif %}"
FROM: "{{ gitea_mailer_from | default(omit) }}"
USER: "{{ gitea_mailer_user | default(omit) }}"
PASSWD: "{{ '`' ~ gitea_mailer_password ~ '`' if gitea_mailer_password is defined else omit }}"
SEND_AS_PLAIN_TEXT: true
PROTOCOL: "{% if gitea_mailer_host is defined %}{{ gitea_mailer_host.startswith('/') | ternary('smtp+unix', 'smtp') }}{% else %}sendmail{% endif %}"
session:
COOKIE_SECURE: "{{ gitea_tls_cert is defined }}"
COOKIE_NAME: 'gitea_session'
log:
ROOT_PATH: "{{ gitea_log_path }}"
# Default logger
MODE: 'file'
LEVEL: "{{ gitea_loglevel }}"
# Router logger
ROUTER: 'file'
ROUTER_LOG_LEVEL: "{{ gitea_loglevel }}"
# Access logger
ENABLE_ACCESS_LOG: "{{ gitea_loglevel | lower in ['trace', 'debug'] }}"
ACCESS: 'file'
oauth2:
JWT_SECRET: '`{{ _JWT_SECRET }}`'
other:
SHOW_FOOTER_VERSION: false