aboutsummaryrefslogtreecommitdiff
path: root/client/client.go
diff options
context:
space:
mode:
authorNiall Sheridan <nsheridan@gmail.com>2016-09-10 17:40:23 +0100
committerNiall Sheridan <nsheridan@gmail.com>2016-09-10 17:40:23 +0100
commita30d6403f723765b8f9b7609e7eb3ade0f5434a0 (patch)
treedfd1197c292fa6fa3bcceae5e135bc8e3991f4c0 /client/client.go
parentd422741d8aab8cd4564141da57e2b98eed3c8a85 (diff)
Make client a top-level package for consistency
Diffstat (limited to 'client/client.go')
-rw-r--r--client/client.go113
1 files changed, 113 insertions, 0 deletions
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
+}