add postgresql
[arvados.git] / services / boot / vault.go
1 package main
2
3 import (
4         "context"
5         "fmt"
6         "io/ioutil"
7         "log"
8         "path"
9         "sync"
10         "time"
11
12         "github.com/hashicorp/vault/api"
13 )
14
15 var (
16         vault    = &vaultBooter{}
17         vaultCfg = api.DefaultConfig()
18 )
19
20 type vaultBooter struct {
21         sync.Mutex
22 }
23
24 func (vb *vaultBooter) Boot(ctx context.Context) error {
25         vb.Lock()
26         defer vb.Unlock()
27
28         if vb.check(ctx) == nil {
29                 return nil
30         }
31         cfg := cfg(ctx)
32         bin := cfg.UsrDir + "/bin/vault"
33         err := (&download{
34                 URL:  "https://releases.hashicorp.com/vault/0.6.4/vault_0.6.4_linux_amd64.zip",
35                 Dest: bin,
36                 Size: 52518022,
37                 Mode: 0755,
38         }).Boot(ctx)
39         if err != nil {
40                 return err
41         }
42
43         cfgPath := path.Join(cfg.DataDir, "vault.hcl")
44         err = atomicWriteFile(cfgPath, []byte(fmt.Sprintf(`backend "consul" {
45                 address = "127.0.0.1:%d"
46                 path = "vault"
47         }
48         listener "tcp" {
49                 address = "127.0.0.1:%d"
50                 tls_disable = 1
51         }`, cfg.Ports.ConsulHTTP, cfg.Ports.VaultServer)), 0644)
52         if err != nil {
53                 return err
54         }
55
56         args := []string{"server", "-config=" + cfgPath}
57         supervisor := newSupervisor(ctx, "arvados-vault", bin, args...)
58         running, err := supervisor.Running(ctx)
59         if err != nil {
60                 return err
61         }
62         if !running {
63                 defer feedbackf(ctx, "starting vault service")()
64                 err = supervisor.Start(ctx)
65                 if err != nil {
66                         return fmt.Errorf("starting vault: %s", err)
67                 }
68         }
69
70         if err := vb.tryInit(ctx); err != nil {
71                 return err
72         }
73         return waitCheck(ctx, 30*time.Second, vb.check)
74 }
75
76 func (vb *vaultBooter) tryInit(ctx context.Context) error {
77         cfg := cfg(ctx)
78
79         var vault *api.Client
80         var init bool
81         if err := waitCheck(ctx, time.Minute, func(context.Context) error {
82                 var err error
83                 vault, err = vb.client(ctx)
84                 if err != nil {
85                         return err
86                 }
87                 init, err = vault.Sys().InitStatus()
88                 return err
89         }); err != nil {
90                 return err
91         } else if init {
92                 return nil
93         }
94
95         resp, err := vault.Sys().Init(&api.InitRequest{
96                 SecretShares:    5,
97                 SecretThreshold: 3,
98         })
99         if err != nil {
100                 return fmt.Errorf("vault-init: %s", err)
101         }
102         atomicWriteJSON(path.Join(cfg.DataDir, "vault-keys.json"), resp, 0400)
103         atomicWriteFile(path.Join(cfg.DataDir, "vault-root-token.txt"), []byte(resp.RootToken), 0400)
104
105         for _, key := range resp.Keys {
106                 resp, err := vault.Sys().Unseal(key)
107                 if err != nil {
108                         log.Printf("error: unseal: %s", err)
109                         continue
110                 }
111                 if !resp.Sealed {
112                         log.Printf("unseal successful")
113                         return nil
114                 }
115         }
116         return fmt.Errorf("vault unseal failed!")
117 }
118
119 func (vb *vaultBooter) client(ctx context.Context) (*api.Client, error) {
120         cfg := cfg(ctx)
121         vaultCfg.Address = fmt.Sprintf("http://0.0.0.0:%d", cfg.Ports.VaultServer)
122         return api.NewClient(vaultCfg)
123 }
124
125 func (vb *vaultBooter) check(ctx context.Context) error {
126         cfg := cfg(ctx)
127         vault, err := vb.client(ctx)
128         if err != nil {
129                 return err
130         }
131         token, err := ioutil.ReadFile(path.Join(cfg.DataDir, "vault-root-token.txt"))
132         if err != nil {
133                 return err
134         }
135         vault.SetToken(string(token))
136         if init, err := vault.Sys().InitStatus(); err != nil {
137                 return err
138         } else if !init {
139                 return fmt.Errorf("vault is not initialized")
140         }
141         return nil
142 }