feat: funciones NordVPN bash y Go — CLI, contenedor Docker y parser de estado

Funciones bash para instalar, conectar, desconectar, estado, IP, ciudades, países y protocolo.
Funciones Go para gestionar contenedor NordVPN (run/start/stop) y parsear estado.
Incluye tipo NordVPNStatus y tests para el parser.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-01 20:55:08 +02:00
parent bf1efb2099
commit 2f119478af
26 changed files with 1076 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
---
name: install_nordvpn
kind: function
lang: bash
domain: infra
version: "1.0.0"
purity: impure
signature: "install_nordvpn() -> void"
description: "Instala NordVPN CLI en Ubuntu/Debian (incluido WSL2). Configura repositorio oficial, instala paquete y habilita servicio nordvpnd. Idempotente."
tags: [vpn, nordvpn, install, infra, wsl2]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/infra/install_nordvpn.sh"
---
## Ejemplo
```bash
source install_nordvpn.sh
install_nordvpn
# nordvpn ya instalado: NordVPN Version 3.x.x
# — o —
# Instalando NordVPN CLI...
# NordVPN instalado: NordVPN Version 3.x.x
# NOTA: ejecuta 'nordvpn login' para autenticarte
```
## Notas
Usa el script de instalacion oficial de NordVPN. En WSL2 sin systemd, levanta nordvpnd manualmente. Agrega el usuario al grupo nordvpn para evitar sudo en comandos posteriores. Despues de instalar, se requiere `nordvpn login` para autenticarse.
+41
View File
@@ -0,0 +1,41 @@
# install_nordvpn
# ---------------
# Instala NordVPN CLI en Ubuntu/Debian (incluido WSL2).
# Configura el repositorio oficial, instala el paquete y habilita el servicio.
# Si ya esta instalado, no hace nada.
#
# USO (sourced):
# source install_nordvpn.sh
# install_nordvpn
install_nordvpn() {
if command -v nordvpn &>/dev/null; then
echo "nordvpn ya instalado: $(nordvpn version 2>/dev/null)"
return 0
fi
echo "Instalando NordVPN CLI..."
# Descargar e instalar via script oficial
sh <(curl -sSf https://downloads.nordcdn.com/apps/linux/install.sh) 2>&1
if ! command -v nordvpn &>/dev/null; then
echo "install_nordvpn: fallo la instalacion" >&2
return 1
fi
# Agregar usuario al grupo nordvpn para evitar sudo
sudo usermod -aG nordvpn "$USER" 2>/dev/null || true
# Habilitar servicio (systemd o manual para WSL2)
if command -v systemctl &>/dev/null && systemctl is-system-running &>/dev/null 2>&1; then
sudo systemctl enable --now nordvpnd 2>/dev/null || true
else
# WSL2 sin systemd — levantar daemon manualmente
sudo nordvpnd &>/dev/null &
sleep 2
fi
echo "NordVPN instalado: $(nordvpn version 2>/dev/null)"
echo "NOTA: ejecuta 'nordvpn login' para autenticarte"
}
+40
View File
@@ -0,0 +1,40 @@
---
name: nordvpn_connect
kind: function
lang: bash
domain: infra
version: "1.0.0"
purity: impure
signature: "nordvpn_connect(country?: string, city?: string) -> json"
description: "Conecta a NordVPN por pais, ciudad o servidor especifico. Sin argumentos conecta al mejor servidor disponible. Devuelve JSON con resultado."
tags: [vpn, nordvpn, connect, infra, network]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/infra/nordvpn_connect.sh"
---
## Ejemplo
```bash
source nordvpn_connect.sh
nordvpn_connect
# {"ok":true,"server":"us1234.nordvpn.com","country":"auto","city":"auto"}
nordvpn_connect Spain
# {"ok":true,"server":"es42.nordvpn.com","country":"Spain","city":"auto"}
nordvpn_connect Spain Madrid
# {"ok":true,"server":"es15.nordvpn.com","country":"Spain","city":"Madrid"}
```
## Notas
Requiere NordVPN CLI instalado y autenticado (`nordvpn login`). La salida JSON facilita composicion con otros scripts y pipelines. Si ya hay una conexion activa, NordVPN reconecta automaticamente al nuevo destino.
+39
View File
@@ -0,0 +1,39 @@
# nordvpn_connect
# ---------------
# Conecta a NordVPN. Acepta pais, ciudad o servidor especifico.
# Sin argumentos conecta al mejor servidor disponible.
# Imprime JSON con el resultado de la conexion.
#
# USO (sourced):
# source nordvpn_connect.sh
# nordvpn_connect # mejor servidor
# nordvpn_connect Spain # por pais
# nordvpn_connect Spain Madrid # por ciudad
# nordvpn_connect Spain '#42' # servidor especifico
nordvpn_connect() {
local country="${1:-}"
local city="${2:-}"
if ! command -v nordvpn &>/dev/null; then
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
return 1
fi
local args=()
[ -n "$country" ] && args+=("$country")
[ -n "$city" ] && args+=("$city")
local output
output=$(nordvpn connect "${args[@]}" 2>&1)
local rc=$?
if [ $rc -eq 0 ] && echo "$output" | grep -qi "connected"; then
local server
server=$(echo "$output" | grep -oP '(?<=to )\S+' | head -1)
echo "{\"ok\":true,\"server\":\"${server}\",\"country\":\"${country:-auto}\",\"city\":\"${city:-auto}\"}"
else
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
return 1
fi
}
@@ -0,0 +1,34 @@
---
name: nordvpn_disconnect
kind: function
lang: bash
domain: infra
version: "1.0.0"
purity: impure
signature: "nordvpn_disconnect() -> json"
description: "Desconecta de NordVPN. Idempotente — si no hay conexion activa retorna ok. Devuelve JSON con resultado."
tags: [vpn, nordvpn, disconnect, infra, network]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/infra/nordvpn_disconnect.sh"
---
## Ejemplo
```bash
source nordvpn_disconnect.sh
nordvpn_disconnect
# {"ok":true,"status":"disconnected"}
```
## Notas
Idempotente: si no hay conexion activa, retorna ok sin error. Requiere NordVPN CLI instalado.
@@ -0,0 +1,26 @@
# nordvpn_disconnect
# ------------------
# Desconecta de NordVPN. Idempotente — si no hay conexion activa, retorna ok.
# Imprime JSON con el resultado.
#
# USO (sourced):
# source nordvpn_disconnect.sh
# nordvpn_disconnect
nordvpn_disconnect() {
if ! command -v nordvpn &>/dev/null; then
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
return 1
fi
local output
output=$(nordvpn disconnect 2>&1)
local rc=$?
if [ $rc -eq 0 ] || echo "$output" | grep -qi "not connected\|disconnected"; then
echo '{"ok":true,"status":"disconnected"}'
else
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
return 1
fi
}
+39
View File
@@ -0,0 +1,39 @@
---
name: nordvpn_get_ip
kind: function
lang: bash
domain: infra
version: "1.0.0"
purity: impure
signature: "nordvpn_get_ip() -> json"
description: "Obtiene IP publica actual con fallback entre multiples servicios. Indica si la conexion VPN esta activa y el servidor usado."
tags: [vpn, nordvpn, ip, infra, network, verification]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/infra/nordvpn_get_ip.sh"
---
## Ejemplo
```bash
source nordvpn_get_ip.sh
# Con VPN activa:
nordvpn_get_ip
# {"ok":true,"ip":"185.x.x.x","vpn_connected":true,"vpn_server":"es42.nordvpn.com","source":"https://api.ipify.org"}
# Sin VPN:
nordvpn_get_ip
# {"ok":true,"ip":"88.x.x.x","vpn_connected":false,"vpn_server":"","source":"https://api.ipify.org"}
```
## Notas
Usa ipify.org como servicio primario con fallback a ifconfig.me e icanhazip.com. Timeout de 5 segundos por servicio. Util para verificar que el tunel VPN esta activo antes de ejecutar operaciones sensibles a la IP.
+42
View File
@@ -0,0 +1,42 @@
# nordvpn_get_ip
# --------------
# Obtiene la IP publica actual para verificar que el tunel VPN funciona.
# Usa multiples servicios como fallback.
#
# USO (sourced):
# source nordvpn_get_ip.sh
# nordvpn_get_ip
nordvpn_get_ip() {
local ip=""
local source=""
# Intentar multiples servicios
for svc in "https://api.ipify.org" "https://ifconfig.me" "https://icanhazip.com"; do
ip=$(curl -s --max-time 5 "$svc" 2>/dev/null)
if echo "$ip" | grep -qP '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'; then
source="$svc"
break
fi
ip=""
done
if [ -z "$ip" ]; then
echo '{"ok":false,"error":"no se pudo obtener IP publica"}' >&2
return 1
fi
# Si nordvpn esta disponible, incluir info de conexion
local connected="false"
local vpn_server=""
if command -v nordvpn &>/dev/null; then
local status_output
status_output=$(nordvpn status 2>/dev/null)
if echo "$status_output" | grep -qi "connected"; then
connected="true"
vpn_server=$(echo "$status_output" | grep -iP "hostname|server" | head -1 | sed 's/.*: *//')
fi
fi
echo "{\"ok\":true,\"ip\":\"$ip\",\"vpn_connected\":$connected,\"vpn_server\":\"$vpn_server\",\"source\":\"$source\"}"
}
@@ -0,0 +1,37 @@
---
name: nordvpn_list_cities
kind: function
lang: bash
domain: infra
version: "1.0.0"
purity: impure
signature: "nordvpn_list_cities(country: string) -> json"
description: "Lista ciudades disponibles de un pais en NordVPN como array JSON ordenado."
tags: [vpn, nordvpn, cities, infra, network]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/infra/nordvpn_list_cities.sh"
---
## Ejemplo
```bash
source nordvpn_list_cities.sh
nordvpn_list_cities Spain
# {"ok":true,"country":"Spain","count":2,"cities":["Barcelona","Madrid"]}
nordvpn_list_cities "United_States"
# {"ok":true,"country":"United_States","count":15,"cities":["Atlanta","Buffalo",...]}
```
## Notas
El nombre de pais debe coincidir con lo que devuelve `nordvpn countries`. Usa underscores para paises compuestos (ej: United_States). Las ciudades se devuelven con espacios.
@@ -0,0 +1,38 @@
# nordvpn_list_cities
# -------------------
# Lista las ciudades disponibles de un pais en NordVPN como array JSON.
#
# USO (sourced):
# source nordvpn_list_cities.sh
# nordvpn_list_cities Spain
nordvpn_list_cities() {
local country="${1:?nordvpn_list_cities: se requiere pais como argumento}"
if ! command -v nordvpn &>/dev/null; then
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
return 1
fi
local output
output=$(nordvpn cities "$country" 2>&1)
local rc=$?
if [ $rc -ne 0 ]; then
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
return 1
fi
echo "$output" | python3 -c '
import sys, json, re
country = "'"$country"'"
text = sys.stdin.read()
text = re.sub(r"\x1b\[[0-9;]*m", "", text)
text = re.sub(r"[\t\r]", " ", text)
cities = [c.strip().replace("_", " ") for c in re.split(r"[,\n]+", text) if c.strip() and c.strip() != "-"]
cities = [c for c in cities if len(c) > 1]
cities.sort()
print(json.dumps({"ok": True, "country": country, "count": len(cities), "cities": cities}))
'
}
@@ -0,0 +1,34 @@
---
name: nordvpn_list_countries
kind: function
lang: bash
domain: infra
version: "1.0.0"
purity: impure
signature: "nordvpn_list_countries() -> json"
description: "Lista paises disponibles en NordVPN como array JSON ordenado alfabeticamente."
tags: [vpn, nordvpn, countries, infra, network]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/infra/nordvpn_list_countries.sh"
---
## Ejemplo
```bash
source nordvpn_list_countries.sh
nordvpn_list_countries
# {"ok":true,"count":60,"countries":["Albania","Argentina","Australia",...,"United States","Vietnam"]}
```
## Notas
Parsea la salida de `nordvpn countries` eliminando codigos ANSI y normalizando separadores. Los nombres de paises se devuelven con espacios en vez de underscores.
@@ -0,0 +1,36 @@
# nordvpn_list_countries
# ----------------------
# Lista los paises disponibles en NordVPN como array JSON.
#
# USO (sourced):
# source nordvpn_list_countries.sh
# nordvpn_list_countries
nordvpn_list_countries() {
if ! command -v nordvpn &>/dev/null; then
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
return 1
fi
local output
output=$(nordvpn countries 2>&1)
local rc=$?
if [ $rc -ne 0 ]; then
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
return 1
fi
echo "$output" | python3 -c '
import sys, json, re
text = sys.stdin.read()
text = re.sub(r"\x1b\[[0-9;]*m", "", text)
text = re.sub(r"[\t\r]", " ", text)
# Split by comma, whitespace, or newline and clean
countries = [c.strip().replace("_", " ") for c in re.split(r"[,\n]+", text) if c.strip() and c.strip() != "-"]
countries = [c for c in countries if len(c) > 1]
countries.sort()
print(json.dumps({"ok": True, "count": len(countries), "countries": countries}))
'
}
@@ -0,0 +1,37 @@
---
name: nordvpn_set_protocol
kind: function
lang: bash
domain: infra
version: "1.0.0"
purity: impure
signature: "nordvpn_set_protocol(protocol: string) -> json"
description: "Cambia el protocolo de NordVPN entre NordLynx (WireGuard) y OpenVPN. NordLynx recomendado por velocidad."
tags: [vpn, nordvpn, protocol, nordlynx, wireguard, openvpn, infra]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/infra/nordvpn_set_protocol.sh"
---
## Ejemplo
```bash
source nordvpn_set_protocol.sh
nordvpn_set_protocol NordLynx
# {"ok":true,"protocol":"NordLynx"}
nordvpn_set_protocol OpenVPN
# {"ok":true,"protocol":"OpenVPN"}
```
## Notas
NordLynx es WireGuard wrapeado por NordVPN — mas rapido y moderno. OpenVPN es mas compatible con redes restrictivas. El cambio de protocolo requiere reconectar si hay una conexion activa.
@@ -0,0 +1,38 @@
# nordvpn_set_protocol
# --------------------
# Cambia el protocolo de NordVPN (NordLynx o OpenVPN).
# NordLynx = WireGuard (recomendado por velocidad).
#
# USO (sourced):
# source nordvpn_set_protocol.sh
# nordvpn_set_protocol NordLynx
# nordvpn_set_protocol OpenVPN
nordvpn_set_protocol() {
local protocol="${1:?nordvpn_set_protocol: se requiere protocolo (NordLynx|OpenVPN)}"
if ! command -v nordvpn &>/dev/null; then
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
return 1
fi
case "$protocol" in
NordLynx|nordlynx|NORDLYNX) protocol="NordLynx" ;;
OpenVPN|openvpn|OPENVPN) protocol="OpenVPN" ;;
*)
echo "{\"ok\":false,\"error\":\"protocolo invalido: $protocol (usar NordLynx o OpenVPN)\"}"
return 1
;;
esac
local output
output=$(nordvpn set protocol "$protocol" 2>&1)
local rc=$?
if [ $rc -eq 0 ] || echo "$output" | grep -qi "already set\|successfully"; then
echo "{\"ok\":true,\"protocol\":\"$protocol\"}"
else
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
return 1
fi
}
+37
View File
@@ -0,0 +1,37 @@
---
name: nordvpn_status
kind: function
lang: bash
domain: infra
version: "1.0.0"
purity: impure
signature: "nordvpn_status() -> json"
description: "Obtiene estado actual de NordVPN como JSON estructurado. Incluye servidor, IP, pais, protocolo y estado de conexion."
tags: [vpn, nordvpn, status, infra, network]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: []
tested: false
tests: []
test_file_path: ""
file_path: "bash/functions/infra/nordvpn_status.sh"
---
## Ejemplo
```bash
source nordvpn_status.sh
nordvpn_status
# {"ok":true,"connected":true,"status":"Connected","hostname":"es42.nordvpn.com","ip":"185.x.x.x","country":"Spain","city":"Madrid","current_technology":"NordLynx","current_protocol":"nordlynx","transfer":"1.2 MiB received, 500 KiB sent","uptime":"5 minutes 32 seconds"}
# Desconectado:
# {"ok":true,"connected":false,"status":"Disconnected"}
```
## Notas
Parsea la salida clave-valor de `nordvpn status` eliminando codigos ANSI. Los campos disponibles dependen del estado de conexion — cuando esta desconectado solo devuelve status y connected.
+43
View File
@@ -0,0 +1,43 @@
# nordvpn_status
# --------------
# Obtiene el estado actual de NordVPN como JSON estructurado.
# Parsea la salida clave-valor de `nordvpn status` a campos JSON.
#
# USO (sourced):
# source nordvpn_status.sh
# nordvpn_status
nordvpn_status() {
if ! command -v nordvpn &>/dev/null; then
echo '{"ok":false,"error":"nordvpn no instalado"}' >&2
return 1
fi
local output
output=$(nordvpn status 2>&1)
local rc=$?
if [ $rc -ne 0 ]; then
echo "{\"ok\":false,\"error\":$(echo "$output" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read().strip()))' 2>/dev/null || echo "\"$output\"")}"
return 1
fi
# Parsear output clave: valor a JSON con python3
echo "$output" | python3 -c '
import sys, json, re
lines = sys.stdin.read().strip().split("\n")
data = {"ok": True}
for line in lines:
line = re.sub(r"\x1b\[[0-9;]*m", "", line).strip()
line = line.lstrip("- ")
if ":" in line:
key, _, val = line.partition(":")
key = key.strip().lower().replace(" ", "_")
val = val.strip()
if key == "status":
data["connected"] = val.lower() == "connected"
data[key] = val
print(json.dumps(data))
'
}