Skip to content

arastu/morti

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

morti

morti

A clean OpenConnect wrapper for Fortinet VPN on macOS.
morti handles login, TOTP generation, reconnects, monitoring, and disconnect flow without the usual manual steps.

macOS Shell License


Why morti

  • Connect to a Fortinet VPN through OpenConnect
  • Generate TOTP codes automatically from your secret
  • Read passwords and TOTP secrets from Apple Keychain or 1Password
  • Retry failed connections and monitor the tunnel in the background
  • Show clear status, logs, and notifications
  • Keep the workflow down to explicit commands: connect, disconnect, status

Quick Start

Install:

curl -fsSL https://raw.githubusercontent.com/arastu/morti/main/install.sh | bash

Then:

nano ~/.morti.conf
source ~/.zshrc
morti connect

Commands

Command Description
morti connect Connect and start the background monitor
morti disconnect Disconnect and stop the monitor
morti reconnect Disconnect and connect again cleanly
morti status Show VPN and monitor state
morti debug Run OpenConnect in the foreground with verbose output
morti logs Tail ~/.morti.log
morti setup Create a fresh ~/.morti.conf
morti help Show the built-in help card

Install

One-line install

curl -fsSL https://raw.githubusercontent.com/arastu/morti/main/install.sh | bash

From a clone

git clone https://github.com/arastu/morti.git
cd morti
bash install.sh

Manual install

brew install openconnect terminal-notifier oath-toolkit
cp morti.sh ~/.morti.sh
cp morti.conf.example ~/.morti.conf
chmod 600 ~/.morti.conf
echo 'source ~/.morti.sh' >> ~/.zshrc
source ~/.zshrc

Configuration

Your config file lives at ~/.morti.conf.

# Required
MORTI_SERVER="vpn.yourcompany.com"
MORTI_USERNAME="your.username"
MORTI_PASSWORD="your-password"
# MORTI_PASSWORD_CMD="security find-generic-password -w -a your.username -s morti-vpn-password"
# MORTI_PASSWORD_CMD="op item get 'Fortinet VPN' --fields label=password"

# TOTP: choose one
MORTI_TOTP_SECRET="BASE32SECRETHERE"
# MORTI_TOTP_SECRET_CMD="security find-generic-password -w -a your.username -s morti-vpn-totp-secret"
# MORTI_TOTP_SECRET_CMD="op item get 'Fortinet VPN' --fields label=totp_secret"
# MORTI_TOTP_CMD="totp-cli p account"
# MORTI_TOTP_CMD="op item get 'Fortinet VPN' --otp"

More options are documented in morti.conf.example, including:

  • MORTI_SERVERCERT
  • MORTI_REALM
  • MORTI_PING_HOST
  • MORTI_EXTRA_ARGS

Apple Keychain

You can keep secrets out of ~/.morti.conf and fetch them at runtime:

security add-generic-password -U -a your.username -s morti-vpn-password -w 'your-password'
security add-generic-password -U -a your.username -s morti-vpn-totp-secret -w 'BASE32SECRETHERE'

Then in ~/.morti.conf:

MORTI_PASSWORD_CMD="security find-generic-password -w -a your.username -s morti-vpn-password"
MORTI_TOTP_SECRET_CMD="security find-generic-password -w -a your.username -s morti-vpn-totp-secret"

1Password

If you use the op CLI:

MORTI_PASSWORD_CMD="op item get 'Fortinet VPN' --fields label=password"
MORTI_TOTP_CMD="op item get 'Fortinet VPN' --otp"

Passwordless sudo

OpenConnect needs root privileges to create and manage the tunnel. To avoid repeated password prompts:

sudo visudo -f /etc/sudoers.d/openconnect

Add:

yourusername ALL=(ALL) NOPASSWD: /opt/homebrew/bin/openconnect
yourusername ALL=(ALL) NOPASSWD: /bin/kill

How It Works

morti connect
  -> load ~/.morti.conf
  -> generate a fresh OTP
  -> clean up any morti-managed session
  -> start openconnect
  -> verify tunnel health
  -> start background monitor

If the tunnel drops, the monitor retries the connection automatically.


Environment Variables

Set these before source ~/.morti.sh if you want to override defaults:

Variable Default Purpose
MORTI_CONFIG_FILE ~/.morti.conf Config file path
MORTI_LOG_FILE ~/.morti.log Log file path
MORTI_MAX_RETRIES 5 Retry count
MORTI_RETRY_DELAY 3 Delay between retries
MORTI_MONITOR_INTERVAL 30 Health check interval
MORTI_OPENCONNECT_PATH /opt/homebrew/bin/openconnect OpenConnect binary path
MORTI_PASSWORD_CMD unset Command used to fetch the VPN password
MORTI_TOTP_SECRET_CMD unset Command used to fetch the raw TOTP secret
MORTI_TOTP_CMD unset Command used to fetch the current OTP directly

Troubleshooting

Config file not found

Run:

morti setup

or:

cp morti.conf.example ~/.morti.conf

Failed to generate OTP

Make sure oath-toolkit is installed, or verify your MORTI_TOTP_CMD.

Connection keeps failing

Run:

morti debug

If needed, try MORTI_EXTRA_ARGS="--no-dtls" or pin the server certificate with MORTI_SERVERCERT.

Tunnel is up but traffic is broken

Set MORTI_PING_HOST to an internal reachable IP so the monitor can detect a dead tunnel.

morti disconnect does not stop the monitor

Check:

morti status
rm -f ~/.morti.monitor.pid

Uninstall

curl -fsSL https://raw.githubusercontent.com/arastu/morti/main/uninstall.sh | bash

Or:

bash uninstall.sh

License

MIT

About

Forticlient VPN manager for macOS, auto-reconnects, generates TOTP, runs in the background, supports Apple Passwords and 1Password.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages