Skip to content
ValeraGinPublic

About

AnyConnect-compatible VPN client for macOS on OpenConnect: SAML SSO with autofill, split DNS, split tunneling, menu bar app

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

ocbar

ocbar

Website

English · Русский

Your work VPN, in your Mac’s menu bar.

An open-source, Cisco AnyConnect–compatible client built on OpenConnect. Sign in with SSO, choose which networks go through the tunnel, and pause or resume from a native macOS menu. The supervisor reconnects after sleep and network changes.

Free under the MIT license. Install through Homebrew, which builds the app on your Mac. Requires macOS 14.4+ and access to your own compatible VPN gateway; ocbar does not provide VPN servers.

ocbar menu: connect, toggle a network, pause

The real menu on a demo profile: connect, switch off a network, pause.

Status: a single-author tool used daily on a real AnyConnect gateway with a Keycloak identity provider. The app, the sign-in windows and the CLI support English and Russian, selectable in Settings; the longer guides, INSTALL.md and TROUBLESHOOTING.md, are in Russian; the first-connection problems are also in TROUBLESHOOTING.en.md. Version history is in CHANGELOG.md.

Not affiliated with Cisco. "AnyConnect" is used only to describe protocol compatibility.

Quick install

Open Terminal and run:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/ValeraGin/ocbar/main/install.sh)"

The installer offers to install missing Apple Command Line Tools and Homebrew, installs ocbar and its helper, and opens the menu app. It asks for administrator access when needed. Share one .ocbar file and the profile installation guide with a teammate; its command installs the app and imports their profile, including embedded CSD. Choose English, Russian or System default in Settings → General → Interface language.

Why

OpenConnect speaks the AnyConnect protocol, but on macOS the gaps are everything around it: gateways that only allow SAML login, a vpnc-script that routes all traffic and rewrites DNS, no reconnect after sleep, and no menu bar UI. ocbar fills those gaps without a kernel extension, a root daemon, or an Apple Developer ID.

How it works

ocbar connect
  │
  ├─ ocbar-auth (Swift, WKWebView)        runs as the user
  │    init → SAML window → cookie → auth-reply → session token + server cert hash
  │
  └─ sudo -n ocbar-helper tunnel-start    root, the only NOPASSWD entry
       ├─ openconnect --cookie-on-stdin --script "ocbar-helper vpnc" -b
       └─ vpnc: ifconfig utunN, our routes, our /etc/resolver zones — nothing else

ocbar supervise (LaunchAgent)             runs as the user, every 5 s
       tunnel pid · kern.waketime · primary interface · routes in place

Privileged code lives in one file, libexec/ocbar-helper: fixed subcommands, every argument validated, state only in root-owned directories, and it only touches what it created itself. See SECURITY.md for the threat model.

Features

  • SSO login (single-sign-on-v2) in a WebKit window: the window stays hidden while a saved identity-provider session is enough, and appears only when a human is needed.
  • Autofill with per-profile rules: username, password, TOTP, buttons, multi-step forms. Rules are recorded by clicking the form (ocbar learn) or by logging in once with "Remember how I sign in" (ocbar connect --teach). Filling happens only on HTTPS hosts of the login chain started by the gateway.
  • Secrets from the macOS Keychain, KeePassXC (keepassxc-cli) or any command (e.g. op item get … --otp). TOTP secrets can be imported from otpauth:// and Google Authenticator export QR codes, including via the Mac camera. Secrets never appear in process arguments.
  • Split tunneling and split DNS: only listed networks go through the tunnel; DNS zones are added as /etc/resolver files. Both can be toggled on a live connection.
  • Pause: remove routes and zones without dropping the session, resume instantly without logging in again (⌥⌘P).
  • Supervisor: reconnects after sleep, network changes and dropped links; stops a connection attempt that hangs for 90 seconds and tries again; never opens a login window on its own; caps logins per hour.
  • Proxy mode: openconnect --script-tun + ocproxy gives a local SOCKS proxy with no routes, no DNS changes and no root at all.
  • English and Russian: the app, the sign-in windows and CLI messages follow the system language.
  • Menu bar app: a native macOS menu — "Connect VPN" / "Disconnect VPN" on top, profiles as rows (the connected one shows its state and session time, or the connection step while connecting; click another to connect it), networks and DNS zones as checkmark submenus, pause and the usual commands below. The menu bar icon fills step by step while connecting. Hold ⌥ while opening the menu for connection details: tunnel address, gateway, MTU, resolvers, speed, traffic, latency. Settings hold the profile editor, notification settings and diagnostics with logs; a first-run wizard walks through system component → profile → first login.

Try it without a VPN

The interface runs on synthetic data, so you can look before installing anything that touches your network:

ocbar ui-lab                                              # menu states in a browser page
app/.build/ocbar.app/Contents/MacOS/ocbar-app --stage     # the real menu, every state at once

Install

Requires macOS 14.4+ and Command Line Tools (full Xcode is not needed).

brew tap ValeraGin/ocbar
brew trust ValeraGin/ocbar            # third-party tap: Homebrew refuses untrusted formulae
brew install ocbar                    # builds from source on your machine
sudo ocbar install                    # once: helper, sudoers rule, LaunchAgent

mkdir -p ~/.config/ocbar/profiles
cp "$(brew --prefix ocbar)"/share/ocbar/examples/example.ocbar ~/.config/ocbar/profiles/main.ocbar
$EDITOR ~/.config/ocbar/profiles/main.ocbar

ocbar secret set-password
ocbar connect --show
ocbar app start && ocbar app autostart on

Why a source build: without a Developer ID a prebuilt app is quarantined by Gatekeeper, while a locally built one is not. brew upgrade ocbar updates it; when a release changes the privileged helper, run sudo ocbar install once more (ocbar doctor says when the helper is behind). Uninstall in this order: sudo ocbar uninstall, then brew uninstall ocbar — otherwise the root helper and its sudoers rule stay behind. Details (in Russian): INSTALL.md.

Profile

Profile window

Notification settings

One file per connection, similar to WireGuard configs: ~/.config/ocbar/profiles/<name>.ocbar. No secrets inside — only references to where they are stored, so a profile can be shared.

[Connection]
Name = Main
Url  = vpn.example.com/employees
User = alice

[Routes]
10.0.0.0/8
172.16.0.0/12

[DNS]
example.com      = 10.0.0.1
corp.example.com = vpn          # "vpn" = resolver pushed by the gateway

[Auth]
Password = keychain             # auto | keychain | keepassxc | command | ask
Totp     = keepassxc            # auto | keychain | keepassxc | command | sms | off
KeepassEntry = Group/Entry

[Health]
Check = wiki.example.com:443    # what must answer after connecting

[Autofill]
# step 1 — idp.example.com/login
fill  password input[id=password]
click button[type=submit]
# step 2 — idp.example.com/otp
fill  totp input[id=otp]
click input[id=kc-login]

Proxy mode instead of a tunnel:

[Connection]
Mode = proxy

[Proxy]
Port        = 11080
SystemProxy = off     # on — set this SOCKS on the active network service
brew install ocproxy
ocbar connect
curl --socks5-hostname 127.0.0.1:11080 https://wiki.example.com

A full annotated example: etc/example.ocbar.

External HostScan script

For gateways that request a device report, set your script in the profile:

[Connection]
CsdWrapper = ~/vpn/csd-post.sh

Use a wrapper approved for your organization's device policy. The bundled examples/csd-wrapper.sh contains placeholder device report values; it is a protocol example, not a compliance checker or a ready corporate configuration. Do not deploy its default report as a device assessment. Make your approved script executable with chmod u+x "$HOME/vpn/csd-post.sh". Paths with spaces work; do not put quotes around the value in the profile. The script runs as your user before SSO, using OpenConnect's wrapper arguments and CSD_HOSTNAME, CSD_TOKEN, CSD_SHA256 environment variables. It submits the prepared report itself; stdout is diagnostic output, not a report for ocbar to submit. ocbar waits for the gateway, then resumes SSO. A required HostScan check without a configured wrapper stops before SSO. Missing files, execution errors and non-zero exit codes also stop sign-in. Whether the report satisfies the device policy is decided by the gateway.

Embed CSD in a portable profile

In the profile editor's advanced settings, choose Embed CSD script…. The file is stored in [Connection] CsdScriptBase64, as one line of Base64. The editor and ocbar export preserve it, so a colleague needs just one .ocbar. Use a UTF-8 script starting with #!, up to 256 KiB. Choose either CsdWrapper or CsdScriptBase64.

Import extracts the script into ~/.config/ocbar/csd/; sudo ocbar install prepares its system copy. The installer handles both steps with --profile alone, without --csd. Reading, validating and importing never run the script. After changing the embedded script, rerun sudo ocbar install to update its system copy.

Connecting through an upstream proxy

Set [Connection] UpstreamProxy = http://127.0.0.1:10808 or socks5://127.0.0.1:10808 to send password login, SSO and VPN transport through one proxy. Both Mode = tunnel and Mode = proxy support it. [Proxy] still configures the local SOCKS endpoint exposed to applications after VPN login.

HTTP CONNECT and SOCKS5 without authentication are supported, with an explicit port (1–65535) and DNS hostname or IPv4 address. Credentials, paths and IPv6 literals are rejected. Native tunnels require helper 0.8.3+ (sudo ocbar install). It applies to password login, SSO and form learning. Explicit upstream always disables DTLS: transport uses TCP. Proxy failure does not fall back to a direct connection to the external gateway. WebKit/URLSession exclude loopback destinations from proxying; this setting is not intended for SSO gateways running on localhost.

CSD wrappers receive http_proxy, https_proxy, all_proxy and uppercase variants, with NO_PROXY cleared. Custom scripts must honor these variables; ocbar does not intercept arbitrary sockets opened by a script. The proxy must already be running. An empty setting preserves existing connection behavior.

Commands

ocbar connect [profile] [--show] [--teach] [-v]   log in and bring the tunnel up
ocbar disconnect
ocbar logout                          disconnect and forget identity-provider sessions
ocbar status [--short|--json]
ocbar profiles | import <file> | export <profile> [file] | order [name…]
ocbar routes on|off|toggle <CIDR>     also: apply | clear | status
ocbar dns on|off|toggle <zone>        also: apply | clear | status
ocbar autoconnect [manual|resume|always <profile>] who brings the tunnel up
ocbar report [file] [--raw]           support report; hosts and logins masked
ocbar logs [-f] [-n N] [source…]      all logs merged by time (supervisor, openconnect, auth, app)
ocbar profile-check <file>            will the client accept this profile file (the editor asks before saving)
ocbar state [--json]                  runtime state in one place: profile, auto-connect, access, flags
ocbar pause | resume | toggle
ocbar secret set-password|set-totp|status|code [profile]
ocbar secret add-totp <profile> --screen|--camera|--stdin   add the TOTP secret from a QR on screen, the camera or an otpauth:// link
ocbar secret import-qr <image> [--list] [--select NAME] [profile]
ocbar learn [profile] [--probe] [--out <file>]    record login form rules by clicking
ocbar rules show|import <file>|clear [profile]
ocbar app start|stop|status | autostart on|off | devmode on|off
ocbar supervisor status|start|stop|restart
ocbar doctor                          diagnostics, changes nothing: parts, leftovers, stray sessions, routes
ocbar cleanup                         remove leftovers after a crash (only ours)
ocbar selftest
sudo ocbar install [--trust] | uninstall
--dry-run                             print privileged commands instead of running them

Automation

There is no Shortcuts action yet, but the CLI is the automation interface — use "Run Shell Script" in Shortcuts, or call it from anything else:

ocbar connect work && ocbar status --short   # key=value lines, easy to parse
ocbar status --json                          # the same state as JSON (what the app reads)
ocbar pause; ocbar resume
ocbar autoconnect manual                     # stop the supervisor from logging in

Coming from Cisco Secure Client? ocbar import profile.xml reads the server list from an AnyConnect XML profile and creates one .ocbar per server (addresses only — routes, DNS and credentials stay yours to set).

Self-tests

None of them needs a network or privileges: configs are synthetic, state and logs go to a temporary directory, the helper is replaced by a stub.

ocbar selftest                  # CLI: profile parsing, rejections, cleanup, notifications
tools/helper-selftest.sh        # privileged helper on --dry-run with stubs
ocbar-auth --selftest           # TOTP (RFC 6238), XML, fill scope, limits, cookies
ocbar-auth --learn-selftest     # form recording and autofill on sample pages
ocbar-app --selftest            # profile read/write parity with the CLI, UI state
tools/i18n-scan.py --check      # every string a person sees has its English

They run in CI on every push.

Supported systems

Status
macOS 26, Apple Silicon used daily by the author; CI builds and runs every self-test
macOS 15, Apple Silicon weekly CI build and self-tests; no real-device VPN verification
macOS 14.4, Apple Silicon deployment target; no real-device verification
Intel Macs expected to work; Homebrew prefix is /usr/local, which the helper checks for ownership. Not verified

Tried ocbar with your gateway? Tell whether it worked — one short report per gateway and sign-in method fills the compatibility table.

No telemetry, crash reporting or analytics. ocbar talks to your gateway and identity provider; configured resource checks contact the resource you choose. Logs stay under ~/Library/Logs/ocbar. Installing and updating downloads code from GitHub and Homebrew; the app has no automatic update check.

Limitations

  • Password groups (Auth = password: login, password and an SMS code) need a human for the code: ocbar fills in the password and asks for the SMS code in a small window, so they never reconnect silently.
  • Full tunnel is configured as 0.0.0.0/1 + 128.0.0.0/1; there is no "route everything and rewrite system DNS" mode.
  • IPv6 inside the tunnel: the address is set, routes are not.
  • The Homebrew-installed shared libraries of openconnect stay in a user-writable prefix; the helper verifies their hashes, which catches accidental changes but not a targeted local attacker. See SECURITY.md.

License

MIT — see LICENSE. Author: Valery Ignatkovich (@ValeraGin).

Profile workflow API

ocbar import --json <file> [name] returns {"profiles":["actual-id",…]} on stdout; import notices go to stderr. Cisco XML may create several profiles. Applications must use these IDs rather than the input filename.

ocbar csd-status [profile] returns required, ready and the system script name as JSON without installing or executing CSD. Missing or changed system copies block connection before sign-in. Run sudo ocbar install and check again. External wrappers use content-based names; an older basename is accepted only when its contents match. ocbar check-access [profile] [URL] checks a work resource once and returns {"reachable":true|false|null} without changing the connection or the supervisor’s state. An optional URL is used only for that check.

About

AnyConnect-compatible VPN client for macOS on OpenConnect: SAML SSO with autofill, split DNS, split tunneling, menu bar app

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages