385 lines
12 KiB
Markdown
385 lines
12 KiB
Markdown
# Gitea público con Tailscale Funnel en NAS UGREEN
|
|
|
|
Guía para exponer Gitea (en Docker, NAS UGREEN con UGOS) por HTTPS a internet usando **Tailscale Funnel**, de modo que funcione desde cualquier equipo **sin instalar Tailscale**, en casa y fuera, con una sola URL.
|
|
|
|
## Datos de esta instalación
|
|
|
|
| Elemento | Valor |
|
|
|---|---|
|
|
| URL pública final | `https://unasgeorge.vaquita-hadar.ts.net` |
|
|
| Tailnet | `vaquita-hadar.ts.net` |
|
|
| Nombre del NAS en Tailscale | `unasgeorge` |
|
|
| IP Tailscale del NAS (contenedor) | `100.68.242.27` |
|
|
| IP local del NAS | `192.168.1.145` |
|
|
| Puerto de Gitea en el NAS (host) | `30000` (interno del contenedor: `3000`) |
|
|
| Puerta de enlace Docker (bridge) | `172.17.0.1` |
|
|
| Destino del proxy de Funnel | `http://172.17.0.1:30000` |
|
|
| Usuario Gitea | `george` |
|
|
|
|
### Esquema
|
|
|
|
```
|
|
Cualquier equipo (con o sin Tailscale)
|
|
│
|
|
▼
|
|
https://unasgeorge.vaquita-hadar.ts.net (puerto 443, HTTPS, Let's Encrypt)
|
|
│
|
|
▼ Tailscale Funnel (contenedor Tailscale en el NAS)
|
|
http://172.17.0.1:30000 (interno, nadie lo usa directamente)
|
|
│
|
|
▼
|
|
Gitea
|
|
```
|
|
|
|
---
|
|
|
|
## 1. Preparar Tailscale (panel web)
|
|
|
|
### 1.1 Activar certificados HTTPS
|
|
|
|
1. Entrar a <https://login.tailscale.com/admin/dns>.
|
|
2. En **HTTPS Certificates**, pulsar **Enable HTTPS**.
|
|
|
|
### 1.2 Permitir Funnel en la política de acceso
|
|
|
|
1. Entrar a <https://login.tailscale.com/admin/acls>.
|
|
2. Verificar que exista este bloque (añadirlo si no está, respetando las comas):
|
|
|
|
```json
|
|
"nodeAttrs": [
|
|
{
|
|
"target": ["autogroup:member"],
|
|
"attr": ["funnel"]
|
|
}
|
|
]
|
|
```
|
|
|
|
3. Guardar (**Save**).
|
|
|
|
> Alternativa: la primera vez que se ejecuta `tailscale funnel`, el comando muestra un enlace (`https://login.tailscale.com/f/funnel?node=...`) que añade este permiso automáticamente.
|
|
|
|
**Validación:** después del paso 2, en <https://login.tailscale.com/admin/machines> debe aparecer la etiqueta **Funnel** junto a `unasgeorge`.
|
|
|
|
---
|
|
|
|
## 2. Configurar Funnel en el contenedor de Tailscale
|
|
|
|
### 2.1 Abrir la terminal del contenedor
|
|
|
|
1. Entrar al panel del NAS (UGOS) → app **Docker** → **Contenedores**.
|
|
2. Abrir el contenedor de **Tailscale** → **Terminal** → añadir terminal.
|
|
3. En *Command* usar **`/bin/sh`** (la imagen de Tailscale no trae bash).
|
|
|
|
> Si algún comando da *failed to connect to local tailscaled*, anteponer el socket:
|
|
> `tailscale --socket=/tmp/tailscaled.sock <comando>`
|
|
|
|
### 2.2 Comprobar que Tailscale está conectado
|
|
|
|
```sh
|
|
tailscale status
|
|
tailscale version
|
|
```
|
|
|
|
Debe aparecer `unasgeorge` con IP `100.68.242.27`. Versión recomendada: 1.50 o superior.
|
|
|
|
### 2.3 Encontrar por dónde ve el contenedor a Gitea
|
|
|
|
El contenedor de Tailscale está en la red bridge de Docker: **no** llega a Gitea por la IP local del NAS ni por `127.0.0.1`. Hay que usar la puerta de enlace de Docker.
|
|
|
|
```sh
|
|
ip route | head -1
|
|
# Resultado esperado: default via 172.17.0.1 dev eth0
|
|
|
|
wget -qO- -T 5 http://172.17.0.1:30000 | head -c 200
|
|
# Debe devolver HTML que empieza con <!DOCTYPE html>
|
|
```
|
|
|
|
Referencia de lo que **no** funciona en esta instalación:
|
|
|
|
```sh
|
|
wget -qO- -T 5 http://192.168.1.145:30000 | head -c 200 # download timed out
|
|
wget -qO- -T 5 http://127.0.0.1:30000 | head -c 200 # Connection refused
|
|
```
|
|
|
|
### 2.4 Activar Funnel
|
|
|
|
```sh
|
|
tailscale serve reset
|
|
tailscale funnel --bg http://172.17.0.1:30000
|
|
```
|
|
|
|
Salida esperada:
|
|
|
|
```
|
|
Available on the internet:
|
|
|
|
https://unasgeorge.vaquita-hadar.ts.net/
|
|
|-- proxy http://172.17.0.1:30000
|
|
|
|
Funnel started and running in the background.
|
|
```
|
|
|
|
### 2.5 Validar
|
|
|
|
```sh
|
|
tailscale funnel status
|
|
```
|
|
|
|
Debe mostrar **un solo** destino:
|
|
|
|
```
|
|
https://unasgeorge.vaquita-hadar.ts.net (Funnel on)
|
|
|-- / proxy http://172.17.0.1:30000
|
|
```
|
|
|
|
> `--bg` deja Funnel activo en segundo plano y sobrevive a reinicios del contenedor. Si el contenedor se **recrea** (no solo se reinicia) y su estado no está en un volumen, hay que repetir el paso 2.4.
|
|
>
|
|
> Para desactivar Funnel: `tailscale funnel --https=443 off`
|
|
|
|
---
|
|
|
|
## 3. Configurar Gitea
|
|
|
|
### 3.1 Variables de entorno del contenedor (prioritario)
|
|
|
|
Las variables `GITEA__seccion__CLAVE` **tienen prioridad sobre `app.ini`** y lo reescriben en cada arranque. En esta instalación existía `GITEA__server__ROOT_URL = http://100.68.242.27:30000/`, que era la causa de que el botón *Clone* mostrara la URL antigua.
|
|
|
|
1. App **Docker** de UGOS → contenedor de **Gitea** → **Detener**.
|
|
2. **Editar → Variables de entorno**.
|
|
3. Dejar los valores así (cambiar o añadir):
|
|
|
|
```
|
|
GITEA__server__ROOT_URL = https://unasgeorge.vaquita-hadar.ts.net/
|
|
GITEA__server__DOMAIN = unasgeorge.vaquita-hadar.ts.net
|
|
GITEA__server__SSH_DOMAIN = 192.168.1.145
|
|
GITEA__server__PUBLIC_URL_DETECTION = never
|
|
GITEA__service__DISABLE_REGISTRATION = true
|
|
```
|
|
|
|
4. Guardar e **Iniciar** el contenedor. Los repositorios no se pierden: están en el volumen.
|
|
|
|
### 3.2 `app.ini` (si no se usan variables)
|
|
|
|
Ruta habitual dentro del volumen: `.../gitea/gitea/conf/app.ini` (en el contenedor: `/data/gitea/conf/app.ini`). Se puede editar desde el **Gestor de archivos** de UGOS.
|
|
|
|
```ini
|
|
[server]
|
|
DOMAIN = unasgeorge.vaquita-hadar.ts.net
|
|
ROOT_URL = https://unasgeorge.vaquita-hadar.ts.net/
|
|
SSH_DOMAIN = 192.168.1.145
|
|
PUBLIC_URL_DETECTION = never
|
|
|
|
[service]
|
|
DISABLE_REGISTRATION = true
|
|
```
|
|
|
|
Reiniciar el contenedor de Gitea tras editar.
|
|
|
|
> `PUBLIC_URL_DETECTION = never` hace que Gitea use siempre `ROOT_URL` para los enlaces, aunque se entre por otra dirección (IP de Tailscale, `ugdocker.link`, etc.).
|
|
> `SSH_DOMAIN` apunta a la IP local: SSH **no** pasa por Funnel, solo funciona en la red de casa.
|
|
|
|
### 3.3 Validar
|
|
|
|
- **Administración del sitio → Configuración → URL de la aplicación** debe ser `https://unasgeorge.vaquita-hadar.ts.net/`.
|
|
- En cualquier repo, el botón **Clone (HTTPS)** debe mostrar:
|
|
`https://unasgeorge.vaquita-hadar.ts.net/george/<repo>.git`
|
|
- Al cerrar sesión **no** debe aparecer la opción *Registrarse*.
|
|
|
|
> El aviso *"The detected web site URL is ... unlikely matching the site config"* solo aparece cuando se entra por una dirección distinta de `ROOT_URL`. Desaparece al entrar por `https://unasgeorge.vaquita-hadar.ts.net`.
|
|
|
|
### 3.4 Crear un token de acceso
|
|
|
|
Git por HTTPS usa el token como contraseña.
|
|
|
|
1. Gitea → avatar → **Configuración → Aplicaciones** → *Administrar tokens de acceso*.
|
|
2. Nombre: uno por equipo (ej. `macbook-air`, `serfactpro`).
|
|
3. Permisos: **repository → Read and Write** (y **user → Read** si lo pide).
|
|
4. **Generar token** y copiarlo (solo se muestra una vez).
|
|
|
|
> No usar **Manage SSH Keys** para esto: las claves SSH no funcionan a través de Funnel.
|
|
|
|
---
|
|
|
|
## 4. Validaciones de acceso
|
|
|
|
### 4.1 Desde un equipo con Tailscale encendido
|
|
|
|
```bash
|
|
curl -I https://unasgeorge.vaquita-hadar.ts.net
|
|
```
|
|
|
|
Esperado:
|
|
|
|
```
|
|
HTTP/2 200
|
|
```
|
|
|
|
Si se queda colgado después de `Request completely sent off`, Funnel no llega a Gitea: revisar el paso 2.3 y el destino del proxy.
|
|
|
|
### 4.2 DNS público (sin Tailscale)
|
|
|
|
```bash
|
|
dig +short unasgeorge.vaquita-hadar.ts.net A @1.1.1.1
|
|
dig +short unasgeorge.vaquita-hadar.ts.net A @8.8.8.8
|
|
```
|
|
|
|
Esperado (IPs de los servidores de entrada de Funnel, pueden variar):
|
|
|
|
```
|
|
209.177.145.97
|
|
209.177.145.192
|
|
```
|
|
|
|
> Si sale vacío o `No answer`, el registro DNS público aún no se ha creado. Puede tardar desde 10 minutos hasta unas horas. No hay que cambiar nada, solo esperar.
|
|
|
|
### 4.3 Desde un equipo o servidor **sin Tailscale**
|
|
|
|
En el Mac: apagar Tailscale (icono de la barra de menú) y limpiar caché DNS:
|
|
|
|
```bash
|
|
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
|
|
```
|
|
|
|
En Linux (si usa systemd-resolved):
|
|
|
|
```bash
|
|
sudo resolvectl flush-caches
|
|
```
|
|
|
|
En Windows (PowerShell):
|
|
|
|
```powershell
|
|
ipconfig /flushdns
|
|
```
|
|
|
|
Comprobaciones (válidas en macOS, Linux y Windows con curl/git):
|
|
|
|
```bash
|
|
# 1. Resuelve por DNS público
|
|
nslookup unasgeorge.vaquita-hadar.ts.net 8.8.8.8
|
|
|
|
# 2. Responde por HTTPS
|
|
curl -I https://unasgeorge.vaquita-hadar.ts.net
|
|
# Esperado: HTTP/2 200
|
|
|
|
# 3. Git llega al repositorio (pedirá usuario y token si es privado)
|
|
git ls-remote https://unasgeorge.vaquita-hadar.ts.net/george/sap-legacy-webhook-emulator.git
|
|
```
|
|
|
|
### 4.4 Prueba desde una red totalmente externa
|
|
|
|
En el iPhone (o cualquier móvil): desactivar Wi-Fi, desactivar Tailscale y abrir en el navegador:
|
|
|
|
```
|
|
https://unasgeorge.vaquita-hadar.ts.net
|
|
```
|
|
|
|
Debe cargar la página de Gitea.
|
|
|
|
### 4.5 URLs de comprobación
|
|
|
|
| Qué | URL |
|
|
|---|---|
|
|
| Gitea (pública) | <https://unasgeorge.vaquita-hadar.ts.net> |
|
|
| Repo de ejemplo | <https://unasgeorge.vaquita-hadar.ts.net/george/sap-legacy-webhook-emulator> |
|
|
| Máquinas Tailscale (etiqueta Funnel) | <https://login.tailscale.com/admin/machines> |
|
|
| Política de acceso (nodeAttrs funnel) | <https://login.tailscale.com/admin/acls> |
|
|
| DNS / certificados HTTPS | <https://login.tailscale.com/admin/dns> |
|
|
|
|
---
|
|
|
|
## 5. Cambiar el `origin` de los proyectos
|
|
|
|
### 5.1 Un proyecto
|
|
|
|
```bash
|
|
cd ruta/al/proyecto
|
|
|
|
# Ver el remoto actual
|
|
git remote -v
|
|
|
|
# Cambiarlo
|
|
git remote set-url origin https://unasgeorge.vaquita-hadar.ts.net/george/<repo>.git
|
|
|
|
# Verificar
|
|
git remote -v
|
|
git ls-remote origin
|
|
git push
|
|
```
|
|
|
|
Credenciales en el primer `push`:
|
|
|
|
- **Username:** `george`
|
|
- **Password:** el **token** (al pegarlo no se ve nada, es normal)
|
|
|
|
### 5.2 Varios proyectos a la vez (macOS / Linux)
|
|
|
|
Desde la carpeta que contiene todos los proyectos. Sustituye cualquiera de las URLs antiguas (IP Tailscale, IP local o `ugdocker.link`) por la nueva:
|
|
|
|
```bash
|
|
for d in */; do
|
|
[ -d "$d/.git" ] || continue
|
|
old=$(git -C "$d" remote get-url origin 2>/dev/null) || continue
|
|
new=$(echo "$old" | sed -E 's#^https?://(100\.68\.242\.27|192\.168\.1\.145|app-30000-unasgeorge\.us15\.ugdocker\.link)(:30000)?#https://unasgeorge.vaquita-hadar.ts.net#')
|
|
if [ "$old" != "$new" ]; then
|
|
git -C "$d" remote set-url origin "$new"
|
|
echo "$d $old -> $new"
|
|
fi
|
|
done
|
|
```
|
|
|
|
### 5.3 Varios proyectos a la vez (Windows PowerShell)
|
|
|
|
```powershell
|
|
Get-ChildItem -Directory | ForEach-Object {
|
|
if (Test-Path "$($_.FullName)\.git") {
|
|
$old = git -C $_.FullName remote get-url origin 2>$null
|
|
if ($old) {
|
|
$new = $old -replace '^https?://(100\.68\.242\.27|192\.168\.1\.145|app-30000-unasgeorge\.us15\.ugdocker\.link)(:30000)?', 'https://unasgeorge.vaquita-hadar.ts.net'
|
|
if ($old -ne $new) {
|
|
git -C $_.FullName remote set-url origin $new
|
|
Write-Host "$($_.Name) $old -> $new"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5.4 Guardar el token para no escribirlo cada vez
|
|
|
|
| Sistema | Comando / comportamiento |
|
|
|---|---|
|
|
| macOS | Se guarda solo en el Llavero (`git config --global credential.helper osxkeychain`) |
|
|
| Windows | Git Credential Manager lo guarda automáticamente |
|
|
| Linux | `git config --global credential.helper store` (guarda en texto plano en `~/.git-credentials`) |
|
|
|
|
### 5.5 Clonar en un equipo nuevo
|
|
|
|
```bash
|
|
git clone https://unasgeorge.vaquita-hadar.ts.net/george/<repo>.git
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Seguridad (Gitea queda público en internet)
|
|
|
|
- [ ] Registro desactivado (`DISABLE_REGISTRATION = true`, verificado en variables de entorno **y** `app.ini`).
|
|
- [ ] 2FA activado: **Configuración → Seguridad**.
|
|
- [ ] Repositorios privados marcados como **privados**.
|
|
- [ ] Un token por equipo; revocar el de un equipo perdido en **Configuración → Aplicaciones**.
|
|
- [ ] Revisar que no exista otra variable `GITEA__...` que anule estos ajustes.
|
|
|
|
---
|
|
|
|
## 7. Problemas encontrados y solución
|
|
|
|
| Síntoma | Causa | Solución |
|
|
|---|---|---|
|
|
| `ssh` y `ping` a `100.68.242.27` hacen timeout | Esa IP es del contenedor de Tailscale, no del sistema del NAS | Usar la terminal del contenedor desde la app Docker de UGOS |
|
|
| `Funnel is not enabled on your tailnet` | Falta permiso Funnel | Abrir el enlace que muestra el comando y aceptar |
|
|
| `port 443 is not allowed for funnel` | El nodo aún no tiene el atributo `funnel` | Revisar `nodeAttrs` (paso 1.2) y reiniciar el contenedor de Tailscale |
|
|
| HTTPS conecta pero no responde (`Request completely sent off` y nada más) | Funnel apuntaba a un destino inalcanzable (`:3000` o `192.168.1.145:30000`) | `tailscale serve reset` y Funnel a `http://172.17.0.1:30000` |
|
|
| `Could not resolve host` sin Tailscale / `dig` vacío | DNS público de Funnel aún no publicado | Esperar (minutos a horas) y repetir `dig` |
|
|
| Clone muestra `http://100.68.242.27:30000/...` o `ugdocker.link` | Variable `GITEA__server__ROOT_URL` antigua anulaba `app.ini` | Cambiar la variable y añadir `PUBLIC_URL_DETECTION = never` |
|
|
| Aviso *detected web site URL ... unlikely matching* | Se entra por una dirección distinta de `ROOT_URL` | Informativo; entrar por la URL pública |
|