Automatización de conexión a VPN con autenticación de dos factores (2FA) para FortiClient.
La versión C# reemplaza los time.sleep() del script Python por detección de estado
real vía Windows UI Automation (UIA) con timeouts configurables, reintentos y
logging estructurado.
src/UniVpn.Automation/
├── UniVpn.Automation.sln
├── UniVpn.Automation/ # Ejecutable Windows (.NET 8 Windows)
│ ├── Program.cs
│ └── Automation/
│ ├── WindowsUiaDetector.cs # Detección de estado vía UIA
│ ├── FortiClientAutomation.cs
│ ├── CredentialProvider.cs # Env vars / Windows Credential Manager
│ └── ICredentialProvider.cs
├── UniVpn.Automation.Core/ # Biblioteca multiplataforma (lógica pura)
│ ├── Configuration/
│ │ ├── AppConfig.cs
│ │ └── UiSelectors.cs
│ └── StateMachine/
│ ├── VpnState.cs
│ ├── VpnStateMachine.cs
│ └── IWindowDetector.cs
└── UniVpn.Automation.Tests/ # Tests unitarios (xUnit + Moq)
├── AppConfigTests.cs
└── VpnStateMachineTests.cs
config.example.json # Plantilla de configuración
- .NET 8 SDK (o superior): https://dotnet.microsoft.com/download
- Windows (el ejecutable es exclusivo de Windows; los tests se pueden ejecutar en cualquier plataforma)
- FortiClient instalado
-
Copia la plantilla:
copy config.example.json config.json
-
Edita
config.jsoncon tu ruta de FortiClient y ajusta los selectores si es necesario.
No almacenes credenciales en el archivo de configuración. -
Proporciona las credenciales mediante variables de entorno (por defecto):
$env:UNI_VPN_USERNAME = "tu_usuario" $env:UNI_VPN_PASSWORD = "tu_contraseña" # Sólo si se pide 2FA: $env:UNI_VPN_2FA_TOKEN = "123456"
O usando Windows Credential Manager (recomendado para entornos permanentes):
cmdkey /generic:UniVpn/FortiClient /user:tu_usuario /pass:tu_contraseñaY establece
"CredentialMode": "WindowsCredentialManager"enconfig.json.
Compilar:
cd src/UniVpn.Automation
dotnet build -c ReleasePublicar como ejecutable único (recomendado para distribución):
dotnet publish .\UniVpn.Automation\UniVpn.Automation.csproj -c Release -r win-x64 -o .\publish\ /p:PublishSingleFile=true /p:SelfContained=trueEl resultado será publish/UniVpn.Automation.exe.
Ejecutar:
# Modo normal (conectar)
.\UniVpn.Automation.exe
# Con configuración personalizada
.\UniVpn.Automation.exe --config "C:\ruta\config.json"
# Modo dry-run: sólo detecta y muestra el estado actual, sin enviar teclas
.\UniVpn.Automation.exe --dry-runCódigos de salida:
| Código | Significado |
|---|---|
| 0 | Éxito / conectado |
| 1 | Error de configuración |
| 2 | Conexión fallida |
| 3 | Tiempo de espera global agotado |
| 4 | Excepción no controlada |
cd src/UniVpn.Automation
dotnet test -c DebugLos tests son multiplataforma y no requieren FortiClient ni Windows.
NotRunning → FortiClient no está en ejecución o ventana no encontrada
Starting → FortiClient arrancando, esperando ventana
Disconnected → Ventana visible, desconectado / inactivo
CredentialsRequired → Campos de usuario/contraseña visibles y activos
TwoFactorRequired → Campo de token 2FA visible y activo
Connecting → Conexión en progreso
Connected → VPN conectada
Error → FortiClient muestra un mensaje de error
Timeout → Tiempo de espera agotado
- ✅ Las credenciales nunca se almacenan en el código ni en el fichero de configuración
- ✅ Se leen de variables de entorno o del Windows Credential Manager
- ✅
config.jsonestá en.gitignore - ✅ El modo
UseFallbackSendKeys(teclas simuladas) está desactivado por defecto
El script Python original se mantiene como referencia. Se recomienda usar el enfoque C# para mayor robustez.
El script automatiza el proceso de:
- Abre FortiClient VPN
- Ingresa automáticamente usuario y contraseña
- Obtiene el código 2FA desde una extensión de autenticador
- Introduce el código 2FA en FortiClient
- Completa la conexión a la VPN
pip install pyautogui pyperclip pygetwindow- FortiClient instalado en
C:\Program Files\Fortinet\FortiClient\FortiClientConsole.exe - Navegador con extensión Authenticator (Chrome/Comet)
- Python 3.7+
Copia credentials.example.json y crea credentials.json:
copy credentials.example.json credentials.jsonCompleta credentials.json:
{
"usuario": "TU_USUARIO_AQUI",
"password": "TU_CONTRASEÑA_AQUI",
"forticlient_path": "C:\\Program Files\\Fortinet\\FortiClient\\FortiClientConsole.exe"
}Importante: Este archivo está en .gitignore y nunca se subirá a GitHub.
python VPN.pyw- Usa
time.sleep()con tiempos fijos → falla si FortiClient tarda más de lo esperado - No detecta el estado real de la UI
- v2.0: Migración a C# / .NET 8 con Windows UI Automation, máquina de estados y tests
- v1.1: Implementado sistema de credenciales externas (Python)
- v1.0: Versión inicial con credenciales hardcodeadas (Python)
Este proyecto es de uso personal.
Villacus
Última actualización: Marzo 2026