Mainflux.mainflux/certs/pki/vault.go

162 lines
3.2 KiB
Go

// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
// Package pki wraps vault client
package pki
import (
"encoding/json"
"io/ioutil"
"net/http"
"time"
"github.com/hashicorp/vault/api"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mitchellh/mapstructure"
)
const (
issue = "issue"
revoke = "revoke"
apiVer = "v1"
)
var (
errFailedVaultCertIssue = errors.New("failed to issue vault certificate")
errFailedCertDecoding = errors.New("failed to decode response from vault service")
)
type pkiAgent struct {
token string
path string
role string
host string
issueURL string
revokeURL string
client *api.Client
}
type certReq struct {
CommonName string `json:"common_name"`
TTL string `json:"ttl"`
KeyBits int `json:"key_bits"`
KeyType string `json:"key_type"`
}
type certRevokeReq struct {
SerialNumber string `json:"serial_number"`
}
func NewVaultClient(token, host, path, role string) (Agent, error) {
conf := &api.Config{
Address: host,
}
client, err := api.NewClient(conf)
if err != nil {
return nil, err
}
client.SetToken(token)
p := pkiAgent{
token: token,
host: host,
role: role,
path: path,
client: client,
issueURL: "/" + apiVer + "/" + path + "/" + issue + "/" + role,
revokeURL: "/" + apiVer + "/" + path + "/" + revoke,
}
return &p, nil
}
func (p *pkiAgent) IssueCert(cn string, ttl, keyType string, keyBits int) (Cert, error) {
cReq := certReq{
CommonName: cn,
TTL: ttl,
KeyBits: keyBits,
KeyType: keyType,
}
r := p.client.NewRequest("POST", p.issueURL)
if err := r.SetJSONBody(cReq); err != nil {
return Cert{}, err
}
resp, err := p.client.RawRequest(r)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return Cert{}, err
}
if resp.StatusCode >= http.StatusBadRequest {
_, err := ioutil.ReadAll(resp.Body)
if err != nil {
return Cert{}, err
}
return Cert{}, errors.Wrap(errFailedVaultCertIssue, err)
}
s, _ := api.ParseSecret(resp.Body)
cert := Cert{}
if err = mapstructure.Decode(s.Data, &cert); err != nil {
return Cert{}, errors.Wrap(errFailedCertDecoding, err)
}
// Expire time calc must be revised value doesnt look correct
exp, err := s.Data["expiration"].(json.Number).Float64()
if err != nil {
return cert, err
}
expTime := time.Unix(0, int64(exp)*int64(time.Millisecond))
cert.Expire = expTime
return cert, nil
}
func (p *pkiAgent) Revoke(serial string) (Revoke, error) {
cReq := certRevokeReq{
SerialNumber: serial,
}
r := p.client.NewRequest("POST", p.revokeURL)
if err := r.SetJSONBody(cReq); err != nil {
return Revoke{}, err
}
resp, err := p.client.RawRequest(r)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return Revoke{}, err
}
if resp.StatusCode >= http.StatusBadRequest {
_, err := ioutil.ReadAll(resp.Body)
if err != nil {
return Revoke{}, err
}
return Revoke{}, errors.Wrap(errFailedVaultCertIssue, err)
}
s, err := api.ParseSecret(resp.Body)
if err != nil {
return Revoke{}, err
}
rev, err := s.Data["revocation_time"].(json.Number).Float64()
if err != nil {
return Revoke{}, err
}
revTime := time.Unix(0, int64(rev)*int64(time.Millisecond))
return Revoke{
RevocationTime: revTime,
}, nil
}