Scriptable configuration with BssCfg and PowerShell

Bitvise SSH Server comes with a textual configuration utility, BssCfg, which is useful for administering SSH servers in large-scale installations. It also comes with a configuration COM object, BssCfgManip, which can be used to configure the SSH Server from any language that supports COM, but is especially intended for use with PowerShell.

Using BssCfg and PowerShell, it is possible to administer the SSH Server remotely from client machines where it is not possible to use Bitvise SSH Client and its Remote Control Panel feature. PowerShell scripts that modify SSH Server settings can be run on the server by a client through an SSH terminal shell or using exec requests.

Note: Everything that can be configured through BssCfg and PowerShell can also be configured through the graphical SSH Server Control Panel. Users who don't need scriptable configuration do not need to learn BssCfg or PowerShell. Such users should simply use the graphical settings, accessed through the Bitvise SSH Server Control Panel.

Main changes from versions 8.xx

Using BssCfg

The BssCfg utility resides in the SSH Server installation directory and can be run from a Windows Command Prompt or PowerShell. It can be run either locally or remotely using an SSH session. If run without parameters, it will show available commands and options.

BssCfg must be run by a user with administrative permissions, in an elevated Command Prompt or PowerShell window.

Configuring the SSH Server with PowerShell

To instantiate the SSH Server's configuration COM object in PowerShell, use:

$cfg = new-object -com "Bitvise.BssCfg"

Note:

  • Like the BssCfg utility, the BssCfgManip COM object must be instantiated by a user with administrative permissions, in an elevated PowerShell window.

  • If more than one SSH Server version is installed, more than one BssCfgManip COM object version will exist concurrently. In this case, instantiating "Bitvise.BssCfg" will access the newest installed version. To discover the name of the object for a previous version, enumerate $cfg.instances.entries and check the member cfgObjName.

Once the object is instantiated, you can view its properties and methods:

$cfg | Get-Member

Statically-typed languages (C#, .NET)

We suggest using BssCfg with a dynamic language, such as PowerShell or VBScript. PowerShell is slower than statically-typed languages, but your scripts will require fewer changes when updating to future SSH Server versions.

To use BssCfg in a static .NET language, such as C#, add a reference to BssCfgManip.exe, which is found in the SSH Server installation directory. If you are using Visual Studio, you can use Add Reference > Browse > navigate to the SSH Server installation directory > BssCfgManip.exe. This will add BssCfg915Lib to your project.

Using BssCfg in a static language will bind tightly to a specific version of BssCfg interfaces. We maintain stable interface versions over minor releases, but new feature releases require new interface versions. To update to future releases with new BssCfg interface versions, you will need to update and recompile your application. Even if you don't require new features, you will eventually want to upgrade to continue receiving security and reliability fixes.

Dynamic languages, such as PowerShell, do not bind tightly to interfaces, and require less maintenance when updating to newer SSH Server versions.

Reference

Every object in the hierarchy features integrated help. You can display it as follows:

$cfg.help $cfg.instances.help $cfg.instances.entries[0].help $cfg.instanceSettings.help $cfg.settings.help $cfg.keypairs.help $cfg.keypairs.entries[0].help

You can find built-in help at all levels in the settings hierarchy. For example:

$cfg.settings.access.help $cfg.settings.access.virtAccounts.help $cfg.settings.access.virtAccounts.new.help $cfg.settings.access.virtAccounts.new.auth.help $cfg.settings.access.virtAccounts.new.auth.keys.help

The same help text has also been compiled, and can be viewed as HTML:

Stop on errors

If your script encounters an error when calling a method such as settings.Lock, it is important the script stops instead of continuing as if no error happened. The reason locking functions must be called is because they can fail: aspects of SSH Server settings can be briefly locked by another process, and it's not safe to try to alter settings in this case.

To make sure your script stops if it encounters any errors, make one of the first commands:

$ErrorActionPreference = "Stop"

Alternately, your script can handle any execution errors in a Try/Catch block.

Selecting the SSH Server instance

It is possible to install multiple concurrent SSH Server instances on the same computer. There can be no more than one default, unnamed instance ("Bitvise SSH Server"). Other instances will have names ("Bitvise SSH Server - InstanceName").

If more than one instance is installed, it must be selected using $cfg.SetInstance. Examples:

# Alternatives to select the default (unnamed) instance $cfg.SetInstance("") $cfg.SetInstance("Bitvise SSH Server") # Alternatives to select the instance named "InstanceName" $cfg.SetInstance("InstanceName") $cfg.SetInstance("Bitvise SSH Server - InstanceName")

Either the short name (the empty string, or "InstanceName") or the long name (prefixed with "Bitvise SSH Server") may be provided.

If only one SSH Server instance is installed, instance selection is not required. The BssCfgManip COM object will automatically use the only installed instance.

Instances can be enumerated using $cfg.instances:

foreach ($instance in $cfg.instances.entries) { Write-Host "Instance: '$($instance.name)', version: $($instance.version)" }

Locking and loading settings

When the BssCfgManip COM object is first instantiated, neither settings nor keypairs are locked or loaded. To modify settings, you must lock and load them as follows:

$cfg.settings.Lock() try { $cfg.settings.Load() # Manipulate settings # ... $cfg.settings.Save() } finally { $cfg.settings.Unlock() }

Similarly, to modify keypairs - lock and load them as follows:

$cfg.keypairs.Lock() try { $cfg.keypairs.Load() # Manipulate keypairs # ... # $true to perform automatic backup; $false to not $cfg.keypairs.Save($true) } finally { $cfg.keypairs.Unlock() }

Master/follower and update settings are stored separately, and must be locked and loaded as follows:

$cfg.instanceSettings.Lock() try { $cfg.instanceSettings.Load() # Manipulate master/follower and update settings # ... $cfg.instanceSettings.Save() } finally { $cfg.instanceSettings.Unlock() }

If you are loading settings or keypairs only to view them; but not change them; it is not necessary to lock them.

Navigating settings

To access server settings, use the property $cfg.settings:

# Load server settings and display the textual log file directory $cfg.settings.Load() Write-Host "Log file directory: $($cfg.settings.server.logging.logFileDir)"

To access master/follower and update settings, use $cfg.instanceSettings:

# Load instance type settings and print the master server's address $cfg.instanceSettings.Load() Write-Host "Master server's address: $($cfg.instanceSettings.follower.host)"

Enumerations

All enumeration values are accessible by name via the BssCfgManip COM object. For example, the following code can be used to set the setting pwCacheAutoSave:

$cfg.settings.access.pwCacheAutoSave = $cfg.enums.pwCacheAutoSave.allAccounts

To list all available values for the pwCacheAutoSave enumeration, use:

$cfg.enums.pwCacheAutoSave

Lists

Every list is represented by a list object with an enumerable .entries property. For example, the list of virtual accounts can be accessed through virtAccounts and virtAccounts.entries. The list object (without .entries) allows manipulation of the list (add, erase, move items). The .entries property (virtAccounts.entries) provides easy, enumerable access to the items.

List object - discard all virtual accounts:

$cfg.settings.access.virtAccounts.Clear()

The .entries property - enumerate virtual accounts:

foreach ($account in $cfg.settings.access.virtAccounts.entries) { Write-Host $account.virtAccount }

Adding new accounts and groups

Accounts and groups are stored by the SSH Server in lists. Some of the fields in each list entry are the entry's sort key; when you use the help property, these fields are displayed with a ">" prefix. Other fields in each list entry may have a unique constraint; such fields will be marked with a "!" prefix in the result of help. For example, virtual accounts are sorted according to the value of their virtAccount field (">" prefix). On the other hand, values of the groupType, winDomain and group properties in Windows group settings entries must be unique ("!" prefix).

New account and group entries are added in two steps:

  1. The entry is initialized with the new settings it will contain. In this step, it is accessed using the new property of the list object. For example:

    $cfg.settings.access.winAccounts.new.winAccountType = $cfg.enums.winAccountType.domainAccount $cfg.settings.access.winAccounts.new.winDomain = "DomainName" $cfg.settings.access.winAccounts.new.winAccount = "User" $cfg.settings.access.winAccounts.new.loginAllowed = $cfg.enums.DefaultYesNo.yes $cfg.settings.access.winAccounts.new.xfer.mountPoints.Clear() $cfg.settings.access.winAccounts.new.xfer.mountPoints.new.allowUnlimitedAccess = $false $cfg.settings.access.winAccounts.new.xfer.mountPoints.new.realRootPath = "C:\Sftp\User" $cfg.settings.access.winAccounts.new.xfer.mountPoints.NewCommit() ...

  2. When the initial data for the new list entry is configured, it is committed into the list:

    $cfg.settings.access.winAccounts.NewCommit()

This process applies not only to account and group settings entries, but to all settings items stored as lists. In the above example, there is already a second nested example, where a mount point is added to xfer.mountPoints.

Searching and removing list entries

Accounts and groups can be found in lists, and removed from them, by enumerating the lists:

for ($i = 0; $i -lt $cfg.settings.access.winAccounts.count; ) { if ($cfg.settings.access.winAccounts.GetItem($i).winAccount -ieq "User") { $cfg.settings.access.winAccounts.Erase($i) } else { $i++ } }

The above will remove any and all Windows account settings entries whose Windows account name matches "User". For example, it will match and remove a domain account "Domain\User", as well as a local account "User".

Faster search and removal

Accessing COM objects using PowerShell is slow, so list enumeration can take a long time. For faster access, the SSH Server supports the methods FirstWhere, AllWhere, and EraseAll:

  • The functions FirstWhere return the first list entry which matches the provided expression.

  • The functions AllWhere return an array of list entries which match the provided expression.

  • The functions EraseAll remove all list entries which match the provided expression.

Each function comes in 10 versions: for example, FirstWhere and FirstWhere1 to FirstWhere9. The unsuffixed version, FirstWhere, accepts an expression with no variables. The remaining versions are named according to the number of variables accepted by the expression. All expression variables are specified using ? as placeholder. The values must be passed as separate parameters to avoid a security problem comparable to SQL injection.

Examples:

# Erase all Windows account settings entries where Windows account name matches "User" $cfg.settings.access.winAccounts.EraseAll1("winAccount eq ?", "User") # Enumerate virtual accounts without public keys configured foreach ($acct in $cfg.settings.access.virtAccounts.AllWhere("not any auth.keys")) { Write-Host $acct.virtAccount } # Export public keys configured for each virtual account # Also works if changed to enumerate virtAccounts.entries instead of AllWhere # AllWhere is faster if there are many virtual accounts and few have public keys $format = $cfg.enums.PublicKeyFormat.openSsh foreach ($acct in $cfg.settings.access.virtAccounts.AllWhere("any auth.keys")) { foreach ($pubkey in $acct.auth.keys.entries) { $keystr = $pubkey.ExportToBase64String($format) Write-Host "$($acct.virtAccount): $keystr" } }

Blocking IP addresses

In order to block a large number of IP addresses, you can use PowerShell, or "BssCfg settings importText", to run settings instructions such as these:

$cfg.settings.access.clientAddresses.Clear() $cfg.settings.access.clientAddresses.new.addressRule.addressType = $cfg.enums.AddressVer6Type.ipv4 $cfg.settings.access.clientAddresses.new.addressRule.ipv4 = "10.2.3.4" $cfg.settings.access.clientAddresses.new.instr.allowConnect = $false $cfg.settings.access.clientAddresses.NewCommit() $cfg.settings.access.clientAddresses.new.addressRule.addressType = $cfg.enums.AddressVer6Type.anyIP $cfg.settings.access.clientAddresses.new.instr.allowConnect = $true $cfg.settings.access.clientAddresses.NewCommit()

These instructions will:

  1. Clear the list of rules in Advanced settings > Access control > Client address rules.
  2. Add a rule to block connections from the single IPv4 address 10.2.3.4.
  3. Add a final rule to allow connections from other IP address.

Faster import

The BssCfg command-line utility implements a number of commands to access all aspects of the SSH Server's configuration. Among them are the commands BssCfg settings exportText and importText.

The command settings exportText is roughly equivalent to the following in PowerShell:

$cfg = new-object -com "Bitvise.BssCfg" $cfg.settings.Load() $cfg.settings.Dump()

The result of settings exportText is executable directly as a PowerShell script, and will set SSH Server settings to what they were at the time of export. However, the settings in this format can also be imported using BssCfg settings importText.

The command settings importText imports SSH Server settings in the textual format exported by exportText. The syntax supported by importText is very limited, and does not support most PowerShell language features. The main feature of importText is that it is much faster than direct execution of the same script in PowerShell.

The same fast-import functionality is also available using the COM object. For example:

$textSettings = $cfg.LoadText("C:\Path\To\Settings.txt") $cfg.settings.Import($textSettings)

Or:

$textSettings = $cfg.LoadText("C:\Path\To\InstanceSettings.txt") $cfg.instanceSettings.Import($textSettings)

Example scripts

The following example scripts have been renamed to .txt from their original extensions:

You can find the following scripts in your SSH Server installation directory:

  • VirtAccountExporter.ps1: A PowerShell script that exports virtual account information into a CSV text file.

  • VirtAccountImporter.ps1: A PowerShell script that imports new virtual accounts from a CSV text file.

These scripts can be used as-is, or modified according to your needs.

Main changes from versions 8.xx

All examples assume the following:

$ErrorActionPreference = "Stop"

Creating the configuration COM object:

# 8.xx: $cfg = new-object -com "BssCfg815.BssCfg815" # 9.xx: $cfg = new-object -com "Bitvise.BssCfg"

Locking and loading settings:

# 8.xx: $cfg.settings.Lock() try { $result = $cfg.settings.Load() if ($result.failure) { Write-Error $result.Describe() } ... } finally { $cfg.settings.Unlock() } # 9.xx: $cfg.settings.Lock() try { $cfg.settings.Load() ... } finally { $cfg.settings.Unlock() }

Changed names and locations of certain items:

# 8.xx: $algId = 6 # $cfg.enums.hostKeyAlgId.ed25519 $cfg.settings.session.sessionTimeout = 120 ... # 9.xx: $algId = 6 # $cfg.enums.KeypairAlgId.ed25519 $cfg.settings.connections.connectionTimeout = 120 ...

Need help?

If you run into any problems or need help with scripted configuration, feel free to contact us. Your questions will help us augment this guide so that solutions to problems can be more readily available.

Older versions

Scriptable configuration in Bitvise SSH Server versions 8.xx

Scriptable configuration in Bitvise SSH Server versions 7.xx