Skip to content

Commit

Permalink
Merge pull request #116 from AdRoll/hologram-console
Browse files Browse the repository at this point in the history
Hologram 1.3
  • Loading branch information
joshua-mullins-nextroll authored Feb 16, 2023
2 parents 49d6d75 + 5147c04 commit 8b3d221
Show file tree
Hide file tree
Showing 9 changed files with 437 additions and 159 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.17.5
FROM golang:1.19.4

RUN echo 'deb http://deb.debian.org/debian stretch main' >> /etc/apt/sources.list

Expand Down Expand Up @@ -26,7 +26,7 @@ RUN cd /tmp && wget https://github.com/google/protobuf/releases/download/v2.6.1/

WORKDIR /tmp
# Get dependencies for building hologram
RUN go get github.com/jteeuwen/go-bindata/...
RUN go install github.com/jteeuwen/go-bindata/...
RUN git clone https://github.com/pote/gpm.git && cd gpm && ./configure && make install && rm -rf /tmp/gpm
RUN wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/xar/xar-1.5.2.tar.gz && tar xf xar-1.5.2.tar.gz && cd xar-1.5.2 && ./configure && make && make install && rm -rf /tmp/xar-1.5.2
RUN git clone https://github.com/hogliux/bomutils.git > /dev/null && cd bomutils && make > /dev/null && make install > /dev/null && rm -rf /tmp/bomutils
Expand Down
164 changes: 164 additions & 0 deletions cmd/hologram/console.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package main

import (
"encoding/json"
"fmt"
"github.com/AdRoll/hologram/log"
"github.com/spf13/cobra"
"io"
"net/http"
"net/url"
"os"
"os/exec"
"runtime"
"time"
)

func init() {
consoleCmd.Flags().Bool("new-session", false, "Start a new Google Chrome session. This allows use of multiple roles simultaneously.")
consoleCmd.Flags().Bool("show-url", false, "Show the federation URL used for sign-in.")
consoleCmd.Flags().Bool("no-launch", false, "Don't launch the browser.")
rootCmd.AddCommand(consoleCmd)
}

var consoleCmd = &cobra.Command{
Use: "console",
Short: "Open the AWS console in the default browser",
Run: func(cmd *cobra.Command, args []string) {
newSession, err := cmd.Flags().GetBool("new-session")
showUrl, err := cmd.Flags().GetBool("show-url")
noLaunch, err := cmd.Flags().GetBool("no-launch")
if err == nil {
err = launchConsole(newSession, showUrl, noLaunch)
}

if err != nil {
log.Errorf("%s", err)
os.Exit(1)
}
},
}

type HttpHologramCredentials struct {
Code string
LastUpdated string
Type string
AccessKeyId string
SecretAccessKey string
Token string
Expiration string
}

type HttpAwsCredentials struct {
SessionId string `json:"sessionId"`
SessionKey string `json:"sessionKey"`
SessionToken string `json:"sessionToken"`
}

type HttpFederationSigninToken struct {
SigninToken string
}

func launchConsole(newSession bool, showUrl bool, noLaunch bool) error {
federationUrlBase := "https://signin.aws.amazon.com/federation"
profileUrl := "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
awsConsoleUrl := "https://console.aws.amazon.com/"

// Get the profile name from the metadata service
response, err := http.Get(profileUrl)
defer response.Body.Close()
if err != nil {
return err
}
profileBytes, err := io.ReadAll(response.Body)
if err != nil {
return err
}
profile := string(profileBytes)

// Get the credentials from the metadata service
metadataUrl := fmt.Sprintf("%v%v", profileUrl, profile)
response, err = http.Get(metadataUrl)
defer response.Body.Close()
if err != nil {
return err
}
if response.StatusCode != 200 {
return fmt.Errorf("error getting credentials. Try running 'hologram me'")
}
metadataBytes, err := io.ReadAll(response.Body)
if err != nil {
return err
}
credentials := HttpHologramCredentials{}
err = json.Unmarshal(metadataBytes, &credentials)
if err != nil {
return err
}

// Get the federation signin token
awsCreds := HttpAwsCredentials{
SessionId: credentials.AccessKeyId,
SessionKey: credentials.SecretAccessKey,
SessionToken: credentials.Token,
}
awsCredsJson, err := json.Marshal(awsCreds)
signinTokenUrl := fmt.Sprintf("%v?Action=getSigninToken&SessionDuration=43200&Session=%v", federationUrlBase, url.QueryEscape(string(awsCredsJson)))
response, err = http.Get(signinTokenUrl)
defer response.Body.Close()
if err != nil {
return err
}
signinToken_bytes, err := io.ReadAll(response.Body)
if err != nil {
return err
}
signinToken := HttpFederationSigninToken{}
err = json.Unmarshal(signinToken_bytes, &signinToken)
if err != nil {
return err
}

// Get the federation login URL
federationUrl := fmt.Sprintf("%v?Action=login&Issuer=Hologram&Destination=%v&SigninToken=%v", federationUrlBase, url.QueryEscape(awsConsoleUrl), signinToken.SigninToken)

// if --show-url is set, print the URL
if showUrl {
fmt.Println(federationUrl)
}
// if --no-launch is set, stop here
if noLaunch {
return nil
}

// Open the URL in the browser
var openArgs []string
switch runtime.GOOS {
case "darwin":
if newSession {
dateSeconds := time.Now().Unix()
userDataDir := fmt.Sprintf("/tmp/hologram_session_%v/", dateSeconds)
err := os.MkdirAll(userDataDir, 0755)
if err != nil {
return err
}
_, err = os.Create(fmt.Sprintf("%v/First Run", userDataDir))
if err != nil {
return err
}
openArgs = append(openArgs, "-na", "Google Chrome", "--args", "--user-data-dir="+userDataDir)
}
openArgs = append(openArgs, federationUrl)
err = exec.Command("open", openArgs...).Run()
case "linux":
if newSession {
fmt.Println("Warning: --new-session is not currently supported on Linux")
}
openArgs = append(openArgs, federationUrl)
err = exec.Command("xdg-open", openArgs...).Run()
default:
return fmt.Errorf("unsupported OS: %v", runtime.GOOS)
}

return err
}
57 changes: 57 additions & 0 deletions cmd/hologram/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

import (
"fmt"
"github.com/AdRoll/hologram/log"
"github.com/AdRoll/hologram/protocol"
"github.com/AdRoll/hologram/transport/local"
"github.com/mitchellh/go-homedir"
"io/ioutil"
"os"
)

func request(req *protocol.AgentRequest) (*protocol.AgentResponse, error) {
client, err := local.NewClient("/var/run/hologram.sock")
if err != nil {
return nil, fmt.Errorf("Unable to connect to hologram socket. Is hologram-agent running? Error: %s", err.Error())
}

// Try to get to the user's SSH agent, for best compatibility.
// However, some agents are broken, so we should also try to
// include the ssh key contents.
sshAgentSock := os.Getenv("SSH_AUTH_SOCK")
req.SshAgentSock = &sshAgentSock

// Send along the raw bytes of the SSH key.
// TODO(silversupreme): Add in logic for id_dsa, id_ecdsa, etc.
if sshDir, homeErr := homedir.Expand("~/.ssh"); homeErr == nil {
sshFilename := fmt.Sprintf("%s/id_rsa", sshDir)
if sshKeyBytes, keyReadErr := ioutil.ReadFile(sshFilename); keyReadErr == nil {
req.SshKeyFile = sshKeyBytes
} else {
log.Debug("Falling back on DSA key.")
// Fallback on a user's DSA key if they have one.
sshFilename := fmt.Sprintf("%s/id_dsa", sshDir)
if sshKeyBytes, keyReadErr := ioutil.ReadFile(sshFilename); keyReadErr == nil {
req.SshKeyFile = sshKeyBytes
}
}
}

msg := &protocol.Message{
AgentRequest: req,
}

err = client.Write(msg)
if err != nil {
return nil, err
}

response, err := client.Read()

if response.GetAgentResponse() == nil {
return nil, fmt.Errorf("unexpected response type: %v", response)
}

return response.GetAgentResponse(), nil
}
Loading

0 comments on commit 8b3d221

Please sign in to comment.