The VPN client needs privileged help to reconfigure networking, and that fact gets over-applied to everything around it. A script that mounts a file share does not need root, because the share belongs to the signed-in user's session and to that user's credentials. Moving the mount out of the privileged path removes the need for sudo rules, root hooks and edits to bundled applications. What remains is a small stay-open applet that watches one profile and makes one attempt.

Why This Belongs in the User Session

A mounted volume is a property of the session that mounted it. Another user's session does not see it, and a root-owned mount behaves differently from the one Finder created. Keeping the mount in the user session preserves those semantics.

Privileged helpers exist for the parts that genuinely need them: creating a tunnel interface, changing routes, updating the resolver. Mounting an SMB share is not one of those parts. Treating it as though it were forces a choice between passwordless sudo and a root script that runs with nobody present. Both are larger risks than the problem justifies, and the user-session approach avoids the choice entirely.

Tunnelblick's own connection hooks are not a substitute here, since they run outside the desktop session and their ordering relative to the client's scripts is not defined. Polling the state from a user applet is a smaller mechanism with fewer assumptions.

Prove the Share Works by Hand First

Automation cannot supply a route, a permission or an account that does not exist. Connect the VPN manually and open the share in Finder before writing any script. If that fails, the script will fail in a more confusing way.

Record the three values the script will use: the profile name exactly as Tunnelblick lists it, the share URL, and the volume name Finder shows after mounting. The volume name matters because the check for an existing mount uses it, and a name-only check is a convenience guard rather than proof of server identity. Use a name that is unique on the machine. Then confirm the server answers at all.

smbutil view //files.corp.example
osascript -e 'tell application "Tunnelblick" to get state of first configuration whose name is "Office"'
mount | grep -i smbfs

The first command proves the server answers, the second proves the profile name resolves, and the third shows what a successful mount looks like. Capture all three before automating anything.

Read the Profile State From Tunnelblick

Tunnelblick exposes its configurations and their states over AppleScript, which is the only hook this design needs. The state you care about is the string reported for that one profile, not the presence of the application.

Polling has consequences worth stating. A reconnect shorter than the interval can pass unnoticed, and the applet never sees the transition at all. That is acceptable for a mount guard and unacceptable for anything that must react to every event. If you need event fidelity, this is the wrong mechanism rather than a mechanism that needs a shorter timer. Choose the interval with that trade-off in mind.

property attempted : false

on idle
    set profileName to "Office"
    set shareURL to "smb://files.corp.example/OfficeTeam"
    set volName to "OfficeTeam"
    tell application "Tunnelblick" to set vpnState to state of first configuration whose name is profileName
    if vpnState is not "CONNECTED" then
        set attempted to false
        return 20
    end if
    tell application "Finder" to set mounted to (exists disk volName)
    if (not mounted) and (not attempted) then
        set attempted to true
        try
            mount volume shareURL
        end try
    end if
    return 20
end idle

Export it as an application with the stay-open option, then launch it in the logged-in session rather than through sudo.

Mount Once, Then Stop Trying

The attempted flag is what keeps this from becoming a credential-guessing loop. Once the applet has tried, it waits for the connection state to change before trying again.

That behaviour is deliberate. A share that refuses the mount usually refuses it for a reason a retry cannot fix, and repeated attempts turn one prompt into a stream of them. The flag resets when the profile leaves the connected state, so a genuine reconnect produces exactly one new attempt. Quitting and relaunching the applet resets it too, which is the manual override. Neither path hides a failure; both leave it visible in Finder.

The script carries no password, and it should stay that way. Let the mount operation prompt, and let the keychain do what the organisation supports. Embedding a credential in a URL turns a convenience script into a secret that has to be rotated.

What the Helper Cannot Do

Three limits are worth writing beside the applet. It cannot eject a busy volume after a network failure, it cannot guarantee that DNS and the server are ready when the state says connected, and it does not react to every reconnect.

The first limit matters most in practice. A share whose server disappears leaves a volume that looks mounted and blocks on access, and the polite fix is to finish file operations and eject before a planned disconnect. The second explains most failed first attempts: the tunnel is up while the resolver has not caught up, so wait and then mount by hand once to confirm. The third is the polling trade-off from earlier rather than a bug to patch.

For an unattended machine or a managed fleet, this design is the wrong shape. Use an administrator-owned mounting policy instead of extending a desktop applet into a privileged service.

Barruulee wal qabatan