Initial version of the forgejo collection

* Forgejo role for quick deployment in podman
* Forgejo_OAuth module to manage authentication source

Signed-off-by: Nis Wechselberg <enbewe@enbewe.de>
This commit is contained in:
Nis Wechselberg 2024-06-23 15:23:51 +02:00
parent 70646c7ba8
commit 47467b3169
Signed by: eNBeWe
GPG key ID: 7B25171F921B9E57
12 changed files with 923 additions and 0 deletions

5
CHANGELOG.md Normal file
View file

@ -0,0 +1,5 @@
# Changelog
## 1.0.0
* Initial Release

22
README.md Normal file
View file

@ -0,0 +1,22 @@
# Ansible Collection - enbewe.forgejo
Collecion to configure forgejo git server.
## Playbooks
### enbewe.forgejo.deployment
Deploys the role `enbewe.forgejo.forgejo` to all hosts in the group `forgejo`.
## Roles
### enbewe.forgejo.forgejo
Deploys [Forgejo](https://forgejo.org/) through a podman container.
Additionally deploys an accompanying postgres database.
When configured the Forgejo instance is automatically connected with some OAuth2 provider,
to implement single-sign-on.
## Modules
### enbewe.forgejo.forgejo_oauth
Manages OAuth sources through the CLI of the forgejo server.

68
galaxy.yml Normal file
View file

@ -0,0 +1,68 @@
---
### REQUIRED
# The namespace of the collection. This can be a company/brand/organization or product namespace under which all
# content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with
# underscores or numbers and cannot contain consecutive underscores
namespace: 'enbewe'
# The name of the collection. Has the same character restrictions as 'namespace'
name: 'forgejo'
# The version of the collection. Must be compatible with semantic versioning
version: '1.0.0'
# The path to the Markdown (.md) readme file. This path is relative to the root of the collection
readme: 'README.md'
# A list of the collection's content authors. Can be just the name or in the format 'Full Name <email> (url)
# @nicks:irc/im.site#channel'
authors:
- 'Nis Wechselberg <enbewe@enbewe.de>'
### OPTIONAL but strongly recommended
# A short summary description of the collection
description: 'Collection to deploy a forgejo server'
# Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only
# accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file'
license:
- 'MIT'
# A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character
# requirements as 'namespace' and 'name'
tags:
- 'linux'
# Collections that this collection requires to be installed for it to be usable. The key of the dict is the
# collection label 'namespace.name'. The value is a version range
# L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version
# range specifiers can be set and are separated by ','
dependencies:
containers.podman: '>=1.13.0'
# The URL of the originating SCM repository
repository: 'https://git.enbewe.de/Coding/ansible-collection-forgejo'
# The URL to any online docs
# documentation: http://docs.example.com
# The URL to the homepage of the collection/project
# homepage: http://example.com
# The URL to the collection issue tracker
# issues: http://example.com/issue/tracker
# A list of file glob-like patterns used to filter any files or directories that should not be included in the build
# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry',
# and '.git' are always filtered. Mutually exclusive with 'manifest'
# build_ignore: []
# A dict controlling use of manifest directives used in building the collection artifact. The key 'directives' is a
# list of MANIFEST.in style
# L(directives,https://packaging.python.org/en/latest/guides/using-manifest-in/#manifest-in-commands). The key
# 'omit_default_directives' is a boolean that controls whether the default directives are used. Mutually exclusive
# with 'build_ignore'
# manifest: null

52
meta/runtime.yml Normal file
View file

@ -0,0 +1,52 @@
---
# Collections must specify a minimum required ansible version to upload
# to galaxy
requires_ansible: '>=2.17.0'
# Content that Ansible needs to load from another location or that has
# been deprecated/removed
# plugin_routing:
# action:
# redirected_plugin_name:
# redirect: ns.col.new_location
# deprecated_plugin_name:
# deprecation:
# removal_version: "4.0.0"
# warning_text: |
# See the porting guide on how to update your playbook to
# use ns.col.another_plugin instead.
# removed_plugin_name:
# tombstone:
# removal_version: "2.0.0"
# warning_text: |
# See the porting guide on how to update your playbook to
# use ns.col.another_plugin instead.
# become:
# cache:
# callback:
# cliconf:
# connection:
# doc_fragments:
# filter:
# httpapi:
# inventory:
# lookup:
# module_utils:
# modules:
# netconf:
# shell:
# strategy:
# terminal:
# test:
# vars:
# Python import statements that Ansible needs to load from another location
# import_redirection:
# ansible_collections.ns.col.plugins.module_utils.old_location:
# redirect: ansible_collections.ns.col.plugins.module_utils.new_location
# Groups of actions/modules that take a common set of options
# action_groups:
# group_name:
# - module1
# - module2

5
playbooks/deploy.yml Normal file
View file

@ -0,0 +1,5 @@
---
- name: 'Deploy forgejo role to host group'
hosts: 'forgejo'
roles:
- 'enbewe.forgejo.forgejo'

31
plugins/README.md Normal file
View file

@ -0,0 +1,31 @@
# Collections Plugins Directory
This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that
is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that
would contain module utils and modules respectively.
Here is an example directory of the majority of plugins currently supported by Ansible:
```
└── plugins
├── action
├── become
├── cache
├── callback
├── cliconf
├── connection
├── filter
├── httpapi
├── inventory
├── lookup
├── module_utils
├── modules
├── netconf
├── shell
├── strategy
├── terminal
├── test
└── vars
```
A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible-core/2.17/plugins/plugins.html).

View file

@ -0,0 +1,299 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: Nis Wechselberg <enbewe@enbewe.de>
# MIT License (see https://spdx.org/licenses/MIT.html)
from __future__ import absolute_import, print_function
DOCUMENTATION = r'''
---
module: forgejo_auth
short_description: Module to manage forgejo oauth authentication sources
version_added: "1.0.0"
description: This module uses the forgejo api to manage the configured oauth
authentication sources. It can add, remove or update an authentication source.
This module only works for forgejo running inside containers, created through
this collections role. There are several assumptions regardings names and ids.
author:
- Nis Wechselberg (@eNBeWe)
'''
EXAMPLES = r'''
# Create a new auth provider, but don't touch existing source
- name: 'Add authentication source'
enbewe.forgejo.forgejo_oauth:
state: 'present'
update: true
name: 'eNBeWe.eu SSO'
provider: 'openidConnect'
key: 'clientIdConfiguredInAuthProvider'
secret: 'secretKeyOfTheClientIdInAuthProvider'
auto_discover_url: 'https://<autodiscover-url>'
skip_local_2fa: true
'''
RETURN = r'''
# Return values
'''
from ansible.module_utils.basic import AnsibleModule
class ForgejoOAuth:
'''
Handles the management of forgejo authentication sources.
'''
module = None
def __init__(self, module: AnsibleModule):
'''
Create a new OAuth Handler
'''
self.module = module
self.name = module.params.get('name')
def run(self):
'''
Apply the configuration.
'''
oauth_source_already_exists, oauth_source_id = self.get_oauth_source_with_name()
if self.module.params.get('state') == 'absent':
if oauth_source_already_exists:
return self.remove_auth_source(oauth_source_id)
return {
'failed': False,
'changed': False,
'msg': f'OAuth2 authorization source {self.name} is not configured.'
}
if self.module.params.get('state') == 'present':
if oauth_source_already_exists and self.module.params.get('update'):
return self.update_oauth_source(oauth_source_id)
if oauth_source_already_exists:
return {
'failed': False,
'changed': False,
'msg': f'OAuth2 authorization source {self.name} already exists.'
}
return self.add_oauth_source()
return {
'failed': True,
'changed': False,
'msg': 'Misconfigured plugin, unknown state.'
}
def get_oauth_source_with_name(self):
'''
Check the list of configured authentication sources for
an existing OAuth source of the given name.
'''
args_list = [
'podman', 'exec',
'-u', '1000:1000',
'-it', 'forgejo-app',
'forgejo', 'admin', 'auth', 'list',
'--vertical-bars'
]
_, auth_list_stdout, _ = self.module.run_command(args_list)
auth_table_rows = auth_list_stdout.split("\n")
if len(auth_table_rows) > 2:
for auth_id in range(1, len(auth_table_rows) - 1):
auth_row = auth_table_rows[auth_id].split('|')
auth_id = int(auth_row[0].strip())
auth_name = auth_row[1].strip()
auth_type = auth_row[2].strip()
if auth_type == 'OAuth2' and auth_name == self.name:
return (True, auth_id)
return (False, -1)
def add_oauth_source(self):
'''
Add a new OAuth2 source with the given configuration.
'''
args_list = [
'podman', 'exec',
'-u', '1000:1000',
'-it', 'forgejo-app',
'forgejo', 'admin', 'auth', 'add-oauth',
]
args_list += self.create_command_arguments()
auth_add_rc, auth_add_stdout, _ = self.module.run_command(args_list)
if auth_add_rc == 0:
return {
'failed': False,
'changed': True,
'msg': f'OAuth2 authorization source {self.name} successful created.'
}
return {
'failed': True,
'msg': auth_add_stdout
}
def remove_auth_source(self, source_id):
'''
Removes the authentication source with the given id from the config.
'''
args_list = [
'podman', 'exec',
'-u', '1000:1000',
'-it', 'forgejo-app',
'forgejo', 'admin', 'auth', 'delete',
'--id', str(source_id),
]
auth_delete_rc, auth_delete_stdout, _ = self.module.run_command(args_list)
if auth_delete_rc == 0:
return {
'failed': False,
'changed': True,
'msg': f'OAuth2 authorization source {self.name} removed.'
}
return {
'failed': True,
'msg': auth_delete_stdout
}
def update_oauth_source(self, source_id):
'''
Updates the authentication source with the given id to match the passed config.
'''
args_list = [
'podman', 'exec',
'-u', '1000:1000',
'-it', 'forgejo-app',
'forgejo', 'admin', 'auth', 'update-oauth',
'--id', str(source_id),
]
args_list += self.create_command_arguments()
auth_update_rc, auth_update_stdout, _ = self.module.run_command(args_list)
if auth_update_rc == 0:
return {
'failed': False,
'changed': True,
'msg': f'OAuth2 authorization source {self.name} has been updated'
}
return {
'failed': True,
'msg': auth_update_stdout
}
def create_command_arguments(self):
'''
Create the command arguments for cli commands from the passed config.
'''
result = [
'--name', self.name,
'--provider', self.module.params.get('provider'),
'--key', self.module.params.get('key'),
'--secret', self.module.params.get('secret'),
'--auto-discover-url',
(self.module.params.get('auto_discover_url')
if self.module.params.get('auto_discover_url') else ""),
'--custom-tenant-id',
(self.module.params.get('custom_tenant_id')
if self.module.params.get('custom_tenant_id') else ""),
'--custom-auth-url',
(self.module.params.get('custom_auth_url')
if self.module.params.get('custom_auth_url') else ""),
'--custom-token-url',
(self.module.params.get('custom_token_url')
if self.module.params.get('custom_token_url') else ""),
'--custom-profile-url',
(self.module.params.get('custom_profile_url')
if self.module.params.get('custom_profile_url') else ""),
'--custom-email-url',
(self.module.params.get('custom_email_url')
if self.module.params.get('custom_email_url') else ""),
'--icon-url',
(self.module.params.get('icon_url')
if self.module.params.get('icon_url') else ""),
'--required-claim-name',
(self.module.params.get('required_claim_name')
if self.module.params.get('required_claim_name') else ""),
'--required-claim-value',
(self.module.params.get('required_claim_value')
if self.module.params.get('required_claim_value') else ""),
'--group-claim-name',
(self.module.params.get('group_claim_name')
if self.module.params.get('group_claim_name') else ""),
'--admin-group',
(self.module.params.get('admin_group')
if self.module.params.get('admin_group') else ""),
'--restricted-group',
(self.module.params.get('restricted_group')
if self.module.params.get('restricted_group') else ""),
'--group-team-map',
(self.module.params.get('group_team_map')
if self.module.params.get('group_team_map') else ""),
]
for scope in self.module.params.get('scopes').split():
result.append('--scopes')
result.append(scope)
if self.module.params.get('use_custom_urls'):
result.append('--use-custom-urls')
if self.module.params.get('skip_local_2fa'):
result.append('--skip-local-2fa')
if self.module.params.get('group_team_map_removal'):
result.append('--group-team-map-removal')
return result
def main():
'''
Run the module code.
'''
module_args = {
'state': {'type': str, 'default': 'present', 'choices': ['present', 'absent']},
'update': {'type': bool, 'default': False},
'name': {'type': str, 'required': True},
'provider': {'type': str, 'required': True},
'key': {'type': str, 'required': True},
'secret': {'type': str, 'required': True},
'auto_discover_url': {'type': str, 'required': False},
'use_custom_urls': {'type': bool, 'default': False},
'custom_tenant_id': {'type': str, 'required': False},
'custom_auth_url': {'type': str, 'required': False},
'custom_token_url': {'type': str, 'required': False},
'custom_profile_url': {'type': str, 'required': False},
'custom_email_url': {'type': str, 'required': False},
'icon_url': {'type': str, 'required': False},
'skip_local_2fa': {'type': bool, 'default': False},
'scopes': {'type': str, 'required': False},
'required_claim_name': {'type': str, 'required': False},
'required_claim_value': {'type': str, 'required': False},
'group_claim_name': {'type': str, 'required': False},
'admin_group': {'type': str, 'required': False},
'restricted_group': {'type': str, 'required': False},
'group_team_map': {'type': str, 'required': False},
'group_team_map_removal': {'type': bool, 'default': False},
}
module = AnsibleModule(
argument_spec = module_args,
supports_check_mode = False,
)
forgejo_auth_module = ForgejoOAuth(module)
result = forgejo_auth_module.run()
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,15 @@
---
forgejo_db_image_name: 'docker.io/library/postgres'
forgejo_db_image_tag: '17.2'
forgejo_db_volume_name: 'forgejo_db_storage'
forgejo_network_name: 'forgejo-net'
forgejo_app_image_name: 'codeberg.org/forgejo/forgejo'
forgejo_app_image_tag: '9.0.3'
forgejo_app_volume_name: 'forgejo_app_storage'
forgejo_db_user: 'forgejo'
forgejo_db_database: 'forgejo'
forgejo_ssh_port: 2222

View file

@ -0,0 +1,69 @@
{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn)}}
{{template "base/alert" .}}
{{end}}
<h4 class="ui top attached header center">
{{if .LinkAccountMode}}
{{ctx.Locale.Tr "auth.oauth_signin_title"}}
{{else}}
{{ctx.Locale.Tr "auth.login_userpass"}}
{{end}}
</h4>
<div class="ui attached segment">
<form class="ui form tw-max-w-2xl tw-m-auto" action="{{.SignInLink}}" method="post">
{{.CsrfTokenHtml}}
{{if (not .OAuth2Providers)}}
<div class="required field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
<label for="user_name">{{ctx.Locale.Tr "home.uname_holder"}}</label>
<input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required>
</div>
{{if or (not .DisablePassword) .LinkAccountMode}}
<div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
<label for="password">{{ctx.Locale.Tr "password"}}</label>
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="current-password" required>
</div>
{{end}}
{{if not .LinkAccountMode}}
<div class="inline field">
<div class="ui checkbox">
<label>{{ctx.Locale.Tr "auth.remember_me"}}</label>
<input name="remember" type="checkbox">
</div>
</div>
{{end}}
{{template "user/auth/captcha" .}}
<div class="field">
<button class="ui primary button">
{{if .LinkAccountMode}}
{{ctx.Locale.Tr "auth.oauth_signin_submit"}}
{{else}}
{{ctx.Locale.Tr "sign_in"}}
{{end}}
</button>
<a href="{{AppSubUrl}}/user/forgot_password">{{ctx.Locale.Tr "auth.forgot_password"}}</a>
</div>
{{if .ShowRegistrationButton}}
<div class="field">
<a href="{{AppSubUrl}}/user/sign_up">{{ctx.Locale.Tr "auth.sign_up_now"}}</a>
</div>
{{end}}
{{end}}
{{if .OAuth2Providers}}
<div id="oauth2-login-navigator" class="tw-py-1">
<div class="tw-flex tw-flex-col tw-justify-center">
<div id="oauth2-login-navigator-inner" class="tw-flex tw-flex-col tw-flex-wrap tw-items-center tw-gap-2">
{{range $provider := .OAuth2Providers}}
<a class="{{$provider.Name}} ui button tw-flex tw-items-center tw-justify-center tw-py-2 tw-w-full oauth-login-link" href="{{AppSubUrl}}/user/oauth2/{{$provider.DisplayName}}">
{{$provider.IconHTML 28}}
{{ctx.Locale.Tr "sign_in_with_provider" $provider.DisplayName}}
</a>
{{end}}
</div>
</div>
</div>
{{end}}
</form>
</div>

View file

@ -0,0 +1,56 @@
---
- name: 'Reload forgejo services'
become: true
ansible.builtin.service:
daemon_reload: true
- name: 'Restart forgejo network'
become: true
ansible.builtin.service:
name: '{{ forgejo_network_name }}-network'
state: 'restarted'
notify:
- 'Restart forgejo-db container'
- 'Restart forgejo-app container'
- name: 'Restart forgejo volumes'
become: true
ansible.builtin.service:
name: '{{ item }}'
state: 'restarted'
loop:
- '{{ forgejo_db_volume_name }}-volume'
- '{{ forgejo_app_volume_name }}-volume'
notify:
- 'Restart forgejo-db container'
- 'Restart forgejo-app container'
- name: 'Restart forgejo-db image'
become: true
ansible.builtin.service:
name: 'forgejo-db-image'
state: 'restarted'
notify:
- 'Restart forgejo-db container'
- name: 'Restart forgejo-db container'
become: true
ansible.builtin.service:
name: 'forgejo-db'
state: 'restarted'
notify:
- 'Restart forgejo-app container'
- name: 'Restart forgejo-app image'
become: true
ansible.builtin.service:
name: 'forgejo-app-image'
state: 'restarted'
notify:
- 'Restart forgejo-app container'
- name: 'Restart forgejo-app container'
become: true
ansible.builtin.service:
name: 'forgejo-app'
state: 'restarted'

View file

@ -0,0 +1,177 @@
---
# Basic stuff
- name: 'Install required software'
become: true
ansible.builtin.apt:
name: 'podman'
state: 'present'
- name: 'Create forgejo podman network'
become: true
containers.podman.podman_network:
name: '{{ forgejo_network_name }}'
state: 'quadlet'
notify:
- 'Reload forgejo services'
- 'Restart forgejo network'
- name: 'Create forgejo volumes'
become: true
containers.podman.podman_volume:
name: '{{ item }}'
state: 'quadlet'
loop:
- '{{ forgejo_db_volume_name }}'
- '{{ forgejo_app_volume_name }}'
notify:
- 'Reload forgejo services'
- 'Restart forgejo volumes'
- name: 'Define forgejo-db image'
become: true
containers.podman.podman_image:
name: '{{ forgejo_db_image_name }}:{{ forgejo_db_image_tag }}'
quadlet_filename: 'forgejo-db'
state: 'quadlet'
notify:
- 'Reload forgejo services'
- 'Restart forgejo-db image'
- name: 'Create forgejo-db container'
become: true
containers.podman.podman_container:
name: 'forgejo-db'
image: 'forgejo-db.image'
network: '{{ forgejo_network_name }}.network'
state: 'quadlet'
volume:
- '{{ forgejo_db_volume_name }}.volume:/var/lib/postgresql/data/'
env:
POSTGRES_DB: '{{ forgejo_db_database }}'
POSTGRES_USER: '{{ forgejo_db_user }}'
POSTGRES_PASSWORD: '{{ forgejo_db_password }}'
quadlet_options: |
[Install]
WantedBy=default.target
[Unit]
Requires={{ forgejo_network_name }}-network.service
Requires={{ forgejo_db_volume_name }}-volume.service
After={{ forgejo_network_name }}-network.service
After={{ forgejo_db_volume_name }}-volume.service
notify:
- 'Reload forgejo services'
- 'Restart forgejo-db container'
# Application
- name: 'Prepare config location'
become: true
ansible.builtin.file:
path: '{{ item }}'
state: 'directory'
owner: 'root'
group: 'root'
mode: 'u=rwx,g=rx,o=rx'
loop:
- '/srv/git/conf/'
- '/srv/git/custom/templates/user/auth'
- name: 'Deploy application config'
become: true
ansible.builtin.template:
src: 'forgejo.ini.j2'
dest: '/srv/git/conf/forgejo.ini'
owner: 'root'
group: 'root'
mode: 'u=rw,g=r,o=r'
notify: 'Restart forgejo-app container'
- name: 'Write customized login page'
become: true
ansible.builtin.copy:
src: 'forgejo/templates/user/auth/signin_inner.tmpl'
dest: '/srv/git/custom/templates/user/auth/signin_inner.tmpl'
owner: 'root'
group: 'root'
mode: 'u=rw,g=r,o=r'
notify: 'Restart forgejo-app container'
- name: 'Define forgejo-app image'
become: true
containers.podman.podman_image:
name: '{{ forgejo_app_image_name }}:{{ forgejo_app_image_tag }}'
quadlet_filename: 'forgejo-app'
state: 'quadlet'
notify:
- 'Reload forgejo services'
- 'Restart forgejo-app image'
- name: 'Create forgejo-app container'
become: true
containers.podman.podman_container:
name: 'forgejo-app'
image: 'forgejo-app.image'
network: '{{ forgejo_network_name }}.network'
state: 'quadlet'
volume:
- '{{ forgejo_app_volume_name }}.volume:/data/'
- '/etc/timezone:/etc/timezone:ro'
- '/etc/localtime:/etc/localtime:ro'
- '/srv/git/conf/forgejo.ini:/data/gitea/conf/app.ini:ro'
- '/srv/git/custom/templates:/data/gitea/templates:ro'
env:
USER_UID: '1000'
USER_GID: '1000'
FORGEJO__database__DB_TYPE: 'postgres'
FORGEJO__database__HOST: 'forgejo-db:5432'
FORGEJO__database__NAME: '{{ forgejo_db_database }}'
FORGEJO__database__USER: '{{ forgejo_db_user }}'
FORGEJO__database__PASSWD: '{{ forgejo_db_password }}'
ports:
- '{{ forgejo_ssh_port }}:22/tcp'
quadlet_options: |
[Install]
WantedBy=default.target
[Unit]
Requires={{ forgejo_network_name }}-network.service
Requires={{ forgejo_app_volume_name }}-volume.service
Requires=forgejo-db.service
After={{ forgejo_network_name }}-network.service
After={{ forgejo_app_volume_name }}-volume.service
After=forgejo-db.service
notify:
- 'Reload forgejo services'
- 'Restart forgejo-app container'
- name: Flush handlers
ansible.builtin.meta: flush_handlers
# Authentication source
- name: 'Configure forgejo authentication source'
become: true
when: forgejo_sso_create_source
enbewe.forgejo.forgejo_oauth:
state: 'present'
update: '{{ forgejo_sso_update | default(false) }}'
name: '{{ forgejo_sso_name }}'
provider: '{{ forgejo_sso_provider }}'
key: '{{ forgejo_sso_key }}'
secret: '{{ forgejo_sso_secret }}'
auto_discover_url: '{{ forgejo_sso_auto_discover_url | default("") }}'
use_custom_urls: '{{ forgejo_sso_use_custom_urls | default(false) }}'
custom_tenant_id: '{{ forgejo_sso_custom_tenant_id | default("") }}'
custom_auth_url: '{{ forgejo_sso_custom_auth_url | default("") }}'
custom_token_url: '{{ forgejo_sso_custom_token_url | default("") }}'
custom_profile_url: '{{ forgejo_sso_custom_profile_url | default("") }}'
custom_email_url: '{{ forgejo_sso_custom_email_url | default("") }}'
icon_url: '{{ forgejo_sso_icon_url | default("") }}'
skip_local_2fa: '{{ forgejo_sso_skip_local_2fa | default(true) }}'
scopes: '{{ forgejo_sso_scopes | default("") }}'
required_claim_name: '{{ forgejo_sso_required_claim_name | default("") }}'
required_claim_value: '{{ forgejo_sso_required_claim_value | default("") }}'
group_claim_name: '{{ forgejo_sso_group_claim_name | default("") }}'
admin_group: '{{ forgejo_sso_admin_group | default("") }}'
restricted_group: '{{ forgejo_sso_restricted_group | default("") }}'
group_team_map: '{{ forgejo_sso_group_team_map | default("") }}'
group_team_map_removal: '{{ forgejo_sso_group_team_map_removal | default(false) }}'
retries: 5
delay: 5

View file

@ -0,0 +1,124 @@
APP_NAME = {{ forgejo_general_app_name }}
RUN_MODE = {{ forgejo_general_run_mode | default('prod') }}
RUN_USER = git
WORK_PATH = /data/gitea
[repository]
ROOT = /data/git/repositories
DEFAULT_PRIVATE = {{ forgejo_repostiory_default_private | default('last') }}
DISABLE_HTTP_GIT = {{ forgejo_repostiory_disable_http_git | default(false) }}
ENABLE_PUSH_CREATE_USER = {{ forgejo_repostiory_enable_push_create_user | default(false) }}
ENABLE_PUSH_CREATE_ORG = {{ forgejo_repostiory_enable_push_create_org | default(false) }}
[repository.pull-request]
DEFAULT_MERGE_STYLE = merge
[repository.upload]
TEMP_PATH = /data/gitea/uploads
[repository.signing]
DEFAULT_TRUST_MODEL = committer
[repository.local]
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
[server]
APP_DATA_PATH = /data/gitea
DOMAIN = {{ forgejo_server_domain }}
SSH_DOMAIN = {{ forgejo_server_ssh_domain | default(forgejo_server_domain) }}
HTTP_PORT = 3000
ROOT_URL = {{ forgejo_server_root_url }}
DISABLE_SSH = false
SSH_PORT = {{ forgejo_ssh_port }}
SSH_LISTEN_PORT = 22
LFS_START_SERVER = true
LFS_JWT_SECRET = {{ forgejo_server_lfs_jwt_secret }}
OFFLINE_MODE = {{ forgejo_server_offline_mode | default(true) }}
LANDING_PAGE = {{ forgejo_server_landing_page | default('home') }}
[database]
DB_TYPE = postgres
HOST = forgejo-db:5432
NAME = {{ forgejo_db_database }}
USER = {{ forgejo_db_user }}
PASSWD = {{ forgejo_db_password }}
SCHEMA =
SSL_MODE = disable
LOG_SQL = false
[indexer]
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
[admin]
DEFAULT_EMAIL_NOTIFICATIONS = {{ forgejo_admin_default_email_notification | default('enabled') }}
DISABLE_REGULAR_ORG_CREATION = {{ forgejo_admin_disable_regular_org_creation | default(false) }}
SEND_NOTIFICATION_EMAIL_ON_NEW_USER = {{ forgejo_admin_send_notification_email_on_new_user | default(false) }}
[security]
INSTALL_LOCK = true
SECRET_KEY = {{ forgejo_security_secret_key }}
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = *
INTERNAL_TOKEN = {{ forgejo_security_internal_token }}
PASSWORD_HASH_ALGO = pbkdf2_hi
[openid]
ENABLE_OPENID_SIGNIN = {{ forgejo_openid_enable_openid_signin | default(true) }}
ENABLE_OPENID_SIGNUP = {{ forgejo_openid_enable_openid_signup | default(false) }}
[oauth2_client]
ENABLE_AUTO_REGISTRATION = {{ forgejo_oauth2_enable_auto_registration | default(false) }}
[service]
REGISTER_EMAIL_CONFIRM = {{ forgejo_service_register_email_confirm | default(false) }}
REGISTER_MANUAL_CONFIRM = {{ forgejo_service_register_manual_confirm | default(false) }}
DISABLE_REGISTRATION = {{ forgejo_service_disable_registration | default(false) }}
REQUIRE_SIGNIN_VIEW = {{ forgejo_service_require_signin_view | default(true) }}
ENABLE_NOTIFY_MAIL = {{ forgejo_service_enable_notify_mail | default(false) }}
ENABLE_CAPTCHA = false
DEFAULT_KEEP_EMAIL_PRIVATE = {{ forgejo_service_default_keep_email_private | default(true) }}
DEFAULT_ALLOW_CREATE_ORGANIZATION = {{ forgejo_service_default_allow_create_organization | default(false) }}
DEFAULT_ENABLE_TIMETRACKING = false
ALLOW_ONLY_INTERNAL_REGISTRATION = {{ forgejo_service_allow_only_internal_registration | default(false) }}
ALLOW_ONLY_EXTERNAL_REGISTRATION = {{ forgejo_service_allow_only_external_registration | default(false) }}
[service.explore]
REQUIRE_SIGNIN_VIEW = {{ forgejo_service_explore_require_signin_view | default(false) }}
DISABLE_USERS_PAGE = {{ forgejo_service_explore_disable_users_page | default(true) }}
[mailer]
ENABLED = false
[session]
PROVIDER = file
PROVIDER_CONFIG = /data/gitea/sessions
[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
[attachment]
PATH = /data/gitea/attachments
[log]
MODE = console
LEVEL = {{ forgejo_log_level | default('info') }}
ROOT_PATH = /data/gitea/log
[cron.update_checker]
ENABLED = true
[oauth2]
ENABLED = {{ forgejo_oauth2_provider_enabled | default(false) }}
JWT_SECRET = {{ forgejo_oauth2_jwt_secret }}
[federation]
ENABLED = {{ forgejo_federation_enabled | default(false) }}
SHARE_USER_STATISTICS = {{ forgejo_federation_share_user_statistics | default(true) }}
MAX_SIZE = {{ forgejo_federation_max_size | default(4) }}
[lfs]
PATH = /data/git/lfs
[actions]
ENABLED = {{ forgejo_actions_enabled | default(false) }}