Skip to content

Commit

Permalink
ssh authentication capture flow
Browse files Browse the repository at this point in the history
  • Loading branch information
mihakralj committed Aug 21, 2023
1 parent 15ea576 commit 8f667e8
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 77 deletions.
28 changes: 14 additions & 14 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ jobs:
run: sudo apt-get install build-essential devscripts debhelper dh-make
shell: bash

- name: Extract version from Makefile
- name: Extract version from root.go
id: extract_version
run: |
VERSION=$(awk -F '=' '/^VERSION/ {print $2}' Makefile)
VERSION=$(grep -o 'Version\s*string\s*=\s*"[^"]*"' cmd/root.go | sed 's/Version\s*string\s*=\s*"//; s/"$//')
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "::set-output name=version::${VERSION}"
echo "VERSION=$VERSION"
shell: bash

- name: Build Windows binary
Expand Down Expand Up @@ -79,17 +80,20 @@ jobs:
- name: Check out code
uses: actions/checkout@v3

- name: Extract version from Makefile
- name: Extract version from root.go
id: extract_version
run: echo "VERSION=$(awk -F '=' '/^VERSION/ {print $2}' Makefile)" >> $GITHUB_ENV
run: |
VERSION=$(grep -o 'Version\s*string\s*=\s*"[^"]*"' cmd/root.go | sed 's/Version\s*string\s*=\s*"//; s/"$//')
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "::set-output name=version::${VERSION}"
echo "VERSION=$VERSION"
shell: bash

- name: Select XCode
uses: BoundfoxStudios/action-xcode-select@v1
with:
version: 14.2


- name: Import app certificate
run: |
security create-keychain -p ${{ secrets.APPLE_CERTPWD }} build.keychain
Expand Down Expand Up @@ -135,10 +139,6 @@ jobs:
xcrun notarytool submit ./opnsense.pkg --wait --apple-id ${{ secrets.APPLE_NOTARIZATION_USERNAME }} --password ${{ secrets.APPLE_NOTARIZATION_PASSWORD }} --team-id ${{ secrets.APPLE_TEAM_NAME }}
xcrun stapler staple ./opnsense.pkg
- name: Compile .txz package in FreeBSD
id: compile
uses: vmactions/[email protected]
Expand Down Expand Up @@ -194,17 +194,17 @@ jobs:
ls -l ./bin
shell: bash

- name: Remove Existing Beta Tag
run: |
git tag -d beta || true
git push origin :refs/tags/beta || true
#- name: Remove Existing Beta Tag
# run: |
# git tag -d beta || true
# git push origin :refs/tags/beta || true

- name: Create Beta Release and Upload Assets
id: create_release
uses: softprops/action-gh-release@v1
with:
prerelease: true
draft: false
draft: true
tag_name: beta
name: opnsense-cli ${{ env.VERSION }}
files: ./bin/opnsense*
Expand Down
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,14 @@ deps:
@echo "Downloading dependencies..."
@$(GO) mod tidy

# Build the binary
build: deps
@echo "Building..."
ifeq ($(OS),Windows_NT)
@if not exist $(BUILD_DIR) mkdir $(BUILD_DIR)
@$(GO) build -ldflags "-X cmd.version=$(VERSION)" -o $(BUILD_DIR)/$(BINARY_NAME).exe .
@$(GO) build -o $(BUILD_DIR)/$(BINARY_NAME).exe .
else
@mkdir -p $(BUILD_DIR)
@$(GO) build -ldflags "-X cmd.version=$(VERSION)" -o $(BUILD_DIR)/$(BINARY_NAME) .
@$(GO) build -o $(BUILD_DIR)/$(BINARY_NAME) .
endif

# Run tests
Expand Down
20 changes: 10 additions & 10 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ Administrators typically only two options how to manage the firewall: either th

### Commands

- **`show system <xpath>`**: Retrieves system information from the firewall.
- **`show config <xpath>`**: Displays hierarchical segments of `config.xml`.
- **`show backup <config>`**: Lists available backup configs or a specific backup.
- **`show system [<xpath>]`**: Retrieves system information from the firewall.
- **`show config [<xpath>]`**: Displays xpath segment of config.xml.
- **`show backup [<backup.xml>]`**: Lists available backup configs or displays a specific backup.
- **`run <service> <command>`**: Executes commands on OPNsense.
- **`set <xpath> value <value>`**: Sets a value of a specific node in the staging config.
- **`commit`**: Moves staging config to active config.
- **`compare [<config>] [<config>]`**: Compares staging config and active config or any two configs.
- **`discard [<xpath>]`**: Discards a value in the staging config or all changes in the staging config.
- **`save [<config>]`**: Creates a new backup config.
- **`load <config>`**: Moves a specific backup config to active config.
- **`delete <config>`**: Deletes a specific config file.
- **`set <xpath> value <value>`**: Sets a value of a specific node in staging.xml file.
- **`commit`**: Moves staging.xml to active config.xml.
- **`compare [<staging.xml>] [<config.xml>]`**: Compares two config files.
- **`discard [<xpath>]`**: Discards a value (or all changes) in the staging.xml.
- **`save [<backup.xml>]`**: Creates a new backup.xml.
- **`load [<backup.xml>]`**: Restores config.xml from a specific backup.xml. (alias: `restore``)
- **`delete <backup.xml>`**: Deletes a specific backup.xml.

### Flags

Expand Down
31 changes: 21 additions & 10 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

var (
Version string
Version string = "0.5.0"
verbose int
force bool
host string
Expand All @@ -33,6 +33,11 @@ func init() {
rootCmd.PersistentFlags().BoolVarP(&force, "force", "f", false, "Accept or bypass checks and prompts")
//rootCmd.PersistentFlags().StringVarP(&configfile, "config", "c", "/conf/config.xml", "path to target config.xml")

rootCmd.Flags().StringVar(&Version, "version", "", "display version of opnsense")
rootCmd.SetHelpCommand(&cobra.Command{
Hidden: true,
})

cobra.OnInitialize(func() {
configfile = "/conf/config.xml"
stagingfile = "/conf/staging.xml"
Expand All @@ -43,22 +48,28 @@ func init() {
}

var rootCmd = &cobra.Command{
Use: "opnsense",
Short: "opnsense is a CLI to manage and monitor OPNsense firewall configuration, check status, change settings, and execute commands.",
Long: `
Description:
opnsense is a command-line utility for managing, configuring, and monitoring OPNsense firewall systems.
It facilitates non-GUI administration, both directly in the shell and remotely via an SSH tunnel.
All interactions with OPNsense utilize the same mechanisms as the Web GUI,
including staged modifications of config.xml and execution of available configd commands.`,
Use: "opnsense [command]",
Short: "CLI to manage and monitor OPNsense firewall systems.",
Long: `Command Line utility to interact with OPNsense firewall.
opnsense CLI is a command-line utility for managing, configuring, and monitoring OPNsense firewall systems.
It facilitates non-GUI administration, both locally on the firewall and remotely via an SSH tunnel.
To avoid entering passwords for each remote call, use 'ssh-add' to add private key to your ssh-agent.`,

Example: ` opnsense -t [email protected] show system - Show system information on remote OPNsense
opnsense show config interfaces/wan --json - Show the inerfaces/wan of config.xml in json format
opnsense show backup -d2 - Show backup details 2 levels deep
opnsense run firmware reboot -f - Reboot OPNsense, force (no confirmation)
opnsense commit - Commit staged changes`,

PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return nil
},
Run: func(cmd *cobra.Command, args []string) {

if len(args) == 0 {
fmt.Println(cmd.Long)
cmd.Help()
os.Exit(0)
}
},
}
Expand Down
21 changes: 14 additions & 7 deletions cmd/action.go → cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ import (
"github.com/spf13/cobra"
)

var actionCmd = &cobra.Command{
Use: "action",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
var runCmd = &cobra.Command{
Use: "run [service] [command] [parameters]",
Short: "Executes commands on OPNsense firewall.",
Long: `Execute registered command on OPNsense.
The 'run' command is used to execute specific command that is registered with 'configctl' on OPNsense.`,

Example: ` opnsense run - List configd services
opnsense run dns - List commands for dns service
opnsense run dhcpd list leases - Show DHCP leases
opnsense run interface flush arp - Flush arp table
opnsense run firmware reboot - Issue a reboot
`,

Cobra is a CLI library for Go that empowers applications.`,
Run: func(cmd *cobra.Command, args []string) {

path := "actions"
Expand Down Expand Up @@ -86,6 +93,6 @@ Cobra is a CLI library for Go that empowers applications.`,
}

func init() {
rootCmd.AddCommand(actionCmd)
rootCmd.AddCommand(runCmd)
// Here you will define your flags and configuration settings.
}
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@ require (
golang.org/x/mod v0.12.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/tools v0.11.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
3 changes: 1 addition & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc=
golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
21 changes: 11 additions & 10 deletions internal/executecmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ package internal

import (
"bytes"
"fmt"
"os/exec"
)

func ExecuteCmd(command, host string) (string, error) {

Log(4,"sh -c %s",command)
Log(4, "sh -c %s", command)
if host == "" {
Log(5, "no target provided; executing command locally.")
out, err := exec.Command("sh", "-c", command).Output()
Expand All @@ -19,21 +17,24 @@ func ExecuteCmd(command, host string) (string, error) {
Log(5, "received results from executed command.")
return string(out), nil
}

sshClient, err := getSSHClient(host)
if err != nil {
return "", err
}

session := sshClient.Session
if err != nil {
return "", fmt.Errorf("failed to create session: %v", err)
if sshClient.Session == nil {
session, err := sshClient.Client.NewSession()
if err != nil {
Log(1, "%s", err)
return "", err
}
sshClient.Session = session
}
defer session.Close()

var stdoutBuf bytes.Buffer
session.Stdout = &stdoutBuf

err = session.Run(command)
sshClient.Session.Stdout = &stdoutBuf
err = sshClient.Session.Run(command)
if err != nil {
Log(3, "failed to execute sh command. %s", err.Error())
return "", err
Expand Down
54 changes: 34 additions & 20 deletions internal/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

type SSHClient struct {
Session *ssh.Session
Client *ssh.Client
}

var (
Expand Down Expand Up @@ -39,40 +40,53 @@ func getSSHClient(target string) (*SSHClient, error) {
host = userhost
}

// try to get config.Auth from sshAgent
// if no identities, skip to password
// try sshDial to get connection
// if failed, skip to password
// try sshDial with password

var connection *ssh.Client

if config == nil {
config = &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}

//try to get sshAgent
sshAgent, err := GetSSHAgent()
if err == nil {
config.Auth = []ssh.AuthMethod{sshAgent}
config.Auth = append(config.Auth, sshAgent)
if len(config.Auth) > 0 {
connection, err = ssh.Dial("tcp", host+":"+port, config)
if err == nil {
return &SSHClient{Client: connection}, nil
}
}
}

if len(config.Auth) == 0 {
fmt.Println("No suitable SSH identities found in ssh-agent.\nFor enhanced security add SSH key to the ssh agent using ssh-add command")
fmt.Printf("Enter password for %s@%s: \n", user, host)
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println("No authorized SSH keys found in local ssh agent, reverting to password-based access.\nTo enable seamless access, use the 'ssh-add' to add the authorized key for user",user)
fmt.Printf("Enter password for %s@%s: ", user, host)
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
if err != nil {
Log(5, "failed to read password: %v", err)
}
password := string(bytePassword)
config.Auth = []ssh.AuthMethod{ssh.Password(password)}
connection, err = ssh.Dial("tcp", host+":"+port, config)
if err != nil {
fmt.Println()
if err != nil {
return nil, fmt.Errorf("failed to read password: %v", err)
}
password := string(bytePassword)
config.Auth = []ssh.AuthMethod{ssh.Password(password)}
Log(1, "%v", err)
} else {
fmt.Println()
return &SSHClient{Client: connection}, nil
}
}

connection, err := ssh.Dial("tcp", host+":"+port, config)
if err != nil {
Log(1, "%v", err)
}

session, err := connection.NewSession()
connection, err = ssh.Dial("tcp", host+":"+port, config)
if err != nil {
Log(1, "%v", err)
}

SshClient = &SSHClient{Session: session}
SshClient = &SSHClient{Client: connection}
return SshClient, nil
}

0 comments on commit 8f667e8

Please sign in to comment.