diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3c6b999 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module github.com/keylockerbv/secrethub-clientd + +require ( + github.com/gorilla/mux v1.7.0 + github.com/keylockerbv/secrethub-go v0.0.0-20190225132925-244d98858e9d + golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6dfd139 --- /dev/null +++ b/go.sum @@ -0,0 +1,38 @@ +bitbucket.org/zombiezen/cardcpx v0.0.0-20150417151802-902f68ff43ef h1:Y5Zf3CYdrdGE7GOuK/MNN98GS1V8mOfeiJlISrKUcEo= +bitbucket.org/zombiezen/cardcpx v0.0.0-20150417151802-902f68ff43ef/go.mod h1:ZJR5FpaQx7Bt2bzIV3gBaCInI1+kG949WhNYYlRr8eA= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448 h1:8tNk6SPXzLDnATTrWoI5Bgw9s/x4uf0kmBpk21NZgI4= +github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4= +github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= +github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +github.com/go-chi/chi v4.0.1+incompatible h1:RSRC5qmFPtO90t7pTL0DBMNpZFsb/sHF3RXVlDgFisA= +github.com/go-chi/chi v4.0.1+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= +github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/keylockerbv/secrethub-go v0.0.0-20190225132925-244d98858e9d h1:NxCFGfkmBF9RaloxF5c/J5knS3FE/g+uWOZ9ENucc1M= +github.com/keylockerbv/secrethub-go v0.0.0-20190225132925-244d98858e9d/go.mod h1:U086plZMagUfy92G4DgCsRAns20Q5j+Rf8bYDXiDxMw= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b h1:+/WWzjwW6gidDJnMKWLKLX1gxn7irUTF1fLpQovfQ5M= +golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +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= diff --git a/main.go b/main.go new file mode 100644 index 0000000..74b532b --- /dev/null +++ b/main.go @@ -0,0 +1,134 @@ +package main + +import ( + "flag" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + + "github.com/gorilla/mux" + "github.com/keylockerbv/secrethub-go/pkg/api" + "github.com/keylockerbv/secrethub-go/pkg/errio" + "github.com/keylockerbv/secrethub-go/pkg/secrethub" +) + +var ( + credential string + credentialPassphrase string + port int + client secrethub.Client +) + +func init() { + flag.StringVar(&credential, "C", "", "(Required) SecretHub credential") + flag.StringVar(&credentialPassphrase, "P", "", "Passphrase to unlock SecretHub credential") + flag.IntVar(&port, "p", 8080, "HTTP port to listen on") + flag.Parse() + + if credential == "" { + flag.Usage() + exit(fmt.Errorf("credential is required")) + } + + cred, err := secrethub.NewCredential(credential, credentialPassphrase) + if err != nil { + exit(err) + } + + client = secrethub.NewClient(cred, nil) +} + +func main() { + err := startHTTPServer() + if err != nil { + exit(err) + } +} + +func startHTTPServer() error { + mux := mux.NewRouter() + v1 := mux.PathPrefix("/v1/").Subrouter() + + v1.PathPrefix("/secrets/").Handler( + http.StripPrefix("/v1/secrets/", http.HandlerFunc(handleSecret)), + ) + + fmt.Println("SecretHub Clientd started, press ^C to exit") + return http.ListenAndServe(fmt.Sprintf(":%v", port), mux) +} + +func handleSecret(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path + err := api.ValidateSecretPath(path) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + io.WriteString(w, err.Error()) + return + } + + switch r.Method { + case "GET": + secret, err := client.Secrets().Versions().GetWithData(path) + if err != nil { + var errCode int + + if err, ok := err.(errio.PublicStatusError); ok { + errCode = err.StatusCode + } + + if errCode == 0 { + errCode = http.StatusInternalServerError + } + + w.WriteHeader(errCode) + io.WriteString(w, err.Error()) + return + } + + w.WriteHeader(http.StatusOK) + w.Write(secret.Data) + case "POST": + secret, err := ioutil.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + io.WriteString(w, err.Error()) + return + } + + _, err = client.Secrets().Write(path, secret) + if err != nil { + var errCode int + + if err, ok := err.(errio.PublicStatusError); ok { + errCode = err.StatusCode + } + + switch err { + case secrethub.ErrCannotWriteToVersion, + secrethub.ErrEmptySecret, + secrethub.ErrSecretTooBig: + errCode = http.StatusBadRequest + } + + if errCode == 0 { + errCode = http.StatusInternalServerError + } + + w.WriteHeader(errCode) + io.WriteString(w, err.Error()) + return + } + + w.WriteHeader(http.StatusCreated) + default: + w.Header().Add("Allow", "GET, POST") + w.WriteHeader(http.StatusMethodNotAllowed) + } +} + +func exit(err error) { + fmt.Printf("secrethub-clientd: error: %v\n", err) + os.Exit(1) +}