12 KiB
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
- Entrar a https://login.tailscale.com/admin/dns.
- En HTTPS Certificates, pulsar Enable HTTPS.
1.2 Permitir Funnel en la política de acceso
- Entrar a https://login.tailscale.com/admin/acls.
- Verificar que exista este bloque (añadirlo si no está, respetando las comas):
"nodeAttrs": [
{
"target": ["autogroup:member"],
"attr": ["funnel"]
}
]
- 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
- Entrar al panel del NAS (UGOS) → app Docker → Contenedores.
- Abrir el contenedor de Tailscale → Terminal → añadir terminal.
- 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
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.
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:
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
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
tailscale funnel status
Debe mostrar un solo destino:
https://unasgeorge.vaquita-hadar.ts.net (Funnel on)
|-- / proxy http://172.17.0.1:30000
--bgdeja 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.
- App Docker de UGOS → contenedor de Gitea → Detener.
- Editar → Variables de entorno.
- 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
- 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.
[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 = neverhace que Gitea use siempreROOT_URLpara los enlaces, aunque se entre por otra dirección (IP de Tailscale,ugdocker.link, etc.).SSH_DOMAINapunta 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 porhttps://unasgeorge.vaquita-hadar.ts.net.
3.4 Crear un token de acceso
Git por HTTPS usa el token como contraseña.
- Gitea → avatar → Configuración → Aplicaciones → Administrar tokens de acceso.
- Nombre: uno por equipo (ej.
macbook-air,serfactpro). - Permisos: repository → Read and Write (y user → Read si lo pide).
- 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
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)
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:
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
En Linux (si usa systemd-resolved):
sudo resolvectl flush-caches
En Windows (PowerShell):
ipconfig /flushdns
Comprobaciones (válidas en macOS, Linux y Windows con curl/git):
# 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
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:
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)
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
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 yapp.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 |