-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add connect command to connect to an instance
- Loading branch information
Eugene Dementiev
committed
Oct 31, 2019
1 parent
d426273
commit f9660b4
Showing
7 changed files
with
131 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package cmd | ||
|
||
import ( | ||
"aws-ssh/lib" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
var connectCmd = &cobra.Command{ | ||
Use: "connect [ssh command (ssh -tt user@instanceid)]", | ||
Short: "SSH into the EC2 instance using ec2 connect feature", | ||
Long: `aws-ssh connects to the EC2 instance using ec2 connect feature. It makes a special API call to upload | ||
the first public key from your running ssh agent and then runs ssh command`, | ||
Aliases: []string{"ssh"}, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
var profile string | ||
|
||
profiles := viper.GetStringSlice("profiles") | ||
if len(profiles) > 0 { | ||
profile = profiles[0] | ||
} | ||
lib.ConnectEC2(profile, viper.GetString("instanceid"), viper.GetString("user"), args) | ||
}, | ||
} | ||
|
||
func init() { | ||
connectCmd.Flags().StringP("instanceid", "i", "", "Instance ID to connect to") | ||
connectCmd.Flags().StringP("user", "u", "ec2-user", "Existing user on the instance") | ||
connectCmd.MarkFlagRequired("instanceid") | ||
|
||
viper.BindPFlag("instanceid", connectCmd.Flags().Lookup("instanceid")) | ||
viper.BindPFlag("user", connectCmd.Flags().Lookup("user")) | ||
rootCmd.AddCommand(connectCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package lib | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
"os" | ||
"os/exec" | ||
"strings" | ||
"syscall" | ||
|
||
"github.com/apex/log" | ||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/session" | ||
"github.com/aws/aws-sdk-go/service/ec2" | ||
"github.com/aws/aws-sdk-go/service/ec2instanceconnect" | ||
"golang.org/x/crypto/ssh/agent" | ||
) | ||
|
||
// ConnectEC2 connects to an EC2 instance by pushing your public key onto it first | ||
// using EC2 connect feature and then runs ssh. | ||
func ConnectEC2(profile, instanceID, instanceUser string, args []string) { | ||
localSession, err := session.NewSessionWithOptions(session.Options{ | ||
Config: aws.Config{}, | ||
|
||
SharedConfigState: session.SharedConfigEnable, | ||
Profile: profile, | ||
}) | ||
if err != nil { | ||
log.WithError(err).Fatal("can't get aws session") | ||
} | ||
ec2Svc := ec2.New(localSession) | ||
ec2Result, err := ec2Svc.DescribeInstances(&ec2.DescribeInstancesInput{ | ||
InstanceIds: aws.StringSlice([]string{instanceID}), | ||
}) | ||
if err != nil { | ||
log.WithError(err).Fatal("can't get ec2 instance") | ||
} | ||
|
||
ec2Instance := ec2Result.Reservations[0].Instances[0] | ||
ec2ICSvc := ec2instanceconnect.New(localSession) | ||
|
||
log.WithField("instance_id", aws.StringValue(ec2Instance.InstanceId)).Info("Pushing SSH key...") | ||
|
||
sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")) | ||
|
||
keys, err := agent.NewClient(sshAgent).List() | ||
if err != nil || len(keys) < 1 { | ||
log.Fatal("Can't get public keys from ssh agent. Please ensure you have the ssh-agent running and have at least one identity added (with ssh-add)") | ||
} | ||
pubkey := keys[0].String() | ||
|
||
if _, err := ec2ICSvc.SendSSHPublicKey(&ec2instanceconnect.SendSSHPublicKeyInput{ | ||
InstanceId: ec2Instance.InstanceId, | ||
InstanceOSUser: aws.String(instanceUser), | ||
AvailabilityZone: ec2Instance.Placement.AvailabilityZone, | ||
SSHPublicKey: aws.String(pubkey), | ||
}); err != nil { | ||
log.WithError(err).Fatal("can't push ssh key") | ||
} | ||
|
||
if len(args) == 0 { | ||
// construct default args | ||
args = []string{ | ||
"ssh", | ||
"-tt", | ||
fmt.Sprintf("%s@%s", instanceUser, instanceID), | ||
} | ||
} | ||
|
||
command, err := exec.LookPath(args[0]) | ||
if err != nil { | ||
log.WithError(err).Fatal("Can't find the binary in the PATH") | ||
} | ||
log.WithField("instance_id", aws.StringValue(ec2Instance.InstanceId)).Infof("Connecting to the instance using '%s'", strings.Join(args, " ")) | ||
|
||
if err := syscall.Exec(command, args, os.Environ()); err != nil { | ||
log.WithFields(log.Fields{"command": command}).WithError(err).Fatal("can't run the command") | ||
} | ||
} |