Skip to content

Commit

Permalink
Add vault command and update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Steve Coffman committed Jun 29, 2019
1 parent a977463 commit b0cee89
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 28 deletions.
47 changes: 32 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This will download the github 0.1.0 binary release for mac, and move any of your
keyfob add [name] [key]
keyfob otp [name]
keyfob list
keyfob vault [name] [profile]
keyfob help

+ `keyfob add name` adds a new key to the keyfob keychain with the given name. It
Expand All @@ -29,7 +30,9 @@ clipboard.

+ `keyfob list` prints the names of all the added keys, if any.

The time-based authentication codes are derived from a hash of the
+ `keyfob vault [name] [profile]` acts as a will act as an [AWS credential helper](https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes) using [AWS Vault](https://github.com/99designs/aws-vault/) and a One Time Password.

The Time-based One Time Password (TOTP) authentication codes are derived from a hash of the
key and the current time, so it is important that the system clock have at
least one-minute accuracy.

Expand All @@ -51,9 +54,10 @@ Then whenever GitHub prompts for a 2FA code, run keyfob to obtain one:
$ keyfob otp github
268346

## Derivation
## Derivation (Credit where Credit is due)

This is just a little toy cobbled together from [2fa](https://github.com/rsc/2fa/), [cobra](https://github.com/spf13/cobra), and [go-keyring](https://github.com/zalando/go-keyring) and using [goreleaser](https://github.com/goreleaser/goreleaser).
The directions I had below this were confusing, so I stole some of the directions from [this article on how to do a similar thing with a yubikey](https://hackernoon.com/use-a-yubikey-as-a-mfa-device-to-replace-google-authenticator-b4f4c0215f2).

## Really, does this make sense?

Expand Down Expand Up @@ -91,37 +95,50 @@ keyring frontend program [Seahorse](https://wiki.gnome.org/Apps/Seahorse):

This assumes you have installed `keyfob` but need to set up your secrets.

Your own organization __*might*__ have a different preferred `source_profile` name from `sosourcey` below.
__*Note:*__ Your own organization __*might*__ have a different preferred `source_profile` name from `source` below, and your AWS account number is probably not `111111111111`.

1. Skip to **[2](#2)** if you already added your AWS access key and secret access key to aws vault. Otherwise do this:
```
$ aws-vault add sosourcey --keychain login
$ brew cask install aws-vault
$ brew install go zbar awscli
$ aws-vault add source --keychain login
```
2. <a name="2"></a>Go to AWS, and [make a new MFA token](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html#enable-virt-mfa-for-iam-user). Either take a screenshot of the QR Code (⌘⇧3 aka Command-Shift-3) and run `zbarimg` on it as below, or click the option to see the text version. Save that secret somewhere. Also add it to your Google Authenticator as normal.

2. <a name="2"></a>Go to the AWS Web console to [make a new MFA token](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_enable_virtual.html#enable-virt-mfa-for-iam-user).

3. After login, go to IAM > Users and click in your user name. Then you need to click on the “Security credentials” tab.
<img src="./images/aws_iam_users_security_credentials.png" />

4. To assign an MFA device, just click Manage. If you have an existing one, you must remove it. In the next screen, select “Virtual MFA device”.
<img src="./images/manage_virtual_mfa_device.png" />

5. Here you can choose to show the QR code or to show the text of the MFA secret key. For our purposes, we want the secret key only.
<img src="./images/get_mfa_secret_key.png" />

6. Add your MFA secret (from above) to keyfob:
```
brew cask install aws-vault
brew install go zbar awscli
# To get the text secret out of the QR Code if you didn't ask to see that
zbarimg AWS_IAM_Management_Console.png
keyfob add aws-source <YOUR_BASE_32_KEY>
```
3. Copy the `aws-credential-helper.sh` script in this repository to a place in your shell path and remember the absolute path to there.

4. Add to your `.aws/config` file something like this:
7. Then run `keyfob otp aws-source` a few times, to get two different, but consecutive 6-digit codes and complete the set up.
<img src="./images/mfa_setup_correctly.png" />

8. Add to your `.aws/config` file something like this:
```
[default]
credential_process = /Users/scoffman/bin/aws-credential-helper-engineer.sh
credential_process = keyfob vault aws-source engineer
region = us-east-1
output = json
[profile sosourcey]
[profile source]
region = us-east-1
mfa_serial = arn:aws:iam::111111111111:mfa/scoffman
[profile engineer]
mfa_serial = arn:aws:iam::111111111111:mfa/scoffman
region = us-east-1
role_arn = arn:aws:iam::111111111111:role/put-power-role-here
source_profile = sosourcey
source_profile = source
```
5. Make sure you've edited and replaced the AWS account, userid, and power-role above.
9. Make sure you've edited and replaced the AWS account, userid, and power-role above.

37 changes: 24 additions & 13 deletions cmd/otp.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (
var otpCmd = &cobra.Command{
Use: "otp [key name]",
Short: "Generate a One Time Password",
Long: `otp name prints a two-factor authentication code from the key with the given name.
Long: `otp [key name] prints a two-factor authentication code from the key with the given name.
If -clip is specified, otp also copies to the code to the system clipboard.
With no arguments, otp prints two-factor authentication codes from all known time-based keys.
Expand All @@ -44,26 +44,37 @@ so it is important that the system clock have at least one-minute accuracy.`,

service := "keyfob"
user := args[0]
secret, err := keyring.Get(service, user)

codeText, err := generateTOTP(service, user)
if err != nil {
log.Fatal(err)
return
}
raw, err := decodeKey(secret)
if err == nil {
code := totp(raw, time.Now(), 6)
codeText := fmt.Sprintf("%0*d", 6, code)

if clip {
clipboard.WriteAll(codeText)
}

fmt.Printf("%s\n", codeText)
return
if clip {
clipboard.WriteAll(codeText)
}
log.Printf("%s: malformed key", secret)
//fmt has no prefix, log does
fmt.Printf("%s\n", codeText)

},
}

func generateTOTP(service, user string) (string, error) {
secret, err := keyring.Get(service, user)
if err != nil {
return "", err
}
raw, err := decodeKey(secret)
if err != nil {
return "", fmt.Errorf("%s: malformed key", secret)
}
code := totp(raw, time.Now(), 6)
codeText := fmt.Sprintf("%0*d", 6, code)

return codeText, nil
}

var clip bool

func init() {
Expand Down
63 changes: 63 additions & 0 deletions cmd/vault.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
Copyright © 2019 NAME HERE <EMAIL ADDRESS>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
"fmt"
"log"
"github.com/spf13/cobra"
"os/exec"
)

// vaultCmd represents the vault command
var vaultCmd = &cobra.Command{
Use: "vault [key name] [aws profile]",
Short: "AWS credential helper using AWS Vault and Time-based One Time Password",
Long: `"vault [key name] [aws profile] will act as an AWS credential helper using
AWS Vault and Time-based One Time Password
Ref: https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#sourcing-credentials-from-external-processes`,
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
service := "keyfob"
user := args[0]
profile := args[1]
codeText, err := generateTOTP(service, user)
if err != nil {
log.Fatal(err)
return
}
out, err := exec.Command(
"aws-vault", "exec", "--mfa-token="+codeText, "-j", profile).CombinedOutput()
fmt.Println(string(out))
if err != nil {
log.Fatalf("aws-vault returned %v", err)
}
},
}

func init() {
rootCmd.AddCommand(vaultCmd)

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// vaultCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// vaultCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
Binary file added images/aws_iam_users_security_credentials.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/get_mfa_secret_key.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/manage_virtual_mfa_device.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/mfa_setup_correctly.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
if [ ! -x "$(command -v keyfob)" ]; then
echo "keyfob is not installed, so I'm going to go grab the mac one for you"
wget -O - https://github.com/StevenACoffman/keyfob/releases/download/v0.1.0/keyfob_0.1.0_Darwin_x86_64.tar.gz | tar xzvf
mkdir -p /usr/local/bin
mv keyfob /usr/local/bin
fi

Expand Down

0 comments on commit b0cee89

Please sign in to comment.