aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiguel Dalberto Pedro <miguel.pedro@precisioninno.com>2026-10-29 16:42:44 -0300
committerMiguel Dalberto Pedro <miguel.pedro@precisioninno.com>2026-10-29 16:42:44 -0300
commiteab4f5746a69783e4e3a3c05808ab58b5d2f494d (patch)
treeff6e149dccbd5ccc999d9e4f648bed823433ffab
parent4b7379914a1865f07b2c54252234cc90158fc253 (diff)
downloadmy-infra-master.tar.gz
my-infra-master.zip
feat(cgit): deploy cgit, fcgiwrap, and dedicated git SSH serverHEADmaster
- Enable cgit role (cgit_enabled: true) for git.miguelpedro.xyz.br - Provision dedicated 'git' user/group (/srv/git, git-shell, authorized_keys) - Add custom git-shell commands ('init' and 'list') for remote bare repo creation - Configure /etc/cgitrc with HTTP clone support, Pygments syntax highlighting, and README rendering - Add Nginx virtual host with automatic TLS upgrade once Let's Encrypt cert is present - Update DISCOVERY_REPORT.md, COMMAND_LOG.md, README.md, and AGENT_INSTRUCTIONS.md per Self-Updating Protocol
-rw-r--r--AGENT_INSTRUCTIONS.md11
-rw-r--r--COMMAND_LOG.md37
-rw-r--r--DISCOVERY_REPORT.md26
-rw-r--r--README.md41
-rw-r--r--group_vars/all/vars.yml9
-rw-r--r--roles/cgit/handlers/main.yml5
-rw-r--r--roles/cgit/tasks/main.yml124
-rw-r--r--roles/cgit/templates/cgitrc.j211
-rw-r--r--roles/cgit/templates/site-cgit.conf.j260
9 files changed, 305 insertions, 19 deletions
diff --git a/AGENT_INSTRUCTIONS.md b/AGENT_INSTRUCTIONS.md
index 45410d3..b6c6e4c 100644
--- a/AGENT_INSTRUCTIONS.md
+++ b/AGENT_INSTRUCTIONS.md
@@ -51,3 +51,14 @@ Before ending your session or creating a final commit, you **MUST** update the r
- Created roles for `common`, `certbot`, `valkey`, `searxng`, `nginx`, and scaffolded `cgit` (`cgit_enabled: false`) for upcoming deployment.
- Cleaned up unused Nginx stub symlink (`/etc/nginx/sites-enabled/miguelpedro.xyz.br` which pointed to `example.com`) via `nginx_remove_unused_sites`.
- **Verification:** Verified playbook against live VPS with `ansible-playbook site.yml --check --diff`.
+
+
+### [2026-10-29] Deploy cgit + fcgiwrap & Dedicated `git` SSH Server
+- **Agent Action:**
+ - Enabled `cgit_enabled: true` in `group_vars/all/vars.yml`.
+ - Expanded `roles/cgit` to install `cgit`, `fcgiwrap`, `python3-pygments`, `python3-markdown`, and `python3-docutils`.
+ - Provisioned dedicated `git` system user/group (`999:989`, home `/srv/git`, shell `/usr/bin/git-shell`) with SSH `authorized_keys` and custom `git-shell-commands` (`init` and `list`).
+ - Added `www-data` to the `git` group and configured `/var/www/.gitconfig` (`safe.directory = *`) so `cgit`/`fcgiwrap` can read bare repositories in `/srv/git`.
+ - Configured `/etc/cgitrc` with HTTP clone support (`enable-http-clone=1`), syntax highlighting, and README rendering.
+ - Deployed `/etc/nginx/sites-available/cgit.conf` (and enabled symlink) for `git.miguelpedro.xyz.br`, blocking `git-receive-pack` over HTTP (`403`) and auto-upgrading to TLS once the Certbot certificate for `git.miguelpedro.xyz.br` is present.
+- **Verification:** Applied `ansible-playbook site.yml --tags "cgit" --diff`, verified `cgit v1.2.3` HTTP response, CSS delivery, and `ssh git@miguelpedro.xyz.br list`, and confirmed full idempotency (`ok=42 changed=0`).
diff --git a/COMMAND_LOG.md b/COMMAND_LOG.md
index e386d19..c530013 100644
--- a/COMMAND_LOG.md
+++ b/COMMAND_LOG.md
@@ -593,3 +593,40 @@ vps : ok=28 changed=3 unreachable=0 failed=0 skipped=6 rescued=0 ignored=0
```
- **Verified 1:1 match (`ok`):** `common` packages, `certbot` timer & certs, `valkey` service, `searxng` user/group/dirs/git/settings.yml/limiter.toml/uwsgi.ini, `nginx.conf`, `default.apps-available/searxng.conf`, and `sites-available/searxng.conf`.
- **Expected cleanups flagged (`changed`):** Removing unused `/etc/nginx/sites-enabled/miguelpedro.xyz.br` stub symlink (`example.com`) and trailing blank lines in `/etc/nginx/sites-available/default`.
+
+
+---
+
+## 10. cgit & fcgiwrap Deployment and Verification (2026-10-29)
+**Pre-flight Check:**
+```bash
+.venv/bin/ansible-playbook site.yml --check --diff
+```
+**Output:** `ok=27 changed=0 failed=0`
+
+**Apply `cgit` Role:**
+```bash
+.venv/bin/ansible-playbook site.yml --tags "cgit" --diff
+```
+**Output:** `ok=18 changed=16 failed=0` (Installed `cgit`, `fcgiwrap`, `python3-docutils`, `python3-markdown`, `python3-pygments`; created `git` user/group `999:989`; added `www-data` to `git` group; configured `/srv/git` with `02750` and `git-shell-commands`; deployed `/etc/cgitrc` and `/etc/nginx/sites-available/cgit.conf`; reloaded `nginx` and restarted `fcgiwrap`).
+
+**Health & SSH Verification:**
+```bash
+ssh root@miguelpedro.xyz.br '
+curl -s -i -H "Host: git.miguelpedro.xyz.br" http://127.0.0.1/ | head -n 35
+curl -s -I -H "Host: git.miguelpedro.xyz.br" http://127.0.0.1/cgit-css/cgit.css
+id git; id www-data
+'
+ssh -o BatchMode=yes git@miguelpedro.xyz.br list
+```
+**Output:**
+- `http://127.0.0.1/` (`Host: git.miguelpedro.xyz.br`) returned `cgit v1.2.3` HTML (`Miguel Pedro Git Repositories`).
+- `/cgit-css/cgit.css` returned `HTTP/1.1 200 OK` (`text/css`, `15112` bytes).
+- `uid=999(git) gid=989(git)`, `uid=33(www-data) gid=33(www-data) groups=33(www-data),989(git)`.
+- `ssh git@miguelpedro.xyz.br list` connected cleanly via `git-shell` with exit code `0`.
+
+**Post-Apply Idempotency Check:**
+```bash
+.venv/bin/ansible-playbook site.yml --check --diff
+```
+**Output:** `ok=42 changed=0 unreachable=0 failed=0 skipped=0`
diff --git a/DISCOVERY_REPORT.md b/DISCOVERY_REPORT.md
index ab45750..dea8c08 100644
--- a/DISCOVERY_REPORT.md
+++ b/DISCOVERY_REPORT.md
@@ -16,6 +16,11 @@ Internet (Ports 80/443)
[ Nginx Reverse Proxy & Web Server ] (/etc/nginx)
├── miguelpedro.xyz.br / www.miguelpedro.xyz.br ──► Static files (/var/www/html)
├── _ (default_server :80) ───────────────────────► Static files + /searxng sub-path
+ ├── git.miguelpedro.xyz.br (:80 / :443)
+ │ │
+ │ ▼ (Unix Socket: /run/fcgiwrap.socket)
+ │ [ fcgiwrap Daemon ] ──► /usr/lib/cgit/cgit.cgi (/etc/cgitrc)
+ │ Scans bare Git repos in /srv/git (user/group `git` 999:989)
└── searx.miguelpedro.xyz.br (HTTPS :443)
│
▼ (Unix Socket: /usr/local/searxng/run/socket)
@@ -46,13 +51,27 @@ Internet (Ports 80/443)
- Security headers: `X-Frame-Options "SAMEORIGIN"`, `X-Content-Type-Options "nosniff"`, `Referrer-Policy "no-referrer"`, `Strict-Transport-Security "max-age=31536000; includeSubDomains"`.
- Proxies `location /` via `uwsgi_pass unix:///usr/local/searxng/run/socket;` with `HTTP_HOST`, `HTTP_CONNECTION`, `HTTP_X_SCHEME`, `HTTP_X_REAL_IP`, and `HTTP_X_FORWARDED_FOR`.
- Port 80 redirect block for `searx.miguelpedro.xyz.br` (301 to HTTPS).
- 3. **`miguelpedro.xyz.br` (`-> /etc/nginx/sites-available/miguelpedro.xyz.br`)**:
- - Port 80 stub with `server_name example.com www.example.com;` pointing to `/var/www/miguelpedro.xyz.br/html` (currently an empty directory, while the active `miguelpedro.xyz.br` site is served from `sites-available/default`).
+ 3. **`cgit.conf` (`-> /etc/nginx/sites-available/cgit.conf`)**:
+ - Serves `git.miguelpedro.xyz.br`, mapping static assets `/cgit-css/*` to `/usr/share/cgit/` and passing CGI requests to `unix:/run/fcgiwrap.socket` (`/usr/lib/cgit/cgit.cgi`). Blocks `git-receive-pack` over HTTP (`403`) so pushes remain strictly over SSH. Automatically upgrades to HTTPS once `/etc/letsencrypt/live/git.miguelpedro.xyz.br/fullchain.pem` is provisioned.
+ 4. **`miguelpedro.xyz.br` (disabled stub in `/etc/nginx/sites-available/miguelpedro.xyz.br`)**:
+ - Symlink removed from `/etc/nginx/sites-enabled/` via Ansible (`nginx_remove_unused_sites`).
- **Default App Includes (`/etc/nginx/default.d/`):**
- `/etc/nginx/default.d/searxng.conf -> /etc/nginx/default.apps-available/searxng.conf` (created by SearXNG's official installation script, exposing `location /searxng` on the default server block).
---
+## 2b. cgit & Git Server (`git.miguelpedro.xyz.br`)
+
+- **Packages:** `cgit`, `fcgiwrap`, `git`, `python3-pygments`, `python3-markdown`, `python3-docutils`
+- **Services:** `fcgiwrap.socket` and `fcgiwrap.service` (active, enabled; socket `/run/fcgiwrap.socket`).
+- **System User & Group:** `git` (`UID=999`, `GID=989`, home `/srv/git`, shell `/usr/bin/git-shell`). `www-data` is a member of the `git` group (`989`) to allow `cgit`/`fcgiwrap` read access, with `/var/www/.gitconfig` configuring `safe.directory = *`.
+- **Repositories Root:** `/srv/git` (`02750` `git:git`)
+ - `/srv/git/.ssh/authorized_keys` (`0600` `git:git`): Contains authorized SSH public keys.
+ - `/srv/git/git-shell-commands/`: Custom `git-shell` helper commands (`init`, `list`, `no-interactive-login`) allowing remote creation of bare repositories via `ssh git@miguelpedro.xyz.br init <repo>.git "<description>"`.
+- **Configuration:** `/etc/cgitrc` (enables `enable-http-clone=1`, syntax highlighting via Pygments, README rendering, and `scan-path=/srv/git`).
+
+---
+
## 3. SearXNG, uWSGI & Valkey
SearXNG was installed using the official SearXNG installation script (`utils/searxng.sh`), running natively via `uwsgi` and backed by `valkey-server`.
@@ -121,7 +140,8 @@ SearXNG was installed using the official SearXNG installation script (`utils/sea
| Component | Critical Path on VPS | Purpose |
| :--- | :--- | :--- |
| **Nginx Core** | `/etc/nginx/nginx.conf` | Global Nginx settings (`server_tokens off`, TLS 1.2/1.3) |
-| **Nginx VHosts** | `/etc/nginx/sites-available/default`<br>`/etc/nginx/sites-available/searxng.conf`<br>`/etc/nginx/sites-available/miguelpedro.xyz.br` | Main domain vhost, SearXNG reverse proxy vhost, and stub config |
+| **Nginx VHosts** | `/etc/nginx/sites-available/default`<br>`/etc/nginx/sites-available/searxng.conf`<br>`/etc/nginx/sites-available/cgit.conf` | Main domain vhost, SearXNG reverse proxy vhost, and cgit vhost |
+| **cgit & Git Repos** | `/etc/cgitrc`<br>`/srv/git/`<br>`/srv/git/git-shell-commands/`<br>`/run/fcgiwrap.socket` | cgit configuration, bare Git repositories (`git:git`), `git-shell` helpers, and FastCGI socket |
| **Nginx App Snippet** | `/etc/nginx/default.apps-available/searxng.conf` | `/searxng` sub-path snippet symlinked in `/etc/nginx/default.d/` |
| **Web Root** | `/var/www/html/`<br>`/var/www/miguelpedro.xyz.br/html/` | Static web roots |
| **SearXNG Config** | `/etc/searxng/settings.yml`<br>`/etc/searxng/limiter.toml` | SearXNG instance settings, secret key, Valkey URL, and bot limiter |
diff --git a/README.md b/README.md
index 770013d..e92509b 100644
--- a/README.md
+++ b/README.md
@@ -8,11 +8,11 @@ This repository manages the configuration and state of the `miguelpedro.xyz.br`
| Service | Domain / Endpoint | Role | Description |
| :--- | :--- | :--- | :--- |
-| **Nginx** | `miguelpedro.xyz.br`<br>`searx.miguelpedro.xyz.br` | [`roles/nginx`](file:///home/mpedro/Repos/my-infra/roles/nginx/tasks/main.yml) | Reverse proxy, TLS termination, and static web server |
+| **Nginx** | `miguelpedro.xyz.br`<br>`searx.miguelpedro.xyz.br`<br>`git.miguelpedro.xyz.br` | [`roles/nginx`](file:///home/mpedro/Repos/my-infra/roles/nginx/tasks/main.yml) | Reverse proxy, TLS termination, and static web server |
| **Certbot** | `/etc/letsencrypt/` | [`roles/certbot`](file:///home/mpedro/Repos/my-infra/roles/certbot/tasks/main.yml) | Automated Let's Encrypt ECDSA certificate management (`certbot.timer`) |
| **SearXNG** | `https://searx.miguelpedro.xyz.br/` | [`roles/searxng`](file:///home/mpedro/Repos/my-infra/roles/searxng/tasks/main.yml) | Privacy-respecting metasearch engine served via `uwsgi` Unix socket |
| **Valkey** | `127.0.0.1:6379` | [`roles/valkey`](file:///home/mpedro/Repos/my-infra/roles/valkey/tasks/main.yml) | Local key-value store backing SearXNG's rate limiter and bot detection |
-| **cgit** *(Prepared)* | `git.miguelpedro.xyz.br` | [`roles/cgit`](file:///home/mpedro/Repos/my-infra/roles/cgit/tasks/main.yml) | Ready-to-enable role (`cgit_enabled: false`) for self-hosted Git web UI via `fcgiwrap` |
+| **cgit** | `https://git.miguelpedro.xyz.br/` | [`roles/cgit`](file:///home/mpedro/Repos/my-infra/roles/cgit/tasks/main.yml) | Self-hosted Git web UI (`cgit` + `fcgiwrap`), HTTP clone support, and `git@` SSH hosting (`/srv/git`) |
---
@@ -84,13 +84,34 @@ Sensitive values (such as `vault_searxng_secret_key`) are encrypted in [`group_v
---
-## 4. Deploying `cgit` (Upcoming)
+## 4. Using `cgit` & Managing Git Repositories
-The `cgit` role ([`roles/cgit`](file:///home/mpedro/Repos/my-infra/roles/cgit/tasks/main.yml)) is already scaffolded. When ready to deploy:
-1. Ensure DNS for `cgit_domain` (default `git.miguelpedro.xyz.br`) points to the VPS.
-2. In [`group_vars/all/vars.yml`](file:///home/mpedro/Repos/my-infra/group_vars/all/vars.yml), set:
- ```yaml
- cgit_enabled: true
+`cgit` is active (`cgit_enabled: true`) and serves repositories from `/srv/git` under the dedicated `git` user (`git-shell`).
+
+### Creating a New Repository via SSH
+A custom `git-shell` helper command (`init`) is installed so you can create bare repositories remotely without logging into root:
+```bash
+ssh git@miguelpedro.xyz.br init my-project.git "My project description"
+```
+
+### Listing Existing Repositories via SSH
+```bash
+ssh git@miguelpedro.xyz.br list
+```
+
+### Pushing to a Repository
+```bash
+git remote add origin git@miguelpedro.xyz.br:my-project.git
+git push -u origin master
+```
+
+### Enabling HTTPS for `git.miguelpedro.xyz.br`
+Once your DNS `A` record for `git.miguelpedro.xyz.br` points to `189.126.106.41`:
+1. Request the Let's Encrypt certificate on the VPS:
+ ```bash
+ ssh root@miguelpedro.xyz.br "certbot certonly --nginx -d git.miguelpedro.xyz.br"
+ ```
+2. Re-run the `cgit` role—[`roles/cgit/templates/site-cgit.conf.j2`](file:///home/mpedro/Repos/my-infra/roles/cgit/templates/site-cgit.conf.j2) automatically detects `/etc/letsencrypt/live/git.miguelpedro.xyz.br/fullchain.pem` and switches the Nginx virtual host to HTTPS with HTTP-to-HTTPS redirect:
+ ```bash
+ .venv/bin/ansible-playbook site.yml --tags "cgit" --diff
```
-3. Run `.venv/bin/ansible-playbook site.yml --tags "cgit" --diff`.
-4. Provision TLS via Certbot (`certbot --nginx -d git.miguelpedro.xyz.br`) and update [`roles/cgit/templates/site-cgit.conf.j2`](file:///home/mpedro/Repos/my-infra/roles/cgit/templates/site-cgit.conf.j2) to include the SSL directives.
diff --git a/group_vars/all/vars.yml b/group_vars/all/vars.yml
index 8ecb430..db792fd 100644
--- a/group_vars/all/vars.yml
+++ b/group_vars/all/vars.yml
@@ -37,8 +37,13 @@ searxng_base_url: "https://{{ searxng_domain }}/"
searxng_valkey_url: "valkey://localhost:6379/0"
searxng_secret_key: "{{ vault_searxng_secret_key }}"
-# Future cgit deployment toggle & defaults
-cgit_enabled: false
+# cgit & Git server configuration
+cgit_enabled: true
+cgit_user: git
+cgit_group: git
cgit_repos_path: /srv/git
cgit_title: "Miguel Pedro Git Repositories"
cgit_description: "Self-hosted Git repositories powered by cgit"
+cgit_enable_http_clone: true
+cgit_ssh_authorized_keys:
+ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDcBQznFkp2YuQg6zxJ9g/9n3E/byi+z+N3JWRpQu17C migueldalberto.acc@gmail.com"
diff --git a/roles/cgit/handlers/main.yml b/roles/cgit/handlers/main.yml
new file mode 100644
index 0000000..e4af55c
--- /dev/null
+++ b/roles/cgit/handlers/main.yml
@@ -0,0 +1,5 @@
+---
+- name: Restart fcgiwrap
+ ansible.builtin.systemd:
+ name: fcgiwrap.service
+ state: restarted
diff --git a/roles/cgit/tasks/main.yml b/roles/cgit/tasks/main.yml
index 44f9459..f454d93 100644
--- a/roles/cgit/tasks/main.yml
+++ b/roles/cgit/tasks/main.yml
@@ -4,17 +4,125 @@
name:
- cgit
- fcgiwrap
- - python3-pygments
+ - git
+ - python3-docutils
- python3-markdown
+ - python3-pygments
+ state: present
+
+- name: Ensure dedicated git system group exists
+ ansible.builtin.group:
+ name: "{{ cgit_group }}"
+ system: true
+ state: present
+
+- name: Ensure dedicated git system user exists
+ ansible.builtin.user:
+ name: "{{ cgit_user }}"
+ group: "{{ cgit_group }}"
+ home: "{{ cgit_repos_path }}"
+ shell: /usr/bin/git-shell
+ comment: "Git version control user"
+ system: true
+ create_home: true
state: present
-- name: Ensure Git repositories root directory exists
+- name: Add www-data to git group so Nginx/fcgiwrap can read repositories
+ ansible.builtin.user:
+ name: www-data
+ groups: "{{ cgit_group }}"
+ append: true
+ notify: Restart fcgiwrap
+
+- name: Ensure Git repositories root directory permissions (setgid for group inheritance)
ansible.builtin.file:
path: "{{ cgit_repos_path }}"
state: directory
+ owner: "{{ cgit_user }}"
+ group: "{{ cgit_group }}"
+ mode: "02750"
+
+- name: Configure git user safe.directory for cgit (www-data)
+ ansible.builtin.copy:
+ dest: /var/www/.gitconfig
owner: www-data
group: www-data
+ mode: "0644"
+ content: |
+ [safe]
+ directory = *
+
+- name: Ensure git-shell-commands directory exists (disables interactive shell login and provides helpers)
+ ansible.builtin.file:
+ path: "{{ cgit_repos_path }}/git-shell-commands"
+ state: directory
+ owner: root
+ group: "{{ cgit_group }}"
+ mode: "0755"
+
+- name: Deploy git-shell helper commands (no-interactive-login, init, list)
+ ansible.builtin.copy:
+ dest: "{{ cgit_repos_path }}/git-shell-commands/{{ item.name }}"
+ owner: root
+ group: "{{ cgit_group }}"
mode: "0755"
+ content: "{{ item.content }}"
+ loop:
+ - name: no-interactive-login
+ content: |
+ #!/bin/sh
+ printf '%s\n' "Hi! You've successfully authenticated as '{{ cgit_user }}', but interactive shell access is disabled."
+ printf '%s\n' "Tip: Run 'ssh {{ cgit_user }}@{{ primary_domain }} init <repo>.git [description]' to create a new repository."
+ exit 128
+ - name: list
+ content: |
+ #!/bin/sh
+ find "{{ cgit_repos_path }}" -maxdepth 2 -name "*.git" -type d -printf "%P\n" | sort
+ - name: init
+ content: |
+ #!/bin/sh
+ set -e
+ REPO="$1"
+ DESC="$2"
+ if [ -z "$REPO" ]; then
+ echo "Usage: ssh {{ cgit_user }}@{{ primary_domain }} init <repo-name>.git [description]"
+ exit 1
+ fi
+ case "$REPO" in
+ *.git) ;;
+ *) REPO="${REPO}.git" ;;
+ esac
+ TARGET="{{ cgit_repos_path }}/${REPO}"
+ if [ -d "$TARGET" ]; then
+ echo "Error: Repository ${REPO} already exists."
+ exit 1
+ fi
+ git init --bare --shared=group "$TARGET"
+ if [ -n "$DESC" ]; then
+ echo "$DESC" > "$TARGET/description"
+ else
+ echo "${REPO%.git}" > "$TARGET/description"
+ fi
+ echo "Initialized bare repository at {{ cgit_user }}@{{ primary_domain }}:${REPO}"
+
+- name: Ensure .ssh directory exists for git user
+ ansible.builtin.file:
+ path: "{{ cgit_repos_path }}/.ssh"
+ state: directory
+ owner: "{{ cgit_user }}"
+ group: "{{ cgit_group }}"
+ mode: "0700"
+
+- name: Configure authorized SSH keys for git user
+ ansible.builtin.copy:
+ dest: "{{ cgit_repos_path }}/.ssh/authorized_keys"
+ owner: "{{ cgit_user }}"
+ group: "{{ cgit_group }}"
+ mode: "0600"
+ content: |
+ {% for key in cgit_ssh_authorized_keys %}
+ {{ key }}
+ {% endfor %}
- name: Deploy /etc/cgitrc configuration
ansible.builtin.template:
@@ -24,11 +132,19 @@
group: root
mode: "0644"
-- name: Ensure fcgiwrap socket/service is enabled and running
+- name: Ensure fcgiwrap socket and service are enabled and running
ansible.builtin.systemd:
- name: fcgiwrap.socket
+ name: "{{ item }}"
enabled: true
state: started
+ loop:
+ - fcgiwrap.socket
+ - fcgiwrap.service
+
+- name: Check if TLS certificate exists for {{ cgit_domain }}
+ ansible.builtin.stat:
+ path: "/etc/letsencrypt/live/{{ cgit_domain }}/fullchain.pem"
+ register: cgit_tls_cert
- name: Deploy Nginx virtual host for cgit
ansible.builtin.template:
diff --git a/roles/cgit/templates/cgitrc.j2 b/roles/cgit/templates/cgitrc.j2
index 0cae2ab..6e9cbd0 100644
--- a/roles/cgit/templates/cgitrc.j2
+++ b/roles/cgit/templates/cgitrc.j2
@@ -7,11 +7,21 @@ root-title={{ cgit_title }}
root-desc={{ cgit_description }}
virtual-root=/
+{% if cgit_enable_http_clone | default(true) %}
+enable-http-clone=1
+clone-url=https://{{ cgit_domain }}/$CGIT_REPO_URL git@{{ primary_domain }}:$CGIT_REPO_URL
+{% else %}
+enable-http-clone=0
+clone-url=git@{{ primary_domain }}:$CGIT_REPO_URL
+{% endif %}
+
enable-index-links=1
enable-index-owner=0
enable-commit-graph=1
enable-log-filecount=1
enable-log-linecount=1
+enable-git-config=1
+remove-suffix=1
snapshots=tar.gz zip
about-filter=/usr/lib/cgit/filters/about-formatting.sh
@@ -23,4 +33,5 @@ readme=:README.rst
readme=:README.txt
readme=:README
+# Scan path must be placed after global repository defaults
scan-path={{ cgit_repos_path }}
diff --git a/roles/cgit/templates/site-cgit.conf.j2 b/roles/cgit/templates/site-cgit.conf.j2
index 557c364..36753d3 100644
--- a/roles/cgit/templates/site-cgit.conf.j2
+++ b/roles/cgit/templates/site-cgit.conf.j2
@@ -1,3 +1,55 @@
+{% if cgit_tls_cert.stat.exists | default(false) %}
+server {
+ server_name {{ cgit_domain }};
+
+ # Security headers
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
+
+ root /usr/share/cgit;
+ try_files $uri @cgit;
+
+ # Serve static cgit assets directly
+ location ~* ^/cgit-css/(.*)$ {
+ alias /usr/share/cgit/$1;
+ expires 30d;
+ }
+
+ # Block git-Receive-pack over HTTP (pushing is strictly via SSH as git@)
+ location ~ ^/.*/git-receive-pack$ {
+ return 403;
+ }
+
+ location @cgit {
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
+ fastcgi_param PATH_INFO $uri;
+ fastcgi_param QUERY_STRING $args;
+ fastcgi_param HTTP_HOST $server_name;
+ fastcgi_pass unix:/run/fcgiwrap.socket;
+ }
+
+ listen 443 ssl; # managed by Certbot / Ansible
+ listen [::]:443 ssl; # managed by Certbot / Ansible
+ ssl_certificate /etc/letsencrypt/live/{{ cgit_domain }}/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/{{ cgit_domain }}/privkey.pem;
+ include /etc/letsencrypt/options-ssl-nginx.conf;
+ ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
+}
+
+server {
+ if ($host = {{ cgit_domain }}) {
+ return 301 https://$host$request_uri;
+ }
+
+ listen 80;
+ listen [::]:80;
+ server_name {{ cgit_domain }};
+ return 404;
+}
+{% else %}
server {
listen 80;
listen [::]:80;
@@ -6,8 +58,15 @@ server {
root /usr/share/cgit;
try_files $uri @cgit;
+ # Serve static cgit assets directly
location ~* ^/cgit-css/(.*)$ {
alias /usr/share/cgit/$1;
+ expires 30d;
+ }
+
+ # Block git-receive-pack over HTTP (pushing is strictly via SSH as git@)
+ location ~ ^/.*/git-receive-pack$ {
+ return 403;
}
location @cgit {
@@ -19,3 +78,4 @@ server {
fastcgi_pass unix:/run/fcgiwrap.socket;
}
}
+{% endif %}