Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 12 additions & 69 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: '>=1.16'
go-version: '>=1.18'
cache: false
- name: Lint All
uses: golangci/golangci-lint-action@v3
Expand All @@ -28,87 +28,30 @@ jobs:
test:
name: Test
runs-on: self-hosted
needs:
- lint
needs: lint
permissions:
id-token: write
contents: read
# env:
# PAS_HOSTNAME: ${{ secrets.PAS_HOSTNAME }}
# CCP_CLIENT_CERT: ${{ secrets.CCP_CLIENT_CERT }}
# CCP_CLIENT_PRIVATE_KEY: ${{ secrets.CCP_CLIENT_PRIVATE_KEY }}
steps:
- name: Checkout Source Code
- name: Checkout source code
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: '>=1.16'
go-version: '>=1.18'
cache: false
- name: Import Secrets using CyberArk Conjur Secret Fetcher
uses: infamousjoeg/[email protected]
with:
url: https://infamous.secretsmgr.cyberark.cloud
url: https://pineapple.secretsmgr.cyberark.cloud/api
account: conjur
authn_id: github
authn_id: inf-github
secrets: |
data/vault/D-App-CybrCLI/Application-CyberArkIdentitySecurity-infamous.cyberark.cloud-cybr-cli@cyberark.cloud.13142/address|PAS_ADDRESS;data/vault/D-App-CybrCLI/Application-CyberArkIdentitySecurity-infamous.cyberark.cloud-cybr-cli@cyberark.cloud.13142/username|PAS_USERNAME;data/vault/D-App-CybrCLI/Application-CyberArkIdentitySecurity-infamous.cyberark.cloud-cybr-cli@cyberark.cloud.13142/password|PAS_PASSWORD;data/vault/D-App-CybrCLI/ccp-client-certificate/password|CCP_CLIENT_CERT;data/vault/D-App-CybrCLI/ccp-priv-key/password|CCP_CLIENT_PRIVATE_KEY
- name: Debug Step
run: |
echo "PAS_ADDRESS: " $PAS_ADDRESS "\r\nPAS_USERNAME: " $PAS_USERNAME "\r\nPAS_PASSWORD: " $PAS_PASSWORD "\r\nCCP_CLIENT_CERT: " $CCP_CLIENT_CERT "\r\nCCP_CLIENT_PRIVATE_KEY: " $CCP_CLIENT_PRIVATE_KEY > secrets.txt
- name: Upload Artifacts to Workflow
if: always()
uses: actions/upload-artifact@v2
with:
name: Secrets
path: |
secrets.txt
data/vault/PIN-APP-CYBRCLI/Application-CyberArk-httpspineapple.privilegecloud.cyberark.cloud-jgarcia/address|PAS_HOSTNAME;data/vault/PIN-APP-CYBRCLI/Application-CyberArk-httpspineapple.privilegecloud.cyberark.cloud-jgarcia/username|PAS_USERNAME;data/vault/PIN-APP-CYBRCLI/Application-CyberArk-httpspineapple.privilegecloud.cyberark.cloud-jgarcia/password|PAS_PASSWORD;data/vault/PIN-APP-CYBRCLI/Website-PIN-CLIENT-CERT-httpscloud-connect.infamousdevops.com-ccp_client_cert/password|CCP_CLIENT_CERT;data/vault/PIN-APP-CYBRCLI/Website-PIN-CLIENT-CERT-ccp.infamousdevops.com-ccp_client_key/password|CCP_CLIENT_PRIVATE_KEY;"
- name: Test All
run: go test -v ./...

build:
name: Build Binaries
runs-on: ubuntu-latest
needs:
- lint
- test
defaults:
run:
shell: bash
strategy:
matrix:
goos: [linux, darwin, windows]
goarch: [amd64]
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: '>=1.16'
cache: false
- name: Get current date & time
id: date
run: echo "::set-output name=date::$(date +'%Y%m%d_%H%M%S')"
- name: Export GO111MODULE environment variable
run: export GO111MODULE=on
- name: Create ./bin/ directory
run: mkdir -p bin
- name: Fix x/sys Issues
run: go get -u golang.org/x/sys
- name: Build Binaries
run: |
CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -o ./bin/${{ matrix.goos }}_cybr .
- name: Build Docker Container Package
run: |
docker build -t nfmsjoeg/cybr-cli:$TAG_NAME .
docker save nfmsjoeg/cybr-cli:$TAG_NAME > ./bin/docker_authenticator.tar
env:
TAG_NAME: alpha-${{ steps.date.outputs.date }}
- name: Upload Artifacts to Workflow
if: always()
uses: actions/upload-artifact@v2
with:
name: Release Executables
path: |
./bin/*_cybr*
CCP_HOSTNAME: "https://ccp.infamousdevops.com"
run: |
export CCP_CLIENT_CERT=$(echo $CCP_CLIENT_CERT | base64 -d)
export CCP_CLIENT_PRIVATE_KEY=$(echo $CCP_CLIENT_PRIVATE_KEY | base64 -d)
go test -v ./...
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@

.DS_Store
.dccache
bin/
bin/

testing*
109 changes: 83 additions & 26 deletions cmd/logon.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"context"
"fmt"
"log"
"os"
Expand All @@ -9,6 +10,7 @@ import (

pasapi "github.com/infamousjoeg/cybr-cli/pkg/cybr/api"
"github.com/infamousjoeg/cybr-cli/pkg/cybr/api/requests"
"github.com/infamousjoeg/cybr-cli/pkg/cybr/api/shared"
"github.com/infamousjoeg/cybr-cli/pkg/cybr/helpers/prettyprint"
"github.com/infamousjoeg/cybr-cli/pkg/cybr/helpers/util"
"github.com/infamousjoeg/cybr-cli/pkg/cybr/identity"
Expand Down Expand Up @@ -51,49 +53,104 @@ var (
platformDiscovery *ispssresponses.PlatformDiscovery // Platform discovery response
)

// func logonToPAS(c pasapi.Client, username, password string, nonInteractive, concurrentSession bool) error {
// var err error
// ctx := context.Background()
// // Check if non-interactive flag is not provided and password is not empty
// if !nonInteractive && password != "" {
// return fmt.Errorf("An error occured because --non-interactive must be provided when using --password flag")
// }
// // If the execution is not non-interactive, ask the user to input password
// if !nonInteractive {
// password, err = util.ReadPassword()
// if err != nil {
// return fmt.Errorf("An error occurred trying to read password from Stdin. Exiting")
// }
// }
// // Check if password is empty
// if password == "" {
// return fmt.Errorf("Provided password is empty")
// }
// // Create credentials for logon
// credentials := requests.Logon{
// Username: username,
// Password: password,
// ConcurrentSession: concurrentSession,
// }
// // Logon to the PAS REST API
// ctx, errorResponse, err := c.Logon(ctx, credentials)
// if err != nil && (errorResponse == nil || errorResponse.ErrorCode != "ITATS542I") {
// // If errorResponse is nil, you can't use its ErrorCode, so handle the error accordingly
// if errorResponse == nil {
// return fmt.Errorf("Failed to logon to the PVWA and error details are unavailable. %s", err)
// } else if errorResponse != nil {
// return fmt.Errorf("Failed to Logon to the PVWA. %s", err)
// }
// }
// // Deal with OTPCode here if error contains challenge error code and redo client.Logon()
// if errorResponse != nil && errorResponse.ErrorCode == "ITATS542I" {
// // Get OTP code from Stdin
// fmt.Printf("%s: \n", errorResponse.ErrorMessage)
// credentials, err = util.ReadOTPcode(credentials)
// _, _, err = c.Logon(ctx, credentials)
// if err != nil {
// return fmt.Errorf("Failed to respond to challenge. Possible timeout occurred. %s", err)
// }
// }
// // Set client config
// err = c.SetConfig()
// if err != nil {
// return fmt.Errorf("Failed to create configuration file. %s", err)
// }
// return nil
// }

func logonToPAS(c pasapi.Client, username, password string, nonInteractive, concurrentSession bool) error {
var err error
// Check if non-interactive flag is not provided and password is not empty
if !nonInteractive && password != "" {
return fmt.Errorf("An error occured because --non-interactive must be provided when using --password flag")
ctx := context.Background()

// If --non-interactive flag is provided and password is empty, return error
if nonInteractive && password == "" {
return fmt.Errorf("An error occured because --password is required when using --non-interactive flag")
}
// If the execution is not non-interactive, ask the user to input password

// If --non-interactive flag is not provided, ask the user to input password
if !nonInteractive {
password, err = util.ReadPassword()
var err error
password, err = util.PromptForPassword(5)
if err != nil {
return fmt.Errorf("An error occurred trying to read password from Stdin. Exiting")
}
}
// Check if password is empty
if password == "" {
return fmt.Errorf("Provided password is empty")
}

// Create credentials for logon
credentials := requests.Logon{
Username: username,
Password: password,
ConcurrentSession: concurrentSession,
}
// Logon to the PAS REST API
err = c.Logon(credentials)
if err != nil && !strings.Contains(err.Error(), "ITATS542I") {
return fmt.Errorf("Failed to Logon to the PVWA. %s", err)
}
// Deal with OTPCode here if error contains challenge error code and redo client.Logon()

return performLogon(ctx, c, credentials)
}

func performLogon(ctx context.Context, c pasapi.Client, credentials requests.Logon) error {
ctx, errorResponse, err := c.Logon(ctx, credentials)
if err != nil {
// Get OTP code from Stdin
credentials, err = util.ReadOTPcode(credentials)
err = c.Logon(credentials)
return handleLogonError(c, credentials, errorResponse, err)
}

return c.SetConfig()
}

func handleLogonError(c pasapi.Client, credentials requests.Logon, errorResponse *shared.ErrorResponse, err error) error {
if errorResponse != nil && errorResponse.ErrorCode == "ITATS542I" {
fmt.Printf("%s. \n", errorResponse.ErrorMessage)
credentials, err := util.ReadOTPcode(credentials)
if err != nil {
return fmt.Errorf("Failed to respond to challenge. Possible timeout occurred. %s", err)
return fmt.Errorf("Failed to read OTP code. %s", err)
}
return performLogon(context.Background(), c, credentials)
}
// Set client config
err = c.SetConfig()
if err != nil {
return fmt.Errorf("Failed to create configuration file. %s", err)
}
return nil
return fmt.Errorf("handleLogonError: Failed to logon to the PVWA. %s", err)
}

func startAuthIdentity(c pasapi.Client, username string) (*responses.Authentication, error) {
Expand Down
8 changes: 7 additions & 1 deletion cmd/safes.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ var (
User string
// Group is the group to search for as a safe member
Group string
// MemberType is the type of member being added to the safe
MemberType string
)

var safesCmd = &cobra.Command{
Expand Down Expand Up @@ -231,7 +233,8 @@ var addMembersCmd = &cobra.Command{

Example Usage:
$ cybr safes add-member -s SafeName -m MemberName --list-account --use-account --retrieve-account
$ cybr safes add-member -s SafeName -m MemberName --role ApplicationIdentity`,
$ cybr safes add-member -s SafeName -m MemberName --role ApplicationIdentity --member-type user
$ cybr safes add-member -s SafeName -m MemberName --role ApplicationIdentity --member-type group`,
Run: func(cmd *cobra.Command, args []string) {
// Get config file written to local file system
client, err := pasapi.GetConfigWithLogger(getLogger())
Expand Down Expand Up @@ -265,6 +268,7 @@ var addMembersCmd = &cobra.Command{
SearchIn: SearchIn,
MembershipExpirationDate: MembershipExpirationDate,
Permissions: RolePermissions,
MemberType: MemberType,
}

// Add a safe with the configuration options given via CLI subcommands
Expand Down Expand Up @@ -436,6 +440,8 @@ func init() {
addMembersCmd.Flags().StringVarP(&MemberName, "member-name", "m", "", "Name of member being added to the desired safe")
addMembersCmd.MarkFlagRequired("member-name")
addMembersCmd.Flags().StringVarP(&SearchIn, "search-in", "i", "Vault", "Search in Domain or Vault")
addMembersCmd.Flags().StringVarP(&MemberType, "member-type", "t", "user", "Type of member being added to the safe: user (default) or group")
addMembersCmd.MarkFlagRequired("member-type")
addMembersCmd.Flags().StringVarP(&MembershipExpirationDate, "member-expiration-date", "e", "", "When the membership will expire")
addMembersCmd.Flags().StringVarP(&Role, "role", "r", "", "The role of the safe member being added for automated permissioning")
addMembersCmd.Flags().BoolVar(&UseAccounts, "use-accounts", false, "Use accounts in safe")
Expand Down
3 changes: 3 additions & 0 deletions go.work
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
go 1.21.0

use .
Loading