Skip to content

Commit 14f561e

Browse files
committed
Big commit for limes-next
1 parent af681a1 commit 14f561e

10 files changed

+766
-226
lines changed

cli-client.go

+104-25
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ import (
1515
"gopkg.in/yaml.v2"
1616

1717
"github.com/aws/aws-sdk-go/aws/credentials"
18-
pb "github.com/otm/ims/proto"
18+
pb "github.com/otm/limes/proto"
1919
"google.golang.org/grpc"
20+
"google.golang.org/grpc/codes"
2021
"google.golang.org/grpc/grpclog"
2122
)
2223

@@ -46,15 +47,15 @@ func newCliClient(address string) *cliClient {
4647
}
4748

4849
// StartService bootstraps the metadata service
49-
func StartService(args *Start) {
50+
func StartService(configFile, adress, profileName, MFA string, fake bool) {
5051
log := &ConsoleLogger{}
5152
config := Config{}
5253

5354
// TODO: Move to function and use a default configuration file
54-
if args.ConfigFile != "" {
55+
if configFile != "" {
5556
// Parse in options from the given config file.
56-
log.Debug("Loading configuration from %s\n", args.ConfigFile)
57-
configContents, configErr := ioutil.ReadFile(args.ConfigFile)
57+
log.Debug("Loading configuration from %s\n", configFile)
58+
configContents, configErr := ioutil.ReadFile(configFile)
5859
if configErr != nil {
5960
log.Fatalf("Could not read from config file. The error was: %s\n", configErr.Error())
6061
}
@@ -69,7 +70,7 @@ func StartService(args *Start) {
6970

7071
defer func() {
7172
log.Debug("Removing UNIX socket.\n")
72-
os.Remove(args.Adress)
73+
os.Remove(adress)
7374
}()
7475

7576
// Startup the HTTP server and respond to requests.
@@ -81,11 +82,16 @@ func StartService(args *Start) {
8182
log.Fatalf("Could not startup the metadata interface: %s\n", err)
8283
}
8384

84-
if args.MFA == "" {
85-
args.MFA = checkMFA(config)
85+
if MFA == "" {
86+
MFA = checkMFA(config)
8687
}
8788

88-
credsManager := NewCredentialsExpirationManager(config, args.MFA)
89+
var credsManager CredentialsManager
90+
if fake {
91+
credsManager = &FakeCredentialsManager{}
92+
} else {
93+
credsManager = NewCredentialsExpirationManager(profileName, config, MFA)
94+
}
8995

9096
mds, metadataError := NewMetadataService(listener, credsManager)
9197
if metadataError != nil {
@@ -94,7 +100,7 @@ func StartService(args *Start) {
94100
mds.Start()
95101

96102
stop := make(chan struct{})
97-
agentServer := NewCliHandler(args.Adress, credsManager, stop, config)
103+
agentServer := NewCliHandler(adress, credsManager, stop, config)
98104
err = agentServer.Start()
99105
if err != nil {
100106
log.Fatalf("Could not start agentServer: %s\n", err.Error())
@@ -139,8 +145,9 @@ func (c *cliClient) stop(args *Stop) error {
139145
}
140146

141147
func (c *cliClient) status(args *Status) error {
142-
status := "up"
148+
status := true
143149

150+
service := "up"
144151
r, err := c.srv.Status(context.Background(), &pb.Void{})
145152
if err != nil {
146153
r = &pb.StatusReply{
@@ -150,41 +157,68 @@ func (c *cliClient) status(args *Status) error {
150157
SessionToken: "n/a",
151158
Expiration: "n/a",
152159
}
153-
status = "down"
154-
defer fmt.Fprintf(os.Stderr, "\ncommunication error: %v\n", err)
160+
service = "down"
161+
status = false
162+
163+
showCorrectionAndExit(err)
164+
defer fmt.Fprintf(errout, "\nerror communicating with daemon: %v\n", err)
155165
}
156166

157167
if r.Error != "" {
158-
status = "error"
159-
defer fmt.Fprintf(os.Stderr, "\nerror retrieving status: %v\n", r.Error)
168+
service = "error"
169+
status = false
170+
defer fmt.Fprintf(errout, "\nerror communication with daemon: %v\n", r.Error)
160171
}
161172

162173
env := "ok"
163174
errConf := checkActiveAWSConfig()
164175
if errConf != nil {
165176
env = "nok"
166-
defer fmt.Fprintf(os.Stderr, "\nwarning: %v\n", errConf)
177+
status = false
178+
defer fmt.Fprintf(errout, "run 'limes fix' to automaticly resolv the problem\n")
179+
defer fmt.Fprintf(errout, "\nwarning: %v\n", errConf)
167180
}
168181

169-
fmt.Printf("Server: %v\n", status)
170-
fmt.Printf("AWS Config: %v\n", env)
171-
fmt.Printf("Role: %v\n", r.Role)
182+
if !status {
183+
fmt.Fprintf(out, "Status: %v\n", "nok")
184+
} else {
185+
fmt.Fprintf(out, "Status: %v\n", "ok")
186+
}
187+
fmt.Fprintf(out, "Role: %v\n", r.Role)
172188

173189
if args.Verbose == false {
174190
return err
175191
}
176192

177-
fmt.Printf("AccessKeyId: %v\n", r.AccessKeyId)
178-
fmt.Printf("SecretAccessKey: %v\n", r.SecretAccessKey)
179-
fmt.Printf("SessionToken: %v\n", r.SessionToken)
180-
fmt.Printf("Expiration: %v\n", r.Expiration)
193+
fmt.Fprintf(out, "Server: %v\n", service)
194+
fmt.Fprintf(out, "AWS Config: %v\n", env)
195+
fmt.Fprintf(out, "AccessKeyId: %v\n", r.AccessKeyId)
196+
fmt.Fprintf(out, "SecretAccessKey: %v\n", r.SecretAccessKey)
197+
fmt.Fprintf(out, "SessionToken: %v\n", r.SessionToken)
198+
fmt.Fprintf(out, "Expiration: %v\n", r.Expiration)
181199

182200
return err
183201
}
184202

185-
func (c *cliClient) assumeRole(role string, args *SwitchProfile) error {
186-
r, err := c.srv.AssumeRole(context.Background(), &pb.AssumeRoleRequest{Name: role})
203+
func (c *cliClient) assumeRole(role string, MFA string) error {
204+
r, err := c.srv.AssumeRole(context.Background(), &pb.AssumeRoleRequest{Name: role, Mfa: MFA})
205+
if err != nil {
206+
if grpc.Code(err) == codes.FailedPrecondition && grpc.ErrorDesc(err) == errMFANeeded.Error() {
207+
return c.assumeRole(role, askMFA())
208+
}
209+
210+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
211+
return err
212+
}
213+
214+
fmt.Fprintf(out, "Assumed: %v\n", r.Role)
215+
return nil
216+
}
217+
218+
func (c *cliClient) setSourceProfile(role, MFA string) error {
219+
r, err := c.srv.SetCredentials(context.Background(), &pb.AssumeRoleRequest{Name: role, Mfa: MFA})
187220
if err != nil {
221+
showCorrectionAndExit(err)
188222
fmt.Fprintf(os.Stderr, "communication error: %v\n", err)
189223
return err
190224
}
@@ -201,6 +235,7 @@ func (c *cliClient) assumeRole(role string, args *SwitchProfile) error {
201235
func (c *cliClient) retreiveRole(role string) (*credentials.Credentials, error) {
202236
r, err := c.srv.RetrieveRole(context.Background(), &pb.AssumeRoleRequest{Name: role})
203237
if err != nil {
238+
showCorrectionAndExit(err)
204239
fmt.Fprintf(os.Stderr, "communication error: %v\n", err)
205240
return nil, err
206241
}
@@ -219,6 +254,23 @@ func (c *cliClient) retreiveRole(role string) (*credentials.Credentials, error)
219254
return creds, nil
220255
}
221256

257+
// Config(ctx context.Context, in *Void, opts ...grpc.CallOption) (*ConfigReply, error)
258+
func (c *cliClient) listRoles() ([]string, error) {
259+
r, err := c.srv.Config(context.Background(), &pb.Void{})
260+
if err != nil {
261+
showCorrectionAndExit(err)
262+
fmt.Fprintf(os.Stderr, "communication error: %v\n", err)
263+
return nil, err
264+
}
265+
266+
roles := make([]string, 0, len(r.Profiles))
267+
for role := range r.Profiles {
268+
roles = append(roles, role)
269+
}
270+
271+
return roles, nil
272+
}
273+
222274
func checkMFA(config Config) string {
223275
var MFA string
224276

@@ -235,3 +287,30 @@ func checkMFA(config Config) string {
235287

236288
return MFA
237289
}
290+
291+
// ask the user for an MFA token
292+
func askMFA() string {
293+
var MFA string
294+
295+
fmt.Printf("Enter MFA: ")
296+
_, err := fmt.Scanf("%s", &MFA)
297+
if err != nil {
298+
log.Fatalf("err: %v\n", err)
299+
}
300+
301+
return MFA
302+
}
303+
304+
func showCorrectionAndExit(err error) {
305+
if grpc.Code(err) == codes.FailedPrecondition {
306+
if grpc.ErrorDesc(err) == errMFANeeded.Error() {
307+
fmt.Fprintf(errout, "%v: run 'limes credentials --mfa <serial>'\n", grpc.ErrorDesc(err))
308+
os.Exit(1)
309+
}
310+
311+
if grpc.ErrorDesc(err) == errUnknownProfile.Error() {
312+
fmt.Fprintf(errout, "%v: run 'limes --profile <name> credentials [--mfa <serial>]'\n", grpc.ErrorDesc(err))
313+
os.Exit(1)
314+
}
315+
}
316+
}

cli-handler.go

+62-10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77

88
"google.golang.org/grpc"
9+
"google.golang.org/grpc/codes"
910

1011
pb "github.com/otm/limes/proto"
1112
"golang.org/x/net/context"
@@ -17,11 +18,11 @@ type CliHandler struct {
1718
stop chan struct{}
1819
log Logger
1920
config Config
20-
credsManager *CredentialsExpirationManager
21+
credsManager CredentialsManager
2122
}
2223

2324
// NewCliHandler returns a cliHandler
24-
func NewCliHandler(address string, credsManager *CredentialsExpirationManager, stop chan struct{}, config Config) *CliHandler {
25+
func NewCliHandler(address string, credsManager CredentialsManager, stop chan struct{}, config Config) *CliHandler {
2526
fmt.Println("new cli handler")
2627
return &CliHandler{
2728
address: address,
@@ -56,11 +57,17 @@ func (h *CliHandler) Start() error {
5657

5758
// Status handles the cli status command
5859
func (h *CliHandler) Status(ctx context.Context, in *pb.Void) (*pb.StatusReply, error) {
59-
creds := h.credsManager.GetCredentials()
60+
creds, err := h.credsManager.GetCredentials()
61+
if err != nil {
62+
if err == errMFANeeded || err == errUnknownProfile {
63+
return nil, grpc.Errorf(codes.FailedPrecondition, err.Error())
64+
}
65+
return nil, err
66+
}
6067

6168
return &pb.StatusReply{
6269
Error: "",
63-
Role: h.credsManager.role,
70+
Role: h.credsManager.Role(),
6471
AccessKeyId: *creds.AccessKeyId,
6572
SecretAccessKey: *creds.SecretAccessKey,
6673
SessionToken: *creds.SessionToken,
@@ -81,14 +88,51 @@ func (h *CliHandler) Stop(ctx context.Context, in *pb.Void) (*pb.StopReply, erro
8188
func (h *CliHandler) AssumeRole(ctx context.Context, in *pb.AssumeRoleRequest) (*pb.StatusReply, error) {
8289
err := h.credsManager.AssumeRole(in.Name, in.Mfa)
8390
if err != nil {
91+
if err == errMFANeeded || err == errUnknownProfile {
92+
return nil, grpc.Errorf(codes.FailedPrecondition, err.Error())
93+
}
94+
return nil, err
95+
}
96+
97+
creds, err := h.credsManager.GetCredentials()
98+
if err != nil {
99+
if err == errMFANeeded || err == errUnknownProfile {
100+
return nil, grpc.Errorf(codes.FailedPrecondition, err.Error())
101+
}
102+
return nil, err
103+
}
104+
105+
return &pb.StatusReply{
106+
Error: "",
107+
Role: h.credsManager.Role(),
108+
AccessKeyId: *creds.AccessKeyId,
109+
SecretAccessKey: *creds.SecretAccessKey,
110+
SessionToken: *creds.SessionToken,
111+
Expiration: creds.Expiration.String(),
112+
}, nil
113+
}
114+
115+
// AssumeRole will switch the current role of the metadata service
116+
func (h *CliHandler) SetCredentials(ctx context.Context, in *pb.AssumeRoleRequest) (*pb.StatusReply, error) {
117+
err := h.credsManager.SetSourceProfile(in.Name, in.Mfa)
118+
if err != nil {
119+
if err == errMFANeeded || err == errUnknownProfile {
120+
return nil, grpc.Errorf(codes.FailedPrecondition, err.Error())
121+
}
84122
return nil, err
85123
}
86124

87-
creds := h.credsManager.GetCredentials()
125+
creds, err := h.credsManager.GetCredentials()
126+
if err != nil {
127+
if err == errMFANeeded || err == errUnknownProfile {
128+
return nil, grpc.Errorf(codes.FailedPrecondition, err.Error())
129+
}
130+
return nil, err
131+
}
88132

89133
return &pb.StatusReply{
90134
Error: "",
91-
Role: h.credsManager.role,
135+
Role: h.credsManager.Role(),
92136
AccessKeyId: *creds.AccessKeyId,
93137
SecretAccessKey: *creds.SecretAccessKey,
94138
SessionToken: *creds.SessionToken,
@@ -100,12 +144,15 @@ func (h *CliHandler) AssumeRole(ctx context.Context, in *pb.AssumeRoleRequest) (
100144
func (h *CliHandler) RetrieveRole(ctx context.Context, in *pb.AssumeRoleRequest) (*pb.StatusReply, error) {
101145
creds, err := h.credsManager.RetrieveRole(in.Name, in.Mfa)
102146
if err != nil {
147+
if err == errMFANeeded || err == errUnknownProfile {
148+
return nil, grpc.Errorf(codes.FailedPrecondition, err.Error())
149+
}
103150
return nil, err
104151
}
105152

106153
return &pb.StatusReply{
107154
Error: "",
108-
Role: h.credsManager.role,
155+
Role: h.credsManager.Role(),
109156
AccessKeyId: *creds.AccessKeyId,
110157
SecretAccessKey: *creds.SecretAccessKey,
111158
SessionToken: *creds.SessionToken,
@@ -120,9 +167,14 @@ func (h *CliHandler) Config(ctx context.Context, in *pb.Void) (*pb.ConfigReply,
120167
}
121168
for name, profile := range h.config.profiles {
122169
res.Profiles[name] = &pb.Profile{
123-
AwsAccessKeyID: profile.AwsAccessKeyID,
170+
AwsAccessKeyID: profile.AwsAccessKeyID,
171+
AwsSecretAccessKey: profile.AwsSecretAccessKey,
172+
AwsSessionToken: profile.AwsSessionToken,
173+
Region: profile.Region,
174+
MFASerial: profile.MFASerial,
175+
RoleARN: profile.RoleARN,
176+
RoleSessionName: profile.RoleSessionName,
124177
}
125178
}
126-
// h.config.profiles
127-
return nil, nil
179+
return res, nil
128180
}

config.example

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ admin:
3232

3333
# source_profile defines the profile to use when assuming this role
3434
source_profile: default
35+
36+
# assumable controls if it is possible to assume the role for the metadata service
37+
# otherwise it is only possible to use with 'run' and 'env' subcommand
38+
assumable: false
3539
region: eu-west-1
3640

3741
# This is an example of an profile that can be assumed with `limes profile readonly`

0 commit comments

Comments
 (0)