Skip to content
Merged
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
18 changes: 9 additions & 9 deletions .github/workflows/lint_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,31 @@ on:
- master
- main
paths:
- '**.go'
- "**.go"
jobs:
test:
name: lint/test
runs-on: "ubuntu-latest"
steps:

- uses: actions/checkout@v4
name: Checkout repository
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
name: Setup go environment
with:
go-version: "1.22"
go-version: "1.24"
- name: Install GoReleaser
uses: goreleaser/goreleaser-action@v5
uses: goreleaser/goreleaser-action@v6
with:
install-only: true
version: "~> v2"

- name: Run tests
run: go test -v ./...
- uses: golangci/golangci-lint-action@v3
name: Run golangci-lint
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v7
with:
version: "latest"
args: --enable revive --timeout=2m --verbose
version: v2.0
args: --verbose
- name: Build libraries
run: |
./build.sh --snapshot
12 changes: 5 additions & 7 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,21 @@ on:
types:
- published
jobs:

release:
name: release binaries
runs-on: "ubuntu-latest"
steps:

- uses: actions/checkout@v4
name: Checkout repository
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
name: Setup go environment
with:
go-version: "1.22"
go-version: "1.24"
- name: Install GoReleaser
uses: goreleaser/goreleaser-action@v5
uses: goreleaser/goreleaser-action@v6
with:
install-only: true
version: "~> v2"

- name: Build libraries
run: |
Expand All @@ -37,7 +36,7 @@ jobs:
release_tag=$(curl -sH "Authorization: token ${{ secrets.GITHUB_TOKEN }}" "$release_url" | jq -r ".tag_name")
if [[ -z "$release_tag" ]]; then echo "variable release_tag is empty!" && exit 1; fi
echo "LATEST_RELEASE_TAG=$release_tag" >> $GITHUB_ENV

- name: Upload release assets
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -51,7 +50,6 @@ jobs:
name: bump krew-index version
runs-on: "ubuntu-latest"
steps:

- uses: actions/checkout@v4
name: Checkout repository
- uses: rajatjindal/[email protected]
Expand Down
7 changes: 7 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: "2"
linters:
default: standard
enable:
- revive
run:
timeout: 2m
10 changes: 6 additions & 4 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 1
version: 2
project_name: kubectl-node_ssm
dist: ./dist

Expand All @@ -12,13 +12,15 @@ builds:
goarch:
- amd64
- arm64

archives:
- format: tar.gz
- formats:
- tar.gz
name_template: "{{ .ProjectName }}-v{{ .Version }}-{{ .Os }}-{{ .Arch }}"
files:
- LICENSE
- README.md
format_overrides:
- goos: windows
format: zip
formats:
- zip
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

## Description

`node-ssm` is a straightforward `kubectl` plugin designed for establishing direct connections to EKS cluster nodes managed by AWS Systems Manager. It operates by utilizing the locally installed AWS CLI and session-manager-plugin. The plugin simplifies the process by automatically converting the provided EKS node name into its corresponding instance ID.
`node-ssm` is a straightforward `kubectl` plugin designed for establishing direct
connections to EKS cluster nodes managed by AWS Systems Manager. It operates by
utilizing the locally installed AWS CLI and session-manager-plugin. The plugin
simplifies the process by automatically converting the provided EKS node name into
its corresponding instance ID if needed.

### Install with Krew plugin manager

Expand All @@ -15,12 +19,12 @@ kubectl krew install node-ssm
### Usage

```shell
❯ kubectl get nodes --no-headers | head -n 1
❯ kubectl get nodes --no-headers | head -n 1
ip-10-10-10-10.ec2.internal Ready <none> 8d v1.22.17-eks-48e63af
❯ kubectl node-ssm start-session --target ip-10-10-10-10.ec2.internal

Starting session with SessionId: <username>@<domain>-0480532656ed795d8
sh-4.2$
sh-4.2$
```

All global global command-line flags listed in `kubectl options` are supported, for example:
Expand All @@ -30,7 +34,7 @@ All global global command-line flags listed in `kubectl options` are supported,
<my-current-context>
❯ kubectl get nodes --context <my-another-context> --no-headers | head -n 1
ip-20-20-20-20.ec2.internal Ready <none> 4d19h v1.22.17-eks-48e63af
❯ kubectl node-ssm start-session --context <my-another-context> --target ip-20-20-20-20.ec2.internal
❯ kubectl node-ssm start-session --context <my-another-context> --target ip-20-20-20-20.ec2.internal

Starting session with SessionId: <username>@<domain>-0dd10b4b84087dff4
sh-4.2$
Expand Down Expand Up @@ -64,5 +68,7 @@ go build -o kubectl-node_ssm \
### Logic

1. Extract `AWS_REGION` and `AWS_PROFILE` from [Config.Host](https://pkg.go.dev/k8s.io/[email protected]/rest#Config.Host) and [[]ExecEnvVar](https://pkg.go.dev/k8s.io/client-go/tools/clientcmd/api#ExecConfig.Env) array of current kubeconfig context.
2. Create [AWS session](https://pkg.go.dev/github.com/aws/aws-sdk-go/aws/session) and resolve EKS node `private-dns-name` to instance ID using [(*EC2) DescribeInstances](https://docs.aws.amazon.com/sdk-for-go/api/service/ec2/#EC2.DescribeInstances) API operation.
2. Create [AWS session](https://pkg.go.dev/github.com/aws/aws-sdk-go/aws/session) and resolve EKS node `private-dns-name` to instance ID using [(\*EC2) DescribeInstances](https://docs.aws.amazon.com/sdk-for-go/api/service/ec2/#EC2.DescribeInstances) API operation.
3. Build `aws ssm start-session --target <instance id>` [command](https://pkg.go.dev/os/exec#Command) with specified parameters and environment and execute it.

Note: `<instance id>` can be provided as it is. In this case no lookup will be performed.
5 changes: 5 additions & 0 deletions cmd/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Package cmd defines the Cobra commands for the node-ssm CLI plugin.
// It includes setup for global flags, the root command, and the start-session
// subcommand which resolves Kubernetes nodes to EC2 instance IDs and launches
// AWS SSM sessions.
package cmd
7 changes: 1 addition & 6 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,15 @@ func newCliOptions(streams genericclioptions.IOStreams) *cliOptions {
}
}

// NewRootCmd creates and returns the root Cobra command for the node-ssm CLI.
func NewRootCmd(streams genericclioptions.IOStreams) *cobra.Command {
var target string
var params []string
cliOptions := newCliOptions(streams)

rootCmd := &cobra.Command{Use: "node-ssm", SilenceUsage: true}
rootCmd.PersistentFlags().StringVar(&target, "target", "", "EKS node name (private-dns-name)")
_ = rootCmd.MarkPersistentFlagRequired("target")

rootCmd.PersistentFlags().StringSliceVar(&params, "session-params",
[]string{}, "SSM session parameters")

cliOptions.flags.AddFlags(rootCmd.Flags())
rootCmd.AddCommand(newStartSessionCmd(cliOptions, &target, &params))

return rootCmd
}
13 changes: 12 additions & 1 deletion cmd/startSession.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"fmt"
"regexp"

"github.com/VioletCranberry/kubectl-node-ssm/pkg/helpers"
"github.com/spf13/cobra"
Expand All @@ -13,7 +14,6 @@ func newStartSessionCmd(opts *cliOptions, target *string, params *[]string) *cob
Short: "Start AWS systems manager session using local AWS CLI and session-manager-plugin",
Long: "Start AWS systems manager session using local AWS CLI and session-manager-plugin",
RunE: func(_ *cobra.Command, _ []string) error {

kubeConfig, err := readKubeConfig(opts)
if err != nil {
return fmt.Errorf("error reading kubeconfig file: %w", err)
Expand All @@ -30,6 +30,12 @@ func newStartSessionCmd(opts *cliOptions, target *string, params *[]string) *cob
return nil
},
}
startSessionCmd.Flags().
StringVar(target, "target", "", "EKS node name (private-dns-name or instance Id)")
_ = startSessionCmd.MarkFlagRequired("target")

startSessionCmd.Flags().
StringSliceVar(params, "session-params", []string{}, "SSM session parameters")
return startSessionCmd
}

Expand All @@ -46,6 +52,11 @@ func readKubeConfig(opts *cliOptions) (*helpers.KubeConfig, error) {
}

func resolveTargetToID(awsProfile, awsRegion, target string) (string, error) {
// shortcut if they already gave us an instance ID
idRe := regexp.MustCompile(`^i-[0-9a-f]+$`)
if idRe.MatchString(target) {
return target, nil
}
client, err := helpers.NewAwsClient(awsProfile, awsRegion)
if err != nil {
return "", fmt.Errorf("error setting up AWS client: %w", err)
Expand Down
73 changes: 36 additions & 37 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,40 +1,38 @@
module github.com/VioletCranberry/kubectl-node-ssm

go 1.22.0

toolchain go1.22.4
go 1.24.1

require (
github.com/spf13/cobra v1.8.1
golang.org/x/sys v0.21.0
github.com/spf13/cobra v1.9.1
golang.org/x/sys v0.32.0
)

require (
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/go-errors/errors v1.5.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
Expand All @@ -43,36 +41,37 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.starlark.net v0.0.0-20240520160348-046347dcd104 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/oauth2 v0.29.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/term v0.31.0 // indirect
golang.org/x/text v0.24.0 // indirect
golang.org/x/time v0.11.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.30.2 // indirect
k8s.io/apimachinery v0.30.2 // indirect
k8s.io/klog/v2 v2.130.0 // indirect
k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a // indirect
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.17.2 // indirect
sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
k8s.io/api v0.32.4 // indirect
k8s.io/apimachinery v0.32.4 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/kustomize/api v0.19.0 // indirect
sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

require (
github.com/aws/aws-sdk-go v1.54.4
github.com/aws/aws-sdk-go v1.55.7
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.9.0
k8s.io/cli-runtime v0.30.2
k8s.io/client-go v0.30.2
github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/testify v1.10.0
k8s.io/cli-runtime v0.32.4
k8s.io/client-go v0.32.4
)
Loading