Skip to main content
Sign in
Snippets Groups Projects
Commit 17b17fc8 authored by Niall Sheridan's avatar Niall Sheridan
Browse files

Add more context to errors

parent d3d2d538
Branches
Tags
No related merge requests found
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"time" "time"
"github.com/nsheridan/cashier/lib" "github.com/nsheridan/cashier/lib"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent" "golang.org/x/crypto/ssh/agent"
) )
...@@ -27,7 +28,7 @@ func InstallCert(a agent.Agent, cert *ssh.Certificate, key Key) error { ...@@ -27,7 +28,7 @@ func InstallCert(a agent.Agent, cert *ssh.Certificate, key Key) error {
LifetimeSecs: uint32(lifetime), LifetimeSecs: uint32(lifetime),
} }
if err := a.Add(pubcert); err != nil { if err := a.Add(pubcert); err != nil {
return fmt.Errorf("error importing certificate: %s", err) return errors.Wrap(err, "unable to add cert to ssh agent")
} }
privkey := agent.AddedKey{ privkey := agent.AddedKey{
PrivateKey: key, PrivateKey: key,
...@@ -35,7 +36,7 @@ func InstallCert(a agent.Agent, cert *ssh.Certificate, key Key) error { ...@@ -35,7 +36,7 @@ func InstallCert(a agent.Agent, cert *ssh.Certificate, key Key) error {
LifetimeSecs: uint32(lifetime), LifetimeSecs: uint32(lifetime),
} }
if err := a.Add(privkey); err != nil { if err := a.Add(privkey); err != nil {
return fmt.Errorf("error importing key: %s", err) return errors.Wrap(err, "unable to add private key to ssh agent")
} }
return nil return nil
} }
...@@ -48,7 +49,7 @@ func send(s []byte, token, ca string, ValidateTLSCertificate bool) (*lib.SignRes ...@@ -48,7 +49,7 @@ func send(s []byte, token, ca string, ValidateTLSCertificate bool) (*lib.SignRes
client := &http.Client{Transport: transport} client := &http.Client{Transport: transport}
u, err := url.Parse(ca) u, err := url.Parse(ca)
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "unable to parse CA url")
} }
u.Path = path.Join(u.Path, "/sign") u.Path = path.Join(u.Path, "/sign")
req, err := http.NewRequest("POST", u.String(), bytes.NewReader(s)) req, err := http.NewRequest("POST", u.String(), bytes.NewReader(s))
...@@ -68,7 +69,7 @@ func send(s []byte, token, ca string, ValidateTLSCertificate bool) (*lib.SignRes ...@@ -68,7 +69,7 @@ func send(s []byte, token, ca string, ValidateTLSCertificate bool) (*lib.SignRes
defer resp.Body.Close() defer resp.Body.Close()
c := &lib.SignResponse{} c := &lib.SignResponse{}
if err := json.NewDecoder(resp.Body).Decode(c); err != nil { if err := json.NewDecoder(resp.Body).Decode(c); err != nil {
return nil, err return nil, errors.Wrap(err, "unable to decode server response")
} }
return c, nil return c, nil
} }
...@@ -84,22 +85,22 @@ func Sign(pub ssh.PublicKey, token string, conf *Config) (*ssh.Certificate, erro ...@@ -84,22 +85,22 @@ func Sign(pub ssh.PublicKey, token string, conf *Config) (*ssh.Certificate, erro
ValidUntil: time.Now().Add(validity), ValidUntil: time.Now().Add(validity),
}) })
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "unable to create sign request")
} }
resp, err := send(s, token, conf.CA, conf.ValidateTLSCertificate) resp, err := send(s, token, conf.CA, conf.ValidateTLSCertificate)
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "error sending request to CA")
} }
if resp.Status != "ok" { if resp.Status != "ok" {
return nil, fmt.Errorf("error: %s", resp.Response) return nil, fmt.Errorf("bad response from CA: %s", resp.Response)
} }
k, _, _, _, err := ssh.ParseAuthorizedKey([]byte(resp.Response)) k, _, _, _, err := ssh.ParseAuthorizedKey([]byte(resp.Response))
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "unable to parse response")
} }
cert, ok := k.(*ssh.Certificate) cert, ok := k.(*ssh.Certificate)
if !ok { if !ok {
return nil, fmt.Errorf("did not receive a certificate from server") return nil, fmt.Errorf("did not receive a valid certificate from server")
} }
return cert, nil return cert, nil
} }
...@@ -8,6 +8,8 @@ import ( ...@@ -8,6 +8,8 @@ import (
"crypto/rsa" "crypto/rsa"
"fmt" "fmt"
"github.com/pkg/errors"
"golang.org/x/crypto/ed25519" "golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
...@@ -68,7 +70,7 @@ func generateECDSAKey(size int) (Key, error) { ...@@ -68,7 +70,7 @@ func generateECDSAKey(size int) (Key, error) {
case 521: case 521:
curve = elliptic.P521() curve = elliptic.P521()
default: default:
return nil, fmt.Errorf("Unsupported key size: %d. Valid sizes are '256', '384', '521'", size) return nil, fmt.Errorf("Unsupported ECDSA key size: %d. Valid sizes are '256', '384', '521'", size)
} }
return ecdsa.GenerateKey(curve, rand.Reader) return ecdsa.GenerateKey(curve, rand.Reader)
} }
...@@ -101,8 +103,8 @@ func GenerateKey(options ...func(*options)) (Key, ssh.PublicKey, error) { ...@@ -101,8 +103,8 @@ func GenerateKey(options ...func(*options)) (Key, ssh.PublicKey, error) {
privkey, err = generateRSAKey(config.size) privkey, err = generateRSAKey(config.size)
} }
if err != nil { if err != nil {
return nil, nil, err return nil, nil, errors.Wrapf(err, "unable to generate %s key-pair", config.keytype)
} }
pubkey, err = ssh.NewPublicKey(privkey.Public()) pubkey, err = ssh.NewPublicKey(privkey.Public())
return privkey, pubkey, err return privkey, pubkey, errors.Wrap(err, "error parsing public key")
} }
...@@ -5,7 +5,6 @@ import ( ...@@ -5,7 +5,6 @@ import (
"crypto/tls" "crypto/tls"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors"
"flag" "flag"
"fmt" "fmt"
"html/template" "html/template"
...@@ -17,6 +16,8 @@ import ( ...@@ -17,6 +16,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/pkg/errors"
"go4.org/wkfs" "go4.org/wkfs"
"golang.org/x/crypto/acme/autocert" "golang.org/x/crypto/acme/autocert"
"golang.org/x/oauth2" "golang.org/x/oauth2"
...@@ -125,7 +126,7 @@ func (a *appContext) login(w http.ResponseWriter, r *http.Request) (int, error) ...@@ -125,7 +126,7 @@ func (a *appContext) login(w http.ResponseWriter, r *http.Request) (int, error)
} }
// parseKey retrieves and unmarshals the signing request. // parseKey retrieves and unmarshals the signing request.
func parseKey(r *http.Request) (*lib.SignRequest, error) { func extractKey(r *http.Request) (*lib.SignRequest, error) {
var s lib.SignRequest var s lib.SignRequest
if err := json.NewDecoder(r.Body).Decode(&s); err != nil { if err := json.NewDecoder(r.Body).Decode(&s); err != nil {
return nil, err return nil, err
...@@ -154,23 +155,25 @@ func signHandler(a *appContext, w http.ResponseWriter, r *http.Request) (int, er ...@@ -154,23 +155,25 @@ func signHandler(a *appContext, w http.ResponseWriter, r *http.Request) (int, er
} }
// Sign the pubkey and issue the cert. // Sign the pubkey and issue the cert.
req, err := parseKey(r) req, err := extractKey(r)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return http.StatusBadRequest, errors.Wrap(err, "unable to extract key from request")
} }
username := a.authprovider.Username(token) username := a.authprovider.Username(token)
a.authprovider.Revoke(token) // We don't need this anymore. a.authprovider.Revoke(token) // We don't need this anymore.
cert, err := a.sshKeySigner.SignUserKey(req, username) cert, err := a.sshKeySigner.SignUserKey(req, username)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, errors.Wrap(err, "error signing key")
} }
if err := a.certstore.SetCert(cert); err != nil { if err := a.certstore.SetCert(cert); err != nil {
log.Printf("Error recording cert: %v", err) log.Printf("Error recording cert: %v", err)
} }
json.NewEncoder(w).Encode(&lib.SignResponse{ if err := json.NewEncoder(w).Encode(&lib.SignResponse{
Status: "ok", Status: "ok",
Response: lib.GetPublicKey(cert), Response: lib.GetPublicKey(cert),
}) }); err != nil {
return http.StatusInternalServerError, errors.Wrap(err, "error encoding response")
}
return http.StatusOK, nil return http.StatusOK, nil
} }
...@@ -219,7 +222,7 @@ func listRevokedCertsHandler(a *appContext, w http.ResponseWriter, r *http.Reque ...@@ -219,7 +222,7 @@ func listRevokedCertsHandler(a *appContext, w http.ResponseWriter, r *http.Reque
} }
rl, err := a.sshKeySigner.GenerateRevocationList(revoked) rl, err := a.sshKeySigner.GenerateRevocationList(revoked)
if err != nil { if err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, errors.Wrap(err, "unable to generate KRL")
} }
w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Type", "application/octet-stream")
w.Write(rl) w.Write(rl)
...@@ -258,7 +261,7 @@ func revokeCertHandler(a *appContext, w http.ResponseWriter, r *http.Request) (i ...@@ -258,7 +261,7 @@ func revokeCertHandler(a *appContext, w http.ResponseWriter, r *http.Request) (i
r.ParseForm() r.ParseForm()
for _, id := range r.Form["cert_id"] { for _, id := range r.Form["cert_id"] {
if err := a.certstore.Revoke(id); err != nil { if err := a.certstore.Revoke(id); err != nil {
return http.StatusInternalServerError, err return http.StatusInternalServerError, errors.Wrap(err, "unable to revoke")
} }
} }
http.Redirect(w, r, "/admin/certs", http.StatusSeeOther) http.Redirect(w, r, "/admin/certs", http.StatusSeeOther)
...@@ -292,7 +295,7 @@ func newState() string { ...@@ -292,7 +295,7 @@ func newState() string {
func readConfig(filename string) (*config.Config, error) { func readConfig(filename string) (*config.Config, error) {
f, err := os.Open(filename) f, err := os.Open(filename)
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "failed to parse config file")
} }
defer f.Close() defer f.Close()
return config.ReadConfig(f) return config.ReadConfig(f)
...@@ -301,11 +304,11 @@ func readConfig(filename string) (*config.Config, error) { ...@@ -301,11 +304,11 @@ func readConfig(filename string) (*config.Config, error) {
func loadCerts(certFile, keyFile string) (tls.Certificate, error) { func loadCerts(certFile, keyFile string) (tls.Certificate, error) {
key, err := wkfs.ReadFile(keyFile) key, err := wkfs.ReadFile(keyFile)
if err != nil { if err != nil {
return tls.Certificate{}, err return tls.Certificate{}, errors.Wrap(err, "error reading TLS private key")
} }
cert, err := wkfs.ReadFile(certFile) cert, err := wkfs.ReadFile(certFile)
if err != nil { if err != nil {
return tls.Certificate{}, err return tls.Certificate{}, errors.Wrap(err, "error reading TLS certificate")
} }
return tls.X509KeyPair(cert, key) return tls.X509KeyPair(cert, key)
} }
...@@ -338,14 +341,15 @@ func main() { ...@@ -338,14 +341,15 @@ func main() {
if conf.Server.HTTPLogFile != "" { if conf.Server.HTTPLogFile != "" {
logfile, err = os.OpenFile(conf.Server.HTTPLogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640) logfile, err = os.OpenFile(conf.Server.HTTPLogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
if err != nil { if err != nil {
log.Fatal(err) log.Printf("unable to open %s for writing. logging to stdout", conf.Server.HTTPLogFile)
logfile = os.Stderr
} }
} }
laddr := fmt.Sprintf("%s:%d", conf.Server.Addr, conf.Server.Port) laddr := fmt.Sprintf("%s:%d", conf.Server.Addr, conf.Server.Port)
l, err := net.Listen("tcp", laddr) l, err := net.Listen("tcp", laddr)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(errors.Wrapf(err, "unable to listen on %s:%d", conf.Server.Addr, conf.Server.Port))
} }
tlsConfig := &tls.Config{} tlsConfig := &tls.Config{}
...@@ -364,7 +368,7 @@ func main() { ...@@ -364,7 +368,7 @@ func main() {
tlsConfig.Certificates = make([]tls.Certificate, 1) tlsConfig.Certificates = make([]tls.Certificate, 1)
tlsConfig.Certificates[0], err = loadCerts(conf.Server.TLSCert, conf.Server.TLSKey) tlsConfig.Certificates[0], err = loadCerts(conf.Server.TLSCert, conf.Server.TLSKey)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(errors.Wrap(err, "unable to create TLS listener"))
} }
} }
l = tls.NewListener(l, tlsConfig) l = tls.NewListener(l, tlsConfig)
...@@ -373,7 +377,7 @@ func main() { ...@@ -373,7 +377,7 @@ func main() {
if conf.Server.User != "" { if conf.Server.User != "" {
log.Print("Dropping privileges...") log.Print("Dropping privileges...")
if err := drop.DropPrivileges(conf.Server.User); err != nil { if err := drop.DropPrivileges(conf.Server.User); err != nil {
log.Fatal(err) log.Fatal(errors.Wrap(err, "unable to drop privileges"))
} }
} }
...@@ -388,7 +392,7 @@ func main() { ...@@ -388,7 +392,7 @@ func main() {
log.Fatalf("Unknown provider %s\n", conf.Auth.Provider) log.Fatalf("Unknown provider %s\n", conf.Auth.Provider)
} }
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(errors.Wrapf(err, "unable to use provider '%s'", conf.Auth.Provider))
} }
certstore, err := store.New(conf.Server.Database) certstore, err := store.New(conf.Server.Database)
... ...
......
...@@ -32,7 +32,7 @@ func New(c *config.Auth) (auth.Provider, error) { ...@@ -32,7 +32,7 @@ func New(c *config.Auth) (auth.Provider, error) {
uw[u] = true uw[u] = true
} }
if c.ProviderOpts["organization"] == "" && len(uw) == 0 { if c.ProviderOpts["organization"] == "" && len(uw) == 0 {
return nil, errors.New("github_opts organization and the users whitelist must not be both empty") return nil, errors.New("either GitHub organization or users whitelist must be specified")
} }
return &Config{ return &Config{
config: &oauth2.Config{ config: &oauth2.Config{
... ...
......
...@@ -29,11 +29,9 @@ func TestNew(t *testing.T) { ...@@ -29,11 +29,9 @@ func TestNew(t *testing.T) {
func TestNewEmptyOrganization(t *testing.T) { func TestNewEmptyOrganization(t *testing.T) {
organization = "" organization = ""
a := assert.New(t) if _, err := newGithub(); err == nil {
t.Error("creating a provider without an organization set should return an error")
_, err := newGithub() }
a.EqualError(err, "github_opts organization and the users whitelist must not be both empty")
organization = "exampleorg" organization = "exampleorg"
} }
... ...
......
...@@ -34,7 +34,7 @@ func New(c *config.Auth) (auth.Provider, error) { ...@@ -34,7 +34,7 @@ func New(c *config.Auth) (auth.Provider, error) {
uw[u] = true uw[u] = true
} }
if c.ProviderOpts["domain"] == "" && len(uw) == 0 { if c.ProviderOpts["domain"] == "" && len(uw) == 0 {
return nil, errors.New("google_opts domain and the users whitelist must not be both empty") return nil, errors.New("either Google Apps domain or users whitelist must be specified")
} }
return &Config{ return &Config{
... ...
......
...@@ -28,12 +28,11 @@ func TestNew(t *testing.T) { ...@@ -28,12 +28,11 @@ func TestNew(t *testing.T) {
} }
func TestNewWithoutDomain(t *testing.T) { func TestNewWithoutDomain(t *testing.T) {
a := assert.New(t)
domain = "" domain = ""
_, err := newGoogle() if _, err := newGoogle(); err == nil {
a.EqualError(err, "google_opts domain and the users whitelist must not be both empty") t.Error("creating a provider without a domain set should return an error")
}
domain = "example.com" domain = "example.com"
} }
... ...
......
package config package config
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
...@@ -12,6 +11,7 @@ import ( ...@@ -12,6 +11,7 @@ import (
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"github.com/nsheridan/cashier/server/helpers/vault" "github.com/nsheridan/cashier/server/helpers/vault"
"github.com/pkg/errors"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
...@@ -156,14 +156,14 @@ func setFromVault(c *Config) error { ...@@ -156,14 +156,14 @@ func setFromVault(c *Config) error {
} }
v, err := vault.NewClient(c.Vault.Address, c.Vault.Token) v, err := vault.NewClient(c.Vault.Address, c.Vault.Token)
if err != nil { if err != nil {
return err return errors.Wrap(err, "vault error")
} }
var errors error var errs error
get := func(value string) string { get := func(value string) string {
if strings.HasPrefix(value, "/vault/") { if strings.HasPrefix(value, "/vault/") {
s, err := v.Read(value) s, err := v.Read(value)
if err != nil { if err != nil {
errors = multierror.Append(errors, err) errs = multierror.Append(errs, err)
} }
return s return s
} }
...@@ -180,12 +180,12 @@ func setFromVault(c *Config) error { ...@@ -180,12 +180,12 @@ func setFromVault(c *Config) error {
c.AWS.AccessKey = get(c.AWS.AccessKey) c.AWS.AccessKey = get(c.AWS.AccessKey)
c.AWS.SecretKey = get(c.AWS.SecretKey) c.AWS.SecretKey = get(c.AWS.SecretKey)
} }
return errors return errors.Wrap(errs, "errors reading from vault")
} }
// Unmarshal the config into a *Config // Unmarshal the config into a *Config
func decode() (*Config, error) { func decode() (*Config, error) {
var errors error var errs error
config := &Config{} config := &Config{}
configPieces := map[string]interface{}{ configPieces := map[string]interface{}{
"auth": &config.Auth, "auth": &config.Auth,
...@@ -200,21 +200,21 @@ func decode() (*Config, error) { ...@@ -200,21 +200,21 @@ func decode() (*Config, error) {
continue continue
} }
if err := mapstructure.WeakDecode(conf[0], val); err != nil { if err := mapstructure.WeakDecode(conf[0], val); err != nil {
errors = multierror.Append(errors, err) errs = multierror.Append(errs, err)
} }
} }
return config, errors return config, errs
} }
// ReadConfig parses a hcl configuration file into a Config struct. // ReadConfig parses a hcl configuration file into a Config struct.
func ReadConfig(r io.Reader) (*Config, error) { func ReadConfig(r io.Reader) (*Config, error) {
viper.SetConfigType("hcl") viper.SetConfigType("hcl")
if err := viper.ReadConfig(r); err != nil { if err := viper.ReadConfig(r); err != nil {
return nil, err return nil, errors.Wrap(err, "unable to read config")
} }
config, err := decode() config, err := decode()
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "unable to parse config")
} }
if err := setFromVault(config); err != nil { if err := setFromVault(config); err != nil {
return nil, err return nil, err
...@@ -222,7 +222,7 @@ func ReadConfig(r io.Reader) (*Config, error) { ...@@ -222,7 +222,7 @@ func ReadConfig(r io.Reader) (*Config, error) {
setFromEnvironment(config) setFromEnvironment(config)
convertDatastoreConfig(config) convertDatastoreConfig(config)
if err := verifyConfig(config); err != nil { if err := verifyConfig(config); err != nil {
return nil, err return nil, errors.Wrap(err, "unable to verify config")
} }
return config, nil return config, nil
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment