Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metadata server running on windows #174

Merged

Conversation

williamdenton
Copy link
Contributor

@williamdenton williamdenton commented Oct 30, 2017

Issue #166 describes whats going on here

This is a breaking change for the way the metadata server is started

aws-vault exec <environment> --server

is now

aws-vault exec <environment> -s -- aws-vault server

@williamdenton
Copy link
Contributor Author

I'm an experienced developer but this is the first Go code I've ever written, so assume I'm a complete newbie. I'm totally happy to have my approach/style critiqued

@williamdenton
Copy link
Contributor Author

One thing i didn't fully understand is why there are two servers running. One on 9099 which generates the security credentials payload and presumably token management and another than exposes it on the metadata ip address.

Could the two not be combined? If they could be combined that would solve the nested calls

@lox
Copy link
Collaborator

lox commented Oct 30, 2017

I wish I'd documented it better, as I wrote it several years ago, but the two part design has some advantages.

The part that fetches credentials and has access to the keyring is run in the foreground on localhost:9099 and only lasts as long as your interactive aws-vault exec session does, whilst the long-running root privileged portion runs in the background.

It's a stronger design from a security perspective, but it also makes it possible to prompt for MFA.

I'd definitely consider keeping things simple and consolidating to a aws-vault credential-server or similar, but we'd need to address the MFA prompt. It would work fine with the applescript mfa prompt, but not sure how good the usability would be on other platforms.

I don't mind the idea of requiring users to run it in another console tab. It takes away some of the magic and will perhaps mean people adopt it more.

Thoughts @mtibben / @pda?

@lox
Copy link
Collaborator

lox commented Oct 30, 2017

I generally dislike the fact that we call sudo ourselves anyway. We could drop that and leave it up to the user to use sudo as makes sense.

@williamdenton
Copy link
Contributor Author

I've tried automatically starting the server in the background in the same way it does now but without sudo -b. As its not background anymore i need to change cmd.Run() to cmd.Start()

https://github.com/99designs/aws-vault/blob/master/server/server.go#L73-L80

however exec is still expecting to execute another command and crashes when it cant

this makes it happy, but clearly im missing something

aws-vault exec playpen --server -- cmd /C  pause

now the process that is bound to port 80 is long lived and continues to run once started. Obviously this requires an elevated shell to run the first time (windows equivalent of sudo).

I feel like I'm missing a key bit of knowledge about how the metadata server is supposed to be executed.

@lox
Copy link
Collaborator

lox commented Oct 31, 2017

So there are two http servers:

  • The "proxy" server starts up in the background and binds to 169.254.169.254:80, which is what the aws tooling connects to. It needs root permissions to create the iface and bind to a port < 1024. This is what is triggered by aws-vault server. It's meant to be pretty useless on it's own, and simply proxies requests to localhost:9099 if it exists.

  • The "credential" server is started at the beginning of an aws-vault exec --server invocation and it only runs for the duration of that command. It's the one with the keys to the kingdom, so it binds to localhost:9099.

So with those two servers in place, the aws cli tools can request IAM creds, and refresh them as they go (refreshing them is important, is it keeps the lifetime low).

The lines you referred to (https://github.com/99designs/aws-vault/blob/master/server/server.go#L73-L80) just check that the "proxy" is running, as the "credential" server isn't much use without it.

Does that help?

@lox
Copy link
Collaborator

lox commented Oct 31, 2017

It seems like the hard part on windows is going to be the "proxy" server. I'd be tempted to require that windows users manually invoke the proxy server component:

	if !checkServerRunning(metadataBind) {
                if runtime.GOOS == "windows" {
                    app.Fatal("The credential proxy server isn't running. Run aws-vault server as Administrator in the background and then try this command again")
                }
		log.Printf("Starting `aws-vault server` as root in the background")
		cmd := exec.Command("sudo", "-b", os.Args[0], "server")
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			return err
		}
	}

@lox
Copy link
Collaborator

lox commented Oct 31, 2017

The more I think about it, I think the two part server design is better. It prevents people from running long lived credential servers, which basically negate the security value of aws-vault.

@lox
Copy link
Collaborator

lox commented Nov 8, 2017

Have we lost you @williamdenton? :)

@williamdenton
Copy link
Contributor Author

Sorry, no, just busy with another project currently, will return here shortly.

Will try the suggestion above and update PR soon

@mtibben
Copy link
Member

mtibben commented Nov 9, 2017

Yep think I agree @lox, 2 part makes a lot of sense.

I did some googling, and on Windows, the runas command might work to run the command as Administrator, although I haven't tried this myself.

I have a few suggestions on the UX around the server starting

  1. If the credential service hasn't been detected but we can start it ourselves, tell the user what we're doing

    $ aws-vault exec --server home -- aws s3 ls
    Starting the aws-vault credential service.
    + sudo aws-vault server
    Password:
    
  2. As a fallback if sudo/runas isn't present or if we're on windows we can prompt the user to start the service

    $ aws-vault exec --server -- aws s3 ls
    In order to run in --server mode, run the following command in your console
    to start the aws-vault credential service
    
        sudo aws-vault server
        runas /user:Administrator "aws-vault server"
    
    

William Denton added 2 commits November 19, 2017 20:12
the server will fail to launch unless in an administrative shell but
the error is descriptive
@williamdenton williamdenton force-pushed the metadata-server-running-on-windows branch from eb47875 to 6fa9164 Compare November 19, 2017 07:51
@williamdenton
Copy link
Contributor Author

this solution has very similar behavior to other platforms now:

  • it starts the server in the background if not started (but requires the main process to be as admin)
  • once the background server is running the actual credential server does not require administrative privileges

I think this is the best option for keeping the behaviour consistent on all platforms

.\aws-vault.exe exec playpen -s -- aws s3 ls
Enter passphrase to unlock ...\.awsvault\keys:
Creation of network alias for server mode requires elevated permissions (Run as administrator).
aws-vault: error: Server failed: exit status 1
Unable to locate credentials. You can configure credentials by running "aws configure".

the Unable to locate credentials... comes from the aws command in the exec

Because I'm using cmd.Start() to launch the background server process i can't tell if it failed to launch, i can't see a way to inspect the cmd object to tell if it has terminated. there is Wait() but it doesn't appear to take a timeout...

if the background server is not launched with administrative rights it
will terminate immediately.

There are two conditions this delay is hit:

* if running in an elevated command prompt for the first time
* not run in an elevated command prompt but it should have been

So during normal operation this delay wont be encountered.
@williamdenton
Copy link
Contributor Author

@lox this seems to get it functional and usable on windows.

@lox
Copy link
Collaborator

lox commented Nov 19, 2017

I'll review in detail shortly! Thanks so much @williamdenton!

@lox
Copy link
Collaborator

lox commented Nov 20, 2017

I wonder if we could use start /B "" program to run it in the background?

server/server.go Outdated
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return err
}
Copy link
Member

@mtibben mtibben Nov 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might read a bit nicer if we pull this logic out into 2 functions, StartCredentialsServerOnWindows and StartCredentialsServerWithSudo? And might be worth doing the checkServerRunning in both cases - there are failure scenarios if sudo doesn't exist for example

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if sudo doesn't exist then we still get to see the error as we start sudo with cmd.Run(). This launches sudo and waits for it to finish - which it does as soon as it starts the process in the background -b

@williamdenton
Copy link
Contributor Author

a couple of questions about merging:
Do I need to squash my commits before this gets merged?
or Do you merge with all the commits?
Do you want signed commits?

@lox
Copy link
Collaborator

lox commented Nov 20, 2017

a couple of questions about merging:
Do I need to squash my commits before this gets merged?
or Do you merge with all the commits?
Do you want signed commits?

It's fine how it is! Basically as soon as it's a 👍 from two of us, we'll merge it.

if runtime.GOOS == "windows" {
return StartCredentialProxyOnWindows()
}
return StartCredentialProxyWithSudo()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@williamdenton Actually another way to do this is put the arch specific functions into conditional build files, rather than using the runtime check. What's preferred @lox ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It gets complicated, let's stick to this switch. LGTM.

@mtibben mtibben merged commit 43243d3 into 99designs:master Nov 21, 2017
@williamdenton williamdenton deleted the metadata-server-running-on-windows branch November 21, 2017 20:15
@lox lox mentioned this pull request Feb 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants