# Headscale

Trucos, líos y soluciones varias para cuando Headscale decide hacerse el interesante.

# Cambiar direcciones IP asignadas a los nodos en Headscale

---
### Introducción

Modificar la IP de un nodo en Headscale puede ser útil para tener un orden lógico o asignar direcciones específicas según el uso. Esto se puede hacer directamente desde la base de datos SQLite que usa Headscale.

---

### Pasos para cambiar la IP

#### 1. Parar Headscale

Con Docker:

```bash
docker-compose down
```

Con systemd:

```bash
sudo systemctl stop headscale
```

---

#### 2. Entrar en la base de datos

```bash
sudo sqlite3 /ruta/a/la/base/de/datos/db.sqlite
```

---

#### 3. Cambiar la IP de un nodo

Consulta SQL genérica:

```sql
update nodes set ipv4 = 'nueva-ipv4' where ipv4 = 'ip-actual';
```

Ejemplo concreto:

```sql
update nodes set ipv4 = '100.64.0.10' where ipv4 = '100.64.0.14';
```

---

#### 4. Salir de SQLite

```bash
.quit
```

---

#### 5. Volver a levantar Headscale

Con Docker:

```bash
docker-compose up -d
```

Con systemd:

```bash
sudo systemctl start headscale
```

---

#### 6. Actualizar los clientes

Es recomendable que los clientes se reconecten para recibir la nueva IP. Puedes apagar y encender Tailscale en cada dispositivo.

---

### Referencias

* [Discusión en GitHub sobre cambio de IP](https://github.com/juanfont/headscale/issues/1455)
* [Guía de instalación de Headscale + Headplane](https://wiki.jtrapero.eu.org/books/contenedores-instalacion/page/headscale-headplane-gui-tu-propio-servidor-tailscale-autohospedado)

# Error DNS al usar Tailscale con Headscale

---
Esto pasa cuando usas Tailscale (con servidor Headscale autohospedado) en Linux y al hacer `tailscale status` ves este tipo de error en el *health check*:

```
# Health check:
#     - running /usr/sbin/resolvconf -m 0 -x -a tailscale: Failed to resolve interface "tailscale": No such device
#     - Tailscale failed to set the DNS configuration of your device: running /usr/sbin/resolvconf -m 0 -x -a tailscale: Failed to resolve interface "tailscale": No such device
```

---

### Causa

Tailscale espera que `/etc/resolv.conf` sea un enlace simbólico a `systemd-resolved`, pero a veces no lo es. Si ese enlace no está bien, no puede gestionar la configuración DNS correctamente y da ese error.

---

### Solución

Forzar el enlace correcto:

```bash
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
sudo systemctl restart tailscaled
```

Después de eso, el error desaparece y vuelve a funcionar el estado DNS como toca.

---

### Fuente original

Esto está documentado en la propia página de Tailscale, en la sección de problemas comunes con DNS en Linux:

[Tailscale – Linux DNS](https://tailscale.com/kb/1188/linux-dns)

# Headscale v0.26 – Consideraciones tras la actualización

---
### Introducción

Notas sobre el cambio a Headscale v0.26.0 y los ajustes necesarios tras el salto a Policy v2. Este artículo recoge los dos puntos críticos que rompían la configuración previa: `base_domain` y los identificadores de usuario.

---

### Requisitos previos

* Tener ya configurado Headscale con una versión anterior funcionando.
* Usar Caddy como proxy inverso con TLS (por eso no se usan las opciones de TLS internas de Headscale).
* Haber actualizado a la versión 0.26.0 o superior.

---

### Cambios importantes en Headscale 0.26.0

#### Usuarios con `@` obligatorio en Policy v2

Con la nueva implementación de políticas (Policy v2), **todos los identificadores de usuario deben contener un `@`**. Si no lo tienen, Headscale lanza un error como este:

```
"Invalid Owner 'xxx'. An alias must be one of the following types..."
```

También puedes ver este error ejecutando:

```bash
docker logs headscale  # o el nombre del contenedor que uses
```

Ejemplo del `policy.json` antes (v1):

```json
{
  "tagOwners": {
    "tag:admin": ["usuario1"],
    "tag:srvadmin": ["usuario1"]
  },
  "acls": [
    {
      "action": "accept",
      "src": ["tag:admin"],
      "dst": ["*:*"]
    },
    {
      "action": "accept",
      "src": ["tag:srvadmin"],
      "dst": [
        "100.64.0.35:15134",
        "100.64.0.35:13515",
        "100.64.0.35:57000",
        "100.64.0.35:5134"
      ]
    }
  ]
}
```

Y después (v2):

```json
{
  "tagOwners": {
    "tag:admin": ["usuario1@midominio.local"],
    "tag:srvadmin": ["usuario1@midominio.local"]
  },
  "acls": [
    {
      "action": "accept",
      "src": ["tag:admin"],
      "dst": ["*:*"]
    },
    {
      "action": "accept",
      "src": ["tag:srvadmin"],
      "dst": [
        "100.64.0.35:15134",
        "100.64.0.35:13515",
        "100.64.0.35:57000",
        "100.64.0.35:5134"
      ]
    }
  ]
}
```

Para ver los usuarios válidos ya creados:

```bash
headscale users list
```

---

### Migración de Policy v1 a v2 (pasos seguidos)

Esto fue lo que se hizo para realizar la migración correctamente:

1. Iniciar Headscale 0.26.0 con la variable de entorno `HEADSCALE_POLICY_V1=1` o `HEADSCALE_POLICY_V1: '1'` activada.

   * Se puede confirmar que ha funcionado si aparece este mensaje al arrancar:

     ```
     Using policy manager version: 1
     ```

2. Volcar la política actual a un archivo:

   ```bash
   headscale policy get > policy.json
   ```

3. Editar `policy.json` y migrarlo manualmente al formato v2.

   * Se puede comprobar si hay errores con:

     ```bash
     headscale policy check --file policy.json
     ```

4. Cargar la política migrada:

   * Si estás usando Headscale en contenedor Docker, el archivo debe estar en una ruta accesible desde dentro del contenedor. Por ejemplo:

     ```yaml
     - './Headscale:/etc/headscale'
     ```
   * En ese caso, el comando sería:

     ```bash
     hs policy set --file /etc/headscale/policy.json
     ```

     Donde `hs` es un alias que apunta a:

     ```bash
     docker exec Headscale headscale
     ```

5. Reiniciar Headscale **sin** la variable `HEADSCALE_POLICY_V1`.

   * Si todo va bien, el mensaje será:

     ```
     Using policy manager version: 2
     ```

---

#### `base_domain` no puede coincidir con `server_url`

Otro breaking change relevante es que `base_domain` no puede ser igual (ni estar contenido) en el `server_url`. Si coinciden, **MagicDNS deja de funcionar** o se comporta de forma errática.

Ejemplo incorrecto:

```yaml
server_url: https://headscale.midominio.local
base_domain: midominio.local
```

Ejemplo correcto:

```yaml
server_url: https://headscale.midominio.local
base_domain: redprivada.lan
```

En este caso se usó un dominio inventado, corto y sin registrar: `redprivada.lan`.

---

### Errores comunes o decisiones importantes

* Si olvidaste poner `@` a los usuarios, el error no es muy claro. Hay que revisar el `policy.json` y asegurarse de que todos los identificadores lo llevan.
* Aunque el `config-example.yaml` trae bloques de Let's Encrypt, **se ignoraron** porque ya se gestiona TLS con Caddy.
* Puedes registrar un dominio personalizado si lo ves útil, pero por ahora solo se usa como base de resolución DNS local.
* La migración de v1 a v2 puede hacerse sin sobresaltos si se siguen los pasos indicados, especialmente si estás usando Docker.

---

### Resumen breve

* Todos los usuarios deben tener `@` en su identificador.
* `base_domain` no puede coincidir con `server_url`.
* MagicDNS depende de que estos dos valores estén bien puestos.
* La política ahora se valida al cargar, no en tiempo de ejecución.
* La migración a Policy v2 se puede hacer conservando primero la configuración previa.
* Si usas Docker, asegúrate de que el archivo esté en una ruta montada dentro del contenedor.

---

### Notas personales

Anotar bien estos dos cambios porque son minas antipersona: si se olvidan, te explota la configuración en la cara sin piedad.

---

### Referencias

* [Changelog oficial de Headscale 0.26.0](https://github.com/juanfont/headscale/releases/tag/v0.26.0)
* [Headscale + Headplane: tu propio servidor Tailscale autohospedado](https://wiki.jtrapero.eu.org/books/contenedores-instalacion/page/headscale-headplane-gui-tu-propio-servidor-tailscale-autohospedado)