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

Embed application config instead of just mTLS #6

Merged
merged 4 commits into from
Oct 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
GOFMT_FILES?=$$(find . -name '*.go' | grep -v vendor)
BINARY_NAME=weep
VERSION=0.1.1
VERSION=0.1.2
REGISTRY=$(REGISTRY)
BRANCH=$(shell git rev-parse --abbrev-ref HEAD)

Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,20 +132,21 @@ AWS_PROFILE=role1 aws s3 ls
In most cases, `weep` can be built by running the `make` command in the repository root. `make release` (requires
[`upx`](https://upx.github.io/)) will build and compress the binary for distribution.

### Embedding mTLS configuration
### Embedded configuration

`weep` binaries can be shipped with an embedded mutual TLS (mTLS) configuration to
avoid making users set this configuration. An example of such a configuration is included
in [mtls/mtls_paths.yaml](mtls/mtls_paths.yaml).
`weep` binaries can be shipped with an embedded configuration to allow shipping an "all-in-one" binary.
An example of such a configuration is included in [example-config.yaml](example-config.yaml).

To compile with an embedded config, set the `MTLS_CONFIG_FILE` environment variable at
To compile with an embedded config, set the `EMBEDDED_CONFIG_FILE` environment variable at
build time. The value of this variable MUST be the **absolute path** of the configuration
file **relative to the root of the module**:

```bash
MTLS_CONFIG_FILE=/mtls/mtls_paths.yaml make
EMBEDDED_CONFIG_FILE=/example-config.yaml make
```

Note that the embedded configuration can be overridden by a configuration file in the locations listed above.

### Docker

#### Building and Running
Expand Down
46 changes: 25 additions & 21 deletions challenge/challenge.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import (
"strings"
"time"

"github.com/spf13/viper"

"github.com/golang/glog"
"github.com/netflix/weep/config"
"github.com/netflix/weep/util"
log "github.com/sirupsen/logrus"
)
Expand Down Expand Up @@ -57,9 +58,6 @@ func NewHTTPClient(consolemeUrl string) (*http.Client, error) {
return nil, err
}
jar.SetCookies(consoleMeUrlParsed, cookies)
if err != nil {
return nil, err
}
client := &http.Client{
Jar: jar,
}
Expand All @@ -79,8 +77,6 @@ func isWSL() bool {
}

func poll(pollingUrl string) (*ConsolemeChallengeResponse, error) {
var pollResponse ConsolemeChallengeResponse
var pollResponseBody []byte
timeout := time.After(2 * time.Minute)
tick := time.Tick(3 * time.Second)
req, err := http.NewRequest("GET", pollingUrl, nil)
Expand All @@ -95,25 +91,30 @@ func poll(pollingUrl string) (*ConsolemeChallengeResponse, error) {
case <-timeout:
return nil, errors.New("*** Unable to validate Challenge Response after 2 minutes. Quitting. ***")
case <-tick:
resp, err := client.Do(req)
pollResponse, err := pollRequest(client, req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.Body != nil {
pollResponseBody, err = ioutil.ReadAll(resp.Body)
err := json.Unmarshal(pollResponseBody, &pollResponse)
if err != nil {
return nil, err
}
if pollResponse.Status == "success" {
return &pollResponse, nil
}
if pollResponse.Status == "success" {
return pollResponse, nil
}
}
}
}

func pollRequest(c *http.Client, r *http.Request) (*ConsolemeChallengeResponse, error) {
var pollResponse ConsolemeChallengeResponse
var pollResponseBody []byte
resp, err := c.Do(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
pollResponseBody, err = ioutil.ReadAll(resp.Body)
err = json.Unmarshal(pollResponseBody, &pollResponse)
return &pollResponse, err
}

func getCredentialsPath() (string, error) {
currentUser, err := user.Current()
if err != nil {
Expand Down Expand Up @@ -158,18 +159,21 @@ func RefreshChallenge() error {
return nil
}
// Step 1: Make unauthed request to ConsoleMe challenge endpoint and get a challenge challenge
if config.Config.ChallengeSettings.User == "" {
if viper.GetString("challenge_settings.user") == "" {
log.Fatalf(
"Invalid configuration. You must define challenge_settings.user as the user you wish to authenticate as.",
)
}
var consoleMeChallengeGeneratorEndpoint string = fmt.Sprintf(
var consoleMeChallengeGeneratorEndpoint = fmt.Sprintf(
"%s/noauth/v1/challenge_generator/%s",
config.Config.ConsoleMeUrl,
config.Config.ChallengeSettings.User,
viper.GetString("consoleme_url"),
viper.GetString("challenge_settings.user"),
)
var challenge ConsolemeChallenge
req, err := http.NewRequest("GET", consoleMeChallengeGeneratorEndpoint, nil)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
Expand Down
20 changes: 14 additions & 6 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,22 @@ func initConfig() {
viper.AddConfigPath(home + "/.config/weep/")
}

err := viper.ReadInConfig()
if err == nil {
log.Debug("Found config")
err = viper.Unmarshal(&config.Config)
if err != nil {
log.Fatalf("unable to decode into struct, %v", err)
if err := config.ReadEmbeddedConfig(); err != nil {
log.Errorf("unable to read embedded config: %v; falling back to config file", err)
}

if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok && config.EmbeddedConfigFile != "" {
log.Debug("no config file found, trying to use embedded config")
} else {
log.Fatalf("unable to read config file: %v", err)
}
}

log.Debug("Found config")
if err := viper.Unmarshal(&config.Config); err != nil {
log.Fatalf("unable to decode config into struct: %v", err)
}
}

func initLogging() {
Expand Down
14 changes: 0 additions & 14 deletions config.yml

This file was deleted.

11 changes: 7 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ type MetaDataConfig struct {
}

type MtlsSettings struct {
Cert string `mapstructure:"cert"`
Key string `mapstructure:"key"`
CATrust string `mapstructure:"catrust"`
Insecure bool `mapstructure:"insecure"`
Cert string `mapstructure:"cert"`
Key string `mapstructure:"key"`
CATrust string `mapstructure:"catrust"`
Insecure bool `mapstructure:"insecure"`
Darwin []string `mapstructure:"darwin"`
Linux []string `mapstructure:"linux"`
Windows []string `mapstructure:"windows"`
}

type ChallengeSettings struct {
Expand Down
29 changes: 29 additions & 0 deletions config/embedded.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package config

import (
"github.com/markbates/pkger"
"github.com/pkg/errors"
"github.com/spf13/viper"
)

var (
EmbeddedConfigFile string // To be set by ldflags at compile time
)

// ReadEmbeddedConfig attempts to read the embedded mTLS config and create a tls.Config
func ReadEmbeddedConfig() error {
if EmbeddedConfigFile == "" {
return EmbeddedConfigDisabledError
}
f, err := pkger.Open(EmbeddedConfigFile)
if err != nil {
return errors.Wrap(err, "could not open embedded config")
}
defer f.Close()

err = viper.ReadConfig(f)
if err != nil {
return errors.Wrap(err, "could not read embedded config")
}
return nil
}
11 changes: 11 additions & 0 deletions config/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package config

type Error string

func (e Error) Error() string { return string(e) }

const ClientCertificatesNotFoundError = Error("could not find client certificates")
const EmbeddedConfigDisabledError = Error("embedded config is disabled")
const HomeDirectoryError = Error("could not resolve user's home directory")
const MissingTLSConfigError = Error("missing required mTLS configuration")
const UnsupportedOSError = Error("running on unsupported operating system")
7 changes: 4 additions & 3 deletions consoleme/consoleme.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import (
"syscall"
"time"

"github.com/spf13/viper"

"github.com/netflix/weep/challenge"
"github.com/netflix/weep/config"
"github.com/netflix/weep/mtls"
log "github.com/sirupsen/logrus"

Expand Down Expand Up @@ -44,8 +45,8 @@ type Client struct {
// GetClient creates an authenticated ConsoleMe client
func GetClient() (*Client, error) {
var client *Client
consoleMeUrl := config.Config.ConsoleMeUrl
authenticationMethod := config.Config.AuthenticationMethod
consoleMeUrl := viper.GetString("consoleme_url")
authenticationMethod := viper.GetString("authentication_method")

if authenticationMethod == "mtls" {
mtlsClient, err := mtls.NewHTTPClient()
Expand Down
29 changes: 29 additions & 0 deletions example-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
consoleme_url: https://path_to_consoleme:port
authentication_method: mtls # challenge or mtls
mtls_settings:
cert: mtls.crt
key: mtls.key
cafile: mtlsCA.pem
insecure: false
darwin: # weep will look in platform-specific directories for the three files specified above
- "/run/mtls/certificates"
- "/mtls/certificates"
- "$HOME/.mtls/certificates"
- "$HOME/.mtls"
linux:
- "/run/mtls/certificates"
- "/mtls/certificates"
- "$HOME/.mtls/certificates"
- "$HOME/.mtls"
windows:
- "C:\\run\\mtls\\certificates"
- "C:\\mtls\\certificates"
- "$HOME\\.mtls\\certificates"
- "$HOME\\.mtls"
metadata:
routes:
- path: latest/user-data
- path: latest/meta-data/local-ipv4
data: "127.0.0.1"
- path: latest/meta-data/local-hostname
data: ip-127-0-0-1.us-west-2.compute.internal
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@ require (
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc // indirect
golang.org/x/sys v0.0.0-20200828081204-131dc92a58d5 // indirect
gopkg.in/ini.v1 v1.62.0
gopkg.in/yaml.v2 v2.3.0
)
6 changes: 3 additions & 3 deletions handlers/customHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ func CustomHandler(w http.ResponseWriter, r *http.Request) {

path := mux.Vars(r)["path"]

for i := range config.Config.MetaData.Routes {
if config.Config.MetaData.Routes[i].Path == path {
fmt.Fprintln(w, config.Config.MetaData.Routes[i].Data)
for _, configRoute := range config.Config.MetaData.Routes {
if configRoute.Path == path {
fmt.Fprintln(w, configRoute.Path)
}
}
}
Loading