summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Heimes <christian@python.org>2015-10-02 19:48:13 +0200
committerChristian Heimes <christian@python.org>2015-10-02 19:48:13 +0200
commit33d6ae23315ae98b9b1d2692cf2d7618adb0710a (patch)
tree83f17c291a12e4c336c737eeb8134d669e442c68
parenta07fa7ae9d08416de8bc9994adce1353310d4ba3 (diff)
downloadgustodia-33d6ae23315ae98b9b1d2692cf2d7618adb0710a.tar.gz
gustodia-33d6ae23315ae98b9b1d2692cf2d7618adb0710a.tar.xz
gustodia-33d6ae23315ae98b9b1d2692cf2d7618adb0710a.zip
Improved go client. I'm slowly getting the hang out of Golang.
-rw-r--r--gustodia.go206
1 files changed, 160 insertions, 46 deletions
diff --git a/gustodia.go b/gustodia.go
index 9757542..c2316c4 100644
--- a/gustodia.go
+++ b/gustodia.go
@@ -1,21 +1,96 @@
+/* Authors:
+ * Christian Heimes <cheimes@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * Custodia entrypoint for Docker
+ */
package main
import (
+ "bufio"
"fmt"
"encoding/json"
"errors"
+ "log"
"net"
"net/http"
"os"
+ "path"
"strings"
"syscall"
)
const PREFIX string = "CUSTODIA_"
const SECRET_PREFIX string = PREFIX + "SECRET_"
-const BASE_PATH string = "http://localhost/secrets/"
-const SOCKET string = "./server_socket"
+const BASE_PATH string = "http+unix://localhost/secrets/"
+/* Unix Domain Socket transport
+ *
+ */
+
+type UDSTransport struct {
+ SocketPath string
+}
+
+func (uds *UDSTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+ if req.URL == nil {
+ if req.Body != nil {
+ req.Body.Close()
+ }
+ return nil, errors.New("uds: nil Request.URL")
+ }
+ if req.Header == nil {
+ if req.Body != nil {
+ req.Body.Close()
+ }
+ return nil, errors.New("uds: nil Request.Header")
+ }
+ if req.URL.Scheme != "http+unix" {
+ if req.Body != nil {
+ req.Body.Close()
+ }
+ return nil, errors.New("uds: unsupported protocol scheme " + req.URL.Scheme)
+ }
+ if req.URL.Host != "localhost" {
+ if req.Body != nil {
+ req.Body.Close()
+ }
+ return nil, errors.New("uds: Host must be 'localhost'")
+ }
+ conn, err := net.Dial("unix", uds.SocketPath)
+ if err != nil {
+ return nil, err
+ }
+ // XXX: leaks conn
+ req.Write(conn)
+ return http.ReadResponse(bufio.NewReader(conn), req)
+}
+
+func UnixClient(path string) *http.Client {
+ transport := new(http.Transport)
+ uds := &UDSTransport{SocketPath: path}
+ transport.RegisterProtocol("http+unix", uds)
+ client := &http.Client{Transport: transport}
+ return client
+}
+
+/* Custodia client
+ */
type CustodiaMessage struct {
Type string `json:"type"`
Value string `json:"value"`
@@ -25,22 +100,50 @@ type CustodiaSecret struct {
Name string
Value string
Secret string
+ Fetched bool
+}
+
+type CustodiaClient struct {
+ SocketPath string
+ BasePath string
+ Prefix string
+ SecretPrefix string
+ Secrets []*CustodiaSecret
+}
+
+func NewCustodiaClient(socketpath string) *CustodiaClient {
+ return &CustodiaClient{
+ SocketPath: socketpath,
+ BasePath: BASE_PATH,
+ Prefix: PREFIX,
+ SecretPrefix: SECRET_PREFIX,
+ Secrets: []*CustodiaSecret{},
+ }
}
-func unixDial(proto, addr string) (conn net.Conn, err error) {
- typ := "unix"
- conn, err = net.DialUnix(typ, nil, &net.UnixAddr{SOCKET, typ})
- return conn, err
+func (cc *CustodiaClient) FindEnvs() {
+ for _, env := range os.Environ() {
+ if strings.HasPrefix(env, cc.SecretPrefix) {
+ pair := strings.SplitN(env, "=", 2)
+ sec := &CustodiaSecret{
+ Name: pair[0][len(cc.SecretPrefix):],
+ Value: pair[1],
+ Secret: "",
+ Fetched: false,
+ }
+ cc.Secrets = append(cc.Secrets, sec)
+ }
+ }
}
-func query_custodia(secrets []*CustodiaSecret) {
- tr := &http.Transport{
- Dial: unixDial,
+func (cc *CustodiaClient) QueryCustodia() {
+ if len(cc.Secrets) == 0 {
+ return
}
- client := &http.Client{Transport: tr}
+ client := UnixClient(cc.SocketPath)
- for _, sec := range secrets {
- path := BASE_PATH + sec.Value
+ for _, sec := range cc.Secrets {
+ path := cc.BasePath + sec.Value
req, err := http.NewRequest(
"GET", path, nil)
if err != nil {
@@ -49,64 +152,75 @@ func query_custodia(secrets []*CustodiaSecret) {
req.Header.Add("REMOTE_USER", "gustodia")
resp, err := client.Do(req)
if err != nil {
- panic(err)
+ log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
- msg := fmt.Sprintf("%s: %s", path, resp.Status)
- err := errors.New(msg)
- panic(err)
+ log.Fatalf("%v: %v\n", path, resp.Status)
}
+
var m CustodiaMessage
body := json.NewDecoder(resp.Body)
- body.Decode(&m)
- sec.Secret = m.Value;
- }
-}
+ err = body.Decode(&m)
+ if err != nil {
+ log.Fatalf("%v json error: %v", path, err)
+ }
-func get_custodia_envs() ([]*CustodiaSecret) {
- secrets := []*CustodiaSecret{}
- for _, env := range os.Environ() {
- if strings.HasPrefix(env, SECRET_PREFIX) {
- pair := strings.SplitN(env, "=", 2)
- sec := &CustodiaSecret{
- Name: pair[0][len(SECRET_PREFIX):],
- Value: pair[1],
- Secret: "",
- }
- secrets = append(secrets, sec)
+ if len(m.Value) > 0 {
+ sec.Secret = m.Value
+ sec.Fetched = true
}
}
- return secrets
}
-func make_env(secrets []*CustodiaSecret) []string {
+func (cc CustodiaClient) MakeEnv() []string {
environ := []string{}
for _, env := range os.Environ() {
- if ! strings.HasPrefix(env, PREFIX) {
+ if ! strings.HasPrefix(env, cc.Prefix) {
environ = append(environ, env)
}
}
- for _, sec := range secrets {
- env := fmt.Sprintf("%s=%s", sec.Name, sec.Secret)
- environ = append(environ, env)
+ for _, sec := range cc.Secrets {
+ if sec.Fetched {
+ env := fmt.Sprintf("%s=%s", sec.Name, sec.Secret)
+ environ = append(environ, env)
+ }
}
return environ
}
-func main() {
- secrets := get_custodia_envs()
- query_custodia(secrets)
- environ := make_env(secrets)
- /*
- for _, sec := range secrets {
- fmt.Printf("%+v\n", *sec)
+func (cc CustodiaClient) Debug() {
+ fmt.Printf("%v secret(s):\n", len(cc.Secrets))
+ for _, sec := range cc.Secrets {
+ fmt.Printf(" %+v\n", *sec)
}
+ fmt.Println("Environ:")
+ environ := cc.MakeEnv()
for _, env := range environ {
- fmt.Println(env)
+ fmt.Printf(" %v\n", env)
}
- */
+ fmt.Println()
+}
+
+func main() {
+ if len(os.Args) < 2 {
+ log.Fatalf("%s entrypoint [args]", os.Args[0])
+ }
+
+ socketpath := os.Getenv("CUSTODIA_SOCKET")
+ if socketpath == "" {
+ // default socket is in the same directory as program
+ socketpath = path.Join(path.Dir(os.Args[0]), "server_socket")
+ }
+
+ client := NewCustodiaClient(socketpath)
+ client.FindEnvs()
+ client.QueryCustodia()
+ //client.Debug()
+ environ := client.MakeEnv()
+
+
args := os.Args[1:]
syscall.Exec(args[0], args, environ)
}