Skip to content

Commit

Permalink
Add basic auth (#54)
Browse files Browse the repository at this point in the history
* Add basic auth for submission.

This commit adds the help text to allow omitting the password, but that
is TODO due to the added complexity and dependencies.

* Add prompting for missing password arg.

* Add testing of basic auth.

* Cast password to string for opts use.

* Fix Opts struct names.

* Add README info about basic auth flags.

* Remove redundant test handler func.

* fix indentation of authuser and authpass

Co-authored-by: Derek <[email protected]>
  • Loading branch information
sheagcraig and derektamsen authored Jun 21, 2020
1 parent a73eef7 commit 3ed9a8e
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 49 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,17 @@ Setting the admin password and escrowing it post imaging:
--currentpassword "<password_to_replace>" \
--cryptserver "<cryptserver.example.com>"

If your Crypt server uses basic authentication to protect the checkin endpoint:

sudo luks2crypt postimaging \
--luksdevice "<device_to_manage>" \
--currentpassword "<password_to_replace>" \
--cryptserver "<cryptserver.example.com>" \
--authuser "<basic auth username>" \
--authpass "<basic auth password>"

If you omit the password, luk2crypt will prompt for one.

Development
-----------

Expand Down
24 changes: 20 additions & 4 deletions cmd/luks2crypt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/square/luks2crypt/pkg/postimaging"

"golang.org/x/crypto/ssh/terminal"
cli "gopkg.in/urfave/cli.v1"
)

Expand Down Expand Up @@ -49,6 +50,10 @@ func run(args []string) error {
cli.StringFlag{Name: "cryptendpoint, e",
Usage: "The Crypt Server endpoint to use when escrowing keys",
Value: "/checkin/"},
cli.StringFlag{Name: "authuser, u",
Usage: "Basic auth username for Crypt server."},
cli.StringFlag{Name: "authpass, P",
Usage: "Basic auth password for Crypt server. If omitted and authuser/u is specified, the password will be prompted for on the terminal."},
},
},
}
Expand All @@ -66,10 +71,21 @@ func optVersion(c *cli.Context) error {
func optPostImaging(c *cli.Context) error {
cryptURL := "https://" + c.String("cryptserver")
opts := postimaging.Opts{
LuksDev: c.String("luksdevice"),
CurPass: c.String("currentpassword"),
Server: cryptURL,
URI: c.String("cryptendpoint"),
LuksDev: c.String("luksdevice"),
CurPass: c.String("currentpassword"),
Server: cryptURL,
URI: c.String("cryptendpoint"),
AuthUser: c.String("authuser"),
AuthPass: c.String("authpass"),
}
if (opts.AuthUser != "") && (opts.AuthPass == "") {
fmt.Printf("Password: ")
password, err := terminal.ReadPassword(0)
if err != nil {
err = fmt.Errorf("error getting basic auth password: %v", err)
return cli.NewExitError(err, 1)
}
opts.AuthPass = string(password)
}
err := postimaging.Run(opts)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/kr/pretty v0.1.0 // indirect
github.com/satori/go.uuid v1.2.0 // indirect
github.com/sethvargo/go-diceware v0.2.0
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/urfave/cli.v1 v1.20.0
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sethvargo/go-diceware v0.2.0 h1:3QzXGqUe0UR9y1XYSz1dxGS+fKtXOxRqqKjy+cG1yTI=
github.com/sethvargo/go-diceware v0.2.0/go.mod h1:II+37A5sTGAtg3zd/JqyVQ8qqAjSm/2r2X6qkVZDjyg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
Expand Down
15 changes: 13 additions & 2 deletions internal/escrow/escrow.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ package escrow
import (
"net/http"
"net/url"
"strings"

"github.com/gorilla/schema"
)

// CryptServerInfo is used to create an object with info about the escrow server
type CryptServerInfo struct {
Server, URI string
Server, URI, Username, Password string
}

// CryptServerData stores the data to be escrowed
Expand All @@ -42,7 +43,17 @@ func (data CryptServerData) PostCryptServer(escrowServer CryptServerInfo) (*http
}

client := new(http.Client)
res, err := client.PostForm(cryptServer, form)
req, err := http.NewRequest("POST", cryptServer, strings.NewReader(form.Encode()))
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

if (escrowServer.Username != "") && (escrowServer.Password != "") {
req.SetBasicAuth(escrowServer.Username, escrowServer.Password)
}

res, err := client.Do(req)
if err != nil {
return nil, err
}
Expand Down
95 changes: 55 additions & 40 deletions internal/escrow/escrow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,53 +20,68 @@ func TestPostCryptServer(t *testing.T) {
Username: "tester",
}

mockCryptServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
basicauth := []string{"DarthHelmet", "12345"}
tests := []struct {
name string
username string
password string
}{
{name: "No auth escrow"},
{name: "Basic auth escrow", username: basicauth[0], password: basicauth[1]},
}

if r.Method != "POST" {
t.Errorf("expected 'POST' request, got '%s'", r.Method)
}
for _, tc := range tests {
mockCryptServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)

if r.URL.EscapedPath() != "/checkin/" {
t.Errorf("expected request to '/checkin', got '%s'", r.URL.EscapedPath())
}
if r.Method != "POST" {
t.Errorf("expected 'POST' request, got '%s'", r.Method)
}

// cryptserver expects the following form data recovery_password, serial,
// macname, username
// see: https://github.com/grahamgilbert/Crypt-Server/blob/master/server/views.py#L442
r.ParseForm()
actual := r.Form.Get("recovery_password")
if actual != expected.Pass {
t.Errorf("expected 'recovery_password=%v' got %v", expected.Pass, actual)
}
if r.URL.EscapedPath() != "/checkin/" {
t.Errorf("expected request to '/checkin', got '%s'", r.URL.EscapedPath())
}

actual = r.Form.Get("serial")
if actual != expected.Serialnum {
t.Errorf("expected 'serial=%v' got %v", expected.Serialnum, actual)
}
authusername, authpassword, _ := r.BasicAuth()
if authusername != tc.username || authpassword != tc.password {
t.Errorf("expected '%s:%s' got '%s:%s'", tc.username, tc.password, authusername, authpassword)
}

actual = r.Form.Get("macname")
if actual != expected.Hostname {
t.Errorf("expected 'macname=%v' got %v", expected.Hostname, actual)
}
// cryptserver expects the following form data recovery_password, serial,
// macname, username
// see: https://github.com/grahamgilbert/Crypt-Server/blob/master/server/views.py#L442
r.ParseForm()
actual := r.Form.Get("recovery_password")
if actual != expected.Pass {
t.Errorf("expected 'recovery_password=%v' got %v", expected.Pass, actual)
}

actual = r.Form.Get("username")
if actual != expected.Username {
t.Errorf("expected 'username=%v' got %v", expected.Username, actual)
}
}))
defer mockCryptServer.Close()
actual = r.Form.Get("serial")
if actual != expected.Serialnum {
t.Errorf("expected 'serial=%v' got %v", expected.Serialnum, actual)
}

endpoint := CryptServerInfo{
Server: mockCryptServer.URL,
URI: "/checkin/",
}
actual = r.Form.Get("macname")
if actual != expected.Hostname {
t.Errorf("expected 'macname=%v' got %v", expected.Hostname, actual)
}

resp, err := expected.PostCryptServer(endpoint)
if err != nil {
t.Errorf("errored posting escrow data to mock cryptserver with %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("errored posting escrow data to mock cryptserver expected 200 got %v", resp.StatusCode)
actual = r.Form.Get("username")
if actual != expected.Username {
t.Errorf("expected 'username=%v' got %v", expected.Username, actual)
}
}))
defer mockCryptServer.Close()

endpoint := CryptServerInfo{Server: mockCryptServer.URL, URI: "/checkin/", Username: tc.username, Password: tc.password}
t.Run(tc.name, func(t *testing.T) {
resp, err := expected.PostCryptServer(endpoint)
if err != nil {
t.Errorf("errored posting escrow data to mock cryptserver with %v", err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("errored posting escrow data to mock cryptserver expected 200 got %v", resp.StatusCode)
}
})
}
}
8 changes: 5 additions & 3 deletions pkg/postimaging/postimaging.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ import (

// Opts is used to store the options needed for postimaging functions
type Opts struct {
LuksDev, CurPass, Server, URI string
LuksDev, CurPass, Server, URI, AuthUser, AuthPass string
}

// Run post imaging password creation, set, and escrow. Returns an error
func Run(opts Opts) error {
cryptServerInfo := escrow.CryptServerInfo{
Server: opts.Server,
URI: opts.URI,
Server: opts.Server,
URI: opts.URI,
Username: opts.AuthUser,
Password: opts.AuthPass,
}
cryptServerData := escrow.CryptServerData{}

Expand Down

0 comments on commit 3ed9a8e

Please sign in to comment.