diff --git a/roles/gitea/defaults/main.yml b/roles/gitea/defaults/main.yml new file mode 100644 index 0000000..12291e7 --- /dev/null +++ b/roles/gitea/defaults/main.yml @@ -0,0 +1,33 @@ +--- +# Gitea version +gitea_version: "1.22" + +# Network configuration +gitea_port: 3000 + +# Container image +gitea_image: gitea/gitea + +# Data directory +gitea_data_dir: "{{ podman_projects_dir }}/gitea/data" + +# Database configuration (PostgreSQL) +gitea_postgres_db_name: gitea +gitea_postgres_user: gitea +gitea_postgres_schema: gitea +# gitea_postgres_password: "" # Required - set in inventory + +# Application configuration +gitea_app_name: "Gitea" +gitea_domain: git.nas.local +gitea_root_url: "https://{{ gitea_domain }}" + +# Disable SSH (HTTPS only for Git operations) +gitea_disable_ssh: true + +# Disable registration +gitea_disable_registration: false + +# Nginx reverse proxy configuration +gitea_nginx_enabled: true +gitea_nginx_hostname: "{{ gitea_domain }}" diff --git a/roles/gitea/handlers/main.yml b/roles/gitea/handlers/main.yml new file mode 100644 index 0000000..eb2e044 --- /dev/null +++ b/roles/gitea/handlers/main.yml @@ -0,0 +1,15 @@ +--- +- name: Reload systemd + ansible.builtin.systemd: + daemon_reload: true + +- name: Restart gitea + ansible.builtin.systemd: + name: gitea + state: restarted + daemon_reload: true + +- name: Reload nginx + ansible.builtin.systemd: + name: nginx + state: reloaded diff --git a/roles/gitea/meta/main.yml b/roles/gitea/meta/main.yml new file mode 100644 index 0000000..0eff038 --- /dev/null +++ b/roles/gitea/meta/main.yml @@ -0,0 +1,4 @@ +--- +dependencies: + - role: podman + - role: postgres diff --git a/roles/gitea/tasks/main.yml b/roles/gitea/tasks/main.yml new file mode 100644 index 0000000..34285e6 --- /dev/null +++ b/roles/gitea/tasks/main.yml @@ -0,0 +1,116 @@ +--- +- name: Validate required passwords are set + ansible.builtin.assert: + that: + - gitea_postgres_password is defined + - gitea_postgres_password | length >= 12 + fail_msg: | + gitea_postgres_password is required (min 12 chars). + See roles/gitea/defaults/main.yml for configuration instructions. + success_msg: "Password validation passed" + +- name: Create PostgreSQL user for Gitea + community.postgresql.postgresql_user: + name: "{{ gitea_postgres_user }}" + password: "{{ gitea_postgres_password }}" + state: present + become_user: "{{ postgres_admin_user }}" + +- name: Create PostgreSQL database for Gitea + community.postgresql.postgresql_db: + name: "{{ gitea_postgres_db_name }}" + owner: "{{ gitea_postgres_user }}" + state: present + become_user: "{{ postgres_admin_user }}" + +- name: Grant all privileges on database to Gitea user + community.postgresql.postgresql_privs: + login_db: "{{ gitea_postgres_db_name }}" + roles: "{{ gitea_postgres_user }}" + type: database + privs: ALL + state: present + become_user: "{{ postgres_admin_user }}" + +- name: Ensure Gitea user has no superuser privileges + community.postgresql.postgresql_user: + name: "{{ gitea_postgres_user }}" + role_attr_flags: NOSUPERUSER,NOCREATEDB,NOCREATEROLE + state: present + become_user: "{{ postgres_admin_user | default('postgres') }}" + +- name: Create PostgreSQL schema for Gitea + community.postgresql.postgresql_schema: + name: "{{ gitea_postgres_schema }}" + database: "{{ gitea_postgres_db_name }}" + owner: "{{ gitea_postgres_user }}" + state: present + become_user: "{{ postgres_admin_user | default('postgres') }}" + +- name: Grant schema permissions to Gitea user + community.postgresql.postgresql_privs: + login_db: "{{ gitea_postgres_db_name }}" + roles: "{{ gitea_postgres_user }}" + type: schema + objs: "{{ gitea_postgres_schema }}" + privs: CREATE,USAGE + state: present + become_user: "{{ postgres_admin_user | default('postgres') }}" + +- name: Create Gitea project directory + ansible.builtin.file: + path: "{{ podman_projects_dir | default('/opt/podman') }}/gitea" + state: directory + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0755" + +- name: Create Gitea data directory + ansible.builtin.file: + path: "{{ gitea_data_dir }}" + state: directory + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0755" + +- name: Deploy Kubernetes YAML for Gitea + ansible.builtin.template: + src: gitea.yaml.j2 + dest: "{{ podman_projects_dir | default('/opt/podman') }}/gitea/gitea.yaml" + owner: "{{ ansible_user }}" + group: "{{ ansible_user }}" + mode: "0644" + notify: Restart gitea + +- name: Create systemd service for Gitea + ansible.builtin.template: + src: gitea.service.j2 + dest: /etc/systemd/system/gitea.service + owner: root + group: root + mode: "0644" + notify: Reload systemd + +- name: Enable and start Gitea service + ansible.builtin.systemd: + name: gitea + enabled: true + state: started + daemon_reload: true + +- name: Deploy nginx vhost configuration for Gitea + ansible.builtin.template: + src: nginx-vhost.conf.j2 + dest: "{{ nginx_conf_dir | default('/etc/nginx/conf.d') }}/gitea.conf" + owner: root + group: root + mode: "0644" + when: gitea_nginx_enabled + notify: Reload nginx + +- name: Remove nginx vhost configuration for Gitea + ansible.builtin.file: + path: "{{ nginx_conf_dir | default('/etc/nginx/conf.d') }}/gitea.conf" + state: absent + when: not gitea_nginx_enabled + notify: Reload nginx diff --git a/roles/gitea/templates/gitea.service.j2 b/roles/gitea/templates/gitea.service.j2 new file mode 100644 index 0000000..c3a31d9 --- /dev/null +++ b/roles/gitea/templates/gitea.service.j2 @@ -0,0 +1,18 @@ +[Unit] +Description=Gitea Git Service +Requires=network-online.target +After=network-online.target + +[Service] +Type=oneshot +RemainAfterExit=true +User={{ ansible_user }} +Group={{ ansible_user }} +WorkingDirectory={{ podman_projects_dir | default('/opt/podman') }}/gitea +ExecStart=/usr/bin/podman play kube --replace gitea.yaml +ExecStop=/usr/bin/podman play kube --down gitea.yaml +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=multi-user.target diff --git a/roles/gitea/templates/gitea.yaml.j2 b/roles/gitea/templates/gitea.yaml.j2 new file mode 100644 index 0000000..df1bafd --- /dev/null +++ b/roles/gitea/templates/gitea.yaml.j2 @@ -0,0 +1,54 @@ +--- +apiVersion: v1 +kind: Pod +metadata: + name: gitea + labels: + app: gitea +spec: + containers: + - name: server + image: {{ gitea_image }}:{{ gitea_version }} + ports: + - containerPort: {{ gitea_port }} + hostPort: {{ gitea_port }} + env: + - name: GITEA__database__DB_TYPE + value: postgres + - name: GITEA__database__HOST + value: {{ immich_postgres_host | default('127.0.0.1') }} + - name: GITEA__database__PORT + value: "5432" + - name: GITEA__database__NAME + value: "{{ gitea_postgres_db_name }}" + - name: GITEA__database__USER + value: "{{ gitea_postgres_user }}" + - name: GITEA__database__PASSWD + value: "{{ gitea_postgres_password }}" + - name: GITEA__server__DOMAIN + value: "{{ gitea_domain }}" + - name: GITEA__server__ROOT_URL + value: "{{ gitea_root_url }}" + - name: GITEA__server__HTTP_PORT + value: "{{ gitea_port }}" + - name: GITEA__server__DISABLE_SSH + value: "{{ 'true' if gitea_disable_ssh else 'false' }}" + - name: GITEA__service__DISABLE_REGISTRATION + value: "{{ 'true' if gitea_disable_registration else 'false' }}" + volumeMounts: + - name: localtime + mountPath: /etc/localtime + readOnly: true + - name: gitea-data + mountPath: /data + restartPolicy: Always + + volumes: + - name: localtime + hostPath: + path: /etc/localtime + type: File + - name: gitea-data + hostPath: + path: {{ gitea_data_dir }} + type: Directory diff --git a/roles/gitea/templates/mail.yml b/roles/gitea/templates/mail.yml new file mode 100644 index 0000000..e69de29 diff --git a/roles/gitea/templates/nginx-vhost.conf.j2 b/roles/gitea/templates/nginx-vhost.conf.j2 new file mode 100644 index 0000000..0fef30d --- /dev/null +++ b/roles/gitea/templates/nginx-vhost.conf.j2 @@ -0,0 +1,53 @@ +# Gitea vhost with Let's Encrypt (Certbot) +# Managed by Ansible - DO NOT EDIT MANUALLY + +server { + listen 80; + server_name {{ gitea_nginx_hostname }}; + + # Certbot webroot for ACME challenges + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + # Redirect to HTTPS + location / { + return 301 https://$server_name$request_uri; + } +} + +server { + listen 443 ssl; + server_name {{ gitea_nginx_hostname }}; + + # Let's Encrypt certificates (managed by Certbot) + ssl_certificate /etc/letsencrypt/live/{{ gitea_nginx_hostname }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ gitea_nginx_hostname }}/privkey.pem; + + # SSL configuration + ssl_protocols {{ nginx_ssl_protocols | default('TLSv1.3') }}; + ssl_prefer_server_ciphers on; + +{% if nginx_log_backend | default('journald') == 'journald' %} + access_log syslog:server=unix:/dev/log,nohostname,tag=nginx_gitea; + error_log syslog:server=unix:/dev/log,nohostname,tag=nginx_gitea; +{% else %} + access_log /var/log/nginx/{{ gitea_nginx_hostname }}_access.log main; + error_log /var/log/nginx/{{ gitea_nginx_hostname }}_error.log; +{% endif %} + + # Increase client max body size for large Git pushes + client_max_body_size 512M; + + location / { + proxy_pass http://127.0.0.1:{{ gitea_port }}; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Required for Git LFS and large repository operations + proxy_buffering off; + proxy_request_buffering off; + } +}