From a30d6403f723765b8f9b7609e7eb3ade0f5434a0 Mon Sep 17 00:00:00 2001 From: Niall Sheridan Date: Sat, 10 Sep 2016 17:40:23 +0100 Subject: Make client a top-level package for consistency --- client/client.go | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 client/client.go (limited to 'client/client.go') diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..ba5b900 --- /dev/null +++ b/client/client.go @@ -0,0 +1,113 @@ +package client + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "path" + "time" + + "github.com/nsheridan/cashier/lib" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" +) + +// InstallCert adds the private key and signed certificate to the ssh agent. +func InstallCert(a agent.Agent, cert *ssh.Certificate, key Key) error { + t := time.Unix(int64(cert.ValidBefore), 0) + lifetime := t.Sub(time.Now()).Seconds() + comment := fmt.Sprintf("%s [Expires %s]", cert.KeyId, t) + pubcert := agent.AddedKey{ + PrivateKey: key, + Certificate: cert, + Comment: comment, + LifetimeSecs: uint32(lifetime), + } + if err := a.Add(pubcert); err != nil { + return fmt.Errorf("error importing certificate: %s", err) + } + privkey := agent.AddedKey{ + PrivateKey: key, + Comment: comment, + LifetimeSecs: uint32(lifetime), + } + if err := a.Add(privkey); err != nil { + return fmt.Errorf("error importing key: %s", err) + } + return nil +} + +// send the signing request to the CA. +func send(s []byte, token, ca string, ValidateTLSCertificate bool) (*lib.SignResponse, error) { + transport := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: !ValidateTLSCertificate}, + } + client := &http.Client{Transport: transport} + u, err := url.Parse(ca) + if err != nil { + return nil, err + } + u.Path = path.Join(u.Path, "/sign") + req, err := http.NewRequest("POST", u.String(), bytes.NewReader(s)) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Add("Accept", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) + resp, err := client.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("Bad response from server: %s", resp.Status) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + c := &lib.SignResponse{} + if err := json.Unmarshal(body, c); err != nil { + return nil, err + } + return c, nil +} + +// Sign sends the public key to the CA to be signed. +func Sign(pub ssh.PublicKey, token string, conf *Config) (*ssh.Certificate, error) { + validity, err := time.ParseDuration(conf.Validity) + if err != nil { + return nil, err + } + marshaled := ssh.MarshalAuthorizedKey(pub) + // Remove the trailing newline. + marshaled = marshaled[:len(marshaled)-1] + s, err := json.Marshal(&lib.SignRequest{ + Key: string(marshaled), + ValidUntil: time.Now().Add(validity), + }) + if err != nil { + return nil, err + } + resp, err := send(s, token, conf.CA, conf.ValidateTLSCertificate) + if err != nil { + return nil, err + } + if resp.Status != "ok" { + return nil, fmt.Errorf("error: %s", resp.Response) + } + k, _, _, _, err := ssh.ParseAuthorizedKey([]byte(resp.Response)) + if err != nil { + return nil, err + } + cert, ok := k.(*ssh.Certificate) + if !ok { + return nil, fmt.Errorf("did not receive a certificate from server") + } + return cert, nil +} -- cgit v1.2.3