Voki configuration management.
Voki uses SSH under the hood to connect to remote machines.
Note
As of right now Voki requires an active SSH Agent to connect to machines.
Change the host in examples/target.hcl to a SSH reachable address and then run
./voki run examples/target.hcltarget "myserver" {
user = "root" // SSH username to connect to the remote host with
host = "ADDRESS:22" // Remote host to connect to
step "cmd" {
command = "echo 'hello world'"
}
step "cmd" {
command = file("hello.sh") // requires a hello.sh script to exist
}
}Voki configuration file consist of Targets and Task files.
The target configurations have the following structure:
Target "label" -> Step "action"
and Task configurations only includes:
Step "action"
Targets and tasks files can have any name desired.
Target takes the following variables to configure the SSH connection.
target "a label for the configuration" {
user = "root"
host = "address:22"
// steps go here
}-
userthe username to connect to the remote host.The user variable can also be provided via a flag
voki -u USERNAMEOr via
voki-config.hclsee the configuration section -
hostthe address including the port number separated by a colon:ipaddress:port
Steps has a label for which Action to perform for the remote host.
target "mytarget" {
user = "root"
host = "address:22"
step "cmd" {
command = "hello world"
}
}In the example above the Step takes the label "cmd" which tells voki what the Action is.
See the actions available below.
Actions are taken based on the label applied to the step as shown in the Steps section above.
Cmd runs a command or multiple commands on the configured target.
Cmd takes the following variables:
commandcommands to run on the remote host.sudoboolean value to execute commands with sudo (defaults tofalse)shellwhich shell to use, i.e.bashorsh(defaults tobash)
target "mytarget" {
user = "root"
host = "address:22"
step "cmd" {
command = "echo 'hello world'"
}
// Multiline input for multiple commands and scripts.
step "cmd" {
sudo = true
shell = "sh"
command = <<-EOT
echo "1"
echo "2"
EOT
}
}See the Inline functions for running scripts with the Cmd action.
File copies a file from the local filesystem to the remote filesystem.
File takes the following variables:
sourcethe path on the host voki is executed on.dataa string to write to the destination file. Ifsourceis set, thendatais ignored.destinationthe path on the remote host where the file should be copied to.modethe permissions on the file on the remote host.
target "mytarget" {
user = "root"
host = "address:22"
step "file" {
source = "myfile.conf"
destination = "/var/myfile.conf"
mode = "0644"
}
}it's also possible to supply data, which enables use of the inline functions file() and template()
target "mytarget" {
user = "root"
host = "address:22"
step "file" {
data = "content for the file"
destination = "/var/myfile.conf"
mode = "0644"
}
}Task is a special action that reads a separate configuration file with steps.
In the target.hcl file define the step "task" {} an use the inline function file() to read the task.hcl file.
-
target.hcltarget "mytarget" { user = "root" host = "address:22" step "task" { task = file("task.hcl") } }
-
task.hclstep "cmd" { command = "echo 'hello task'" }
A task file does not have a Target specification, only Steps.
Task files can also include nested task actions if so desired.
Load a text file such as a script and pass it without modification.
In the below example a script is loaded and executed on a target.
// target.hcl
target "myserver" {
user = "root"
host = "xyz:22"
step "cmd" {
command = file("hello.sh")
}
}and the contents of hello.sh
echo "hello world"Load a text file such as a script and pass dynamic data to be rendered before use.
The template rendering uses Go's builtin html/template.
In the below example a script has a key/value pair passed in that will be rendered before executed on a target.
// target.hcl
target "myserver" {
user = "root"
host = "xyz:22"
step "cmd" {
command = template("hello.sh.tpl", {
Name: "world!"
})
}
}in the hello.sh.tpl the Name is being passed in before execution.
echo "hello {{ .Name }}"The target file can have variables set in the following way
// target.hcl
name = "my name"
target "myserver" {
user = "root"
host = "xyz:22"
step "cmd" {
command = "echo ${name}"
}
}Configuration and environment variables can be specified via a voki-config.hcl file.
# voki-config.hcl
user="myuser"
vault-address="http://127.0.0.1:8200"
vault-token="123456"or with VOKI_ prefixed variables
VOKI_USER="me" voki run target.hclWith this set, the target section no longer requires the user variables.
Voki supports specifying multiple targets and run them sequentially or in parallel.
Default is sequential by simply invoking multiple target files:
voki run target1.hcl target2.hcl ... etc.and to run them in parallel add the -p <number> specifying how many to run in parallel:
voki run -p 2 target1.hcl target2.hcl ... etc.It is possible to only run specific steps if they have been given a name:
// target.hcl
target "myserver" {
user = "root"
host = "xyz:22"
step "cmd" {
name = "mystep"
command = "echo one"
}
step "cmd" {
name = "another"
command = "echo two"
}
}With the example above the names "mystep" and "another" can now be selected with the flag --step (-s):
voki run -s mystep target.hclor multiple steps
voki run -s mystep -s another target.hclInstall vault and run it in dev mode to test locally.
cd /tmp
curl -L https://releases.hashicorp.com/vault/1.19.0/vault_1.19.0_linux_amd64.zip -O
unzip vault_1.19.0_linux_amd64.zip
rm -f LICENSE.txt
rm -f vault_1.19.0_linux_amd64.zip
install vault ~/.local/bin/vault
rm -f vault
cd
export VAULT_DEV_ROOT_TOKEN_ID=123456
vault server -dev
export VAULT_ADDR='http://127.0.0.1:8200'add a secret to vault
vault kv put -mount=secret voki hello=worldNow use the secret in a target file.
Notice the alias in the Vault block, it's required to access the secret i.e. vault.mysecret.hello.
Note
Why not let the path be the accessor like path my/long/path -> my.long.path This is because paths can have dots in them, so having the alias makes accessing nested paths workable without much effort.
vault {
mountpath = "secret"
path = "voki"
alias = "mysecret"
}
target "myserver" {
host = "127.0.0.1:22"
user = "root"
step "cmd" {
command = "echo ${vault.mysecret.hello}"
}
}and invoke voki
VOKI_VAULT_TOKEN=123456 VOKI_VAULT_ADDR="http://127.0.0.1:8200" voki run target.hclIt's also possible to grab secrets from multiple paths.
Add another secret in a different path
vault kv put -mount=secret another hello=worldvault {
mountpath = "secret"
path = "voki"
alias = "this"
}
vault {
mountpath = "secret"
path = "another"
alias = "that"
}
target "myserver" {
host = "127.0.0.1:22"
user = "root"
step "cmd" {
command = "echo ${vault.this.hello}"
}
step "cmd" {
command = "echo ${vault.that.hello}"
}
}Local mode is where the target is the same host as where voki is executed.
To run in local mode simply set the target host value to "localhost"
target "here" {
host = "localhost"
user = "root"
step "cmd" {
command = "echo world"
}
}In local mode it can be useful to reference environment variables on the system.
This can be done with ${env.THE_VARIABLE}.
Notice the user variable can be disregarded as it will simply run as the current user.
target "here" {
host = "localhost"
step "cmd" {
// sudo = true
command = "whoami && echo ${env.USER}"
}
}Ensure Go 1.23 or newer is installed.
make build
./bin/voki --helpInstall into /home/$(USER)/.local/bin
make install