Skip to content

Commit 8b3d221

Browse files
Merge pull request #116 from AdRoll/hologram-console
Hologram 1.3
2 parents 49d6d75 + 5147c04 commit 8b3d221

File tree

9 files changed

+437
-159
lines changed

9 files changed

+437
-159
lines changed

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.17.5
1+
FROM golang:1.19.4
22

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

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

2727
WORKDIR /tmp
2828
# Get dependencies for building hologram
29-
RUN go get github.com/jteeuwen/go-bindata/...
29+
RUN go install github.com/jteeuwen/go-bindata/...
3030
RUN git clone https://github.com/pote/gpm.git && cd gpm && ./configure && make install && rm -rf /tmp/gpm
3131
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
3232
RUN git clone https://github.com/hogliux/bomutils.git > /dev/null && cd bomutils && make > /dev/null && make install > /dev/null && rm -rf /tmp/bomutils

cmd/hologram/console.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"github.com/AdRoll/hologram/log"
7+
"github.com/spf13/cobra"
8+
"io"
9+
"net/http"
10+
"net/url"
11+
"os"
12+
"os/exec"
13+
"runtime"
14+
"time"
15+
)
16+
17+
func init() {
18+
consoleCmd.Flags().Bool("new-session", false, "Start a new Google Chrome session. This allows use of multiple roles simultaneously.")
19+
consoleCmd.Flags().Bool("show-url", false, "Show the federation URL used for sign-in.")
20+
consoleCmd.Flags().Bool("no-launch", false, "Don't launch the browser.")
21+
rootCmd.AddCommand(consoleCmd)
22+
}
23+
24+
var consoleCmd = &cobra.Command{
25+
Use: "console",
26+
Short: "Open the AWS console in the default browser",
27+
Run: func(cmd *cobra.Command, args []string) {
28+
newSession, err := cmd.Flags().GetBool("new-session")
29+
showUrl, err := cmd.Flags().GetBool("show-url")
30+
noLaunch, err := cmd.Flags().GetBool("no-launch")
31+
if err == nil {
32+
err = launchConsole(newSession, showUrl, noLaunch)
33+
}
34+
35+
if err != nil {
36+
log.Errorf("%s", err)
37+
os.Exit(1)
38+
}
39+
},
40+
}
41+
42+
type HttpHologramCredentials struct {
43+
Code string
44+
LastUpdated string
45+
Type string
46+
AccessKeyId string
47+
SecretAccessKey string
48+
Token string
49+
Expiration string
50+
}
51+
52+
type HttpAwsCredentials struct {
53+
SessionId string `json:"sessionId"`
54+
SessionKey string `json:"sessionKey"`
55+
SessionToken string `json:"sessionToken"`
56+
}
57+
58+
type HttpFederationSigninToken struct {
59+
SigninToken string
60+
}
61+
62+
func launchConsole(newSession bool, showUrl bool, noLaunch bool) error {
63+
federationUrlBase := "https://signin.aws.amazon.com/federation"
64+
profileUrl := "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
65+
awsConsoleUrl := "https://console.aws.amazon.com/"
66+
67+
// Get the profile name from the metadata service
68+
response, err := http.Get(profileUrl)
69+
defer response.Body.Close()
70+
if err != nil {
71+
return err
72+
}
73+
profileBytes, err := io.ReadAll(response.Body)
74+
if err != nil {
75+
return err
76+
}
77+
profile := string(profileBytes)
78+
79+
// Get the credentials from the metadata service
80+
metadataUrl := fmt.Sprintf("%v%v", profileUrl, profile)
81+
response, err = http.Get(metadataUrl)
82+
defer response.Body.Close()
83+
if err != nil {
84+
return err
85+
}
86+
if response.StatusCode != 200 {
87+
return fmt.Errorf("error getting credentials. Try running 'hologram me'")
88+
}
89+
metadataBytes, err := io.ReadAll(response.Body)
90+
if err != nil {
91+
return err
92+
}
93+
credentials := HttpHologramCredentials{}
94+
err = json.Unmarshal(metadataBytes, &credentials)
95+
if err != nil {
96+
return err
97+
}
98+
99+
// Get the federation signin token
100+
awsCreds := HttpAwsCredentials{
101+
SessionId: credentials.AccessKeyId,
102+
SessionKey: credentials.SecretAccessKey,
103+
SessionToken: credentials.Token,
104+
}
105+
awsCredsJson, err := json.Marshal(awsCreds)
106+
signinTokenUrl := fmt.Sprintf("%v?Action=getSigninToken&SessionDuration=43200&Session=%v", federationUrlBase, url.QueryEscape(string(awsCredsJson)))
107+
response, err = http.Get(signinTokenUrl)
108+
defer response.Body.Close()
109+
if err != nil {
110+
return err
111+
}
112+
signinToken_bytes, err := io.ReadAll(response.Body)
113+
if err != nil {
114+
return err
115+
}
116+
signinToken := HttpFederationSigninToken{}
117+
err = json.Unmarshal(signinToken_bytes, &signinToken)
118+
if err != nil {
119+
return err
120+
}
121+
122+
// Get the federation login URL
123+
federationUrl := fmt.Sprintf("%v?Action=login&Issuer=Hologram&Destination=%v&SigninToken=%v", federationUrlBase, url.QueryEscape(awsConsoleUrl), signinToken.SigninToken)
124+
125+
// if --show-url is set, print the URL
126+
if showUrl {
127+
fmt.Println(federationUrl)
128+
}
129+
// if --no-launch is set, stop here
130+
if noLaunch {
131+
return nil
132+
}
133+
134+
// Open the URL in the browser
135+
var openArgs []string
136+
switch runtime.GOOS {
137+
case "darwin":
138+
if newSession {
139+
dateSeconds := time.Now().Unix()
140+
userDataDir := fmt.Sprintf("/tmp/hologram_session_%v/", dateSeconds)
141+
err := os.MkdirAll(userDataDir, 0755)
142+
if err != nil {
143+
return err
144+
}
145+
_, err = os.Create(fmt.Sprintf("%v/First Run", userDataDir))
146+
if err != nil {
147+
return err
148+
}
149+
openArgs = append(openArgs, "-na", "Google Chrome", "--args", "--user-data-dir="+userDataDir)
150+
}
151+
openArgs = append(openArgs, federationUrl)
152+
err = exec.Command("open", openArgs...).Run()
153+
case "linux":
154+
if newSession {
155+
fmt.Println("Warning: --new-session is not currently supported on Linux")
156+
}
157+
openArgs = append(openArgs, federationUrl)
158+
err = exec.Command("xdg-open", openArgs...).Run()
159+
default:
160+
return fmt.Errorf("unsupported OS: %v", runtime.GOOS)
161+
}
162+
163+
return err
164+
}

cmd/hologram/helpers.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"github.com/AdRoll/hologram/log"
6+
"github.com/AdRoll/hologram/protocol"
7+
"github.com/AdRoll/hologram/transport/local"
8+
"github.com/mitchellh/go-homedir"
9+
"io/ioutil"
10+
"os"
11+
)
12+
13+
func request(req *protocol.AgentRequest) (*protocol.AgentResponse, error) {
14+
client, err := local.NewClient("/var/run/hologram.sock")
15+
if err != nil {
16+
return nil, fmt.Errorf("Unable to connect to hologram socket. Is hologram-agent running? Error: %s", err.Error())
17+
}
18+
19+
// Try to get to the user's SSH agent, for best compatibility.
20+
// However, some agents are broken, so we should also try to
21+
// include the ssh key contents.
22+
sshAgentSock := os.Getenv("SSH_AUTH_SOCK")
23+
req.SshAgentSock = &sshAgentSock
24+
25+
// Send along the raw bytes of the SSH key.
26+
// TODO(silversupreme): Add in logic for id_dsa, id_ecdsa, etc.
27+
if sshDir, homeErr := homedir.Expand("~/.ssh"); homeErr == nil {
28+
sshFilename := fmt.Sprintf("%s/id_rsa", sshDir)
29+
if sshKeyBytes, keyReadErr := ioutil.ReadFile(sshFilename); keyReadErr == nil {
30+
req.SshKeyFile = sshKeyBytes
31+
} else {
32+
log.Debug("Falling back on DSA key.")
33+
// Fallback on a user's DSA key if they have one.
34+
sshFilename := fmt.Sprintf("%s/id_dsa", sshDir)
35+
if sshKeyBytes, keyReadErr := ioutil.ReadFile(sshFilename); keyReadErr == nil {
36+
req.SshKeyFile = sshKeyBytes
37+
}
38+
}
39+
}
40+
41+
msg := &protocol.Message{
42+
AgentRequest: req,
43+
}
44+
45+
err = client.Write(msg)
46+
if err != nil {
47+
return nil, err
48+
}
49+
50+
response, err := client.Read()
51+
52+
if response.GetAgentResponse() == nil {
53+
return nil, fmt.Errorf("unexpected response type: %v", response)
54+
}
55+
56+
return response.GetAgentResponse(), nil
57+
}

0 commit comments

Comments
 (0)