162 lines
3.2 KiB
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
|
|
|
|
}
|