Andrew Rynhard ee226dddac
chore: enforce commit and license policies (#304)
Signed-off-by: Andrew Rynhard <andrew@andrewrynhard.com>
2019-01-13 16:10:49 -08:00

124 lines
3.1 KiB
Go

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package gen
import (
"context"
stdlibx509 "crypto/x509"
"encoding/pem"
"fmt"
"log"
stdlibnet "net"
"time"
"github.com/autonomy/talos/internal/app/trustd/proto"
"github.com/autonomy/talos/internal/pkg/crypto/x509"
"github.com/autonomy/talos/internal/pkg/grpc/middleware/auth/basic"
"github.com/autonomy/talos/internal/pkg/net"
"github.com/autonomy/talos/internal/pkg/userdata"
"google.golang.org/grpc"
)
// Generator represents the OS identity generator.
type Generator struct {
client proto.TrustdClient
}
// NewGenerator initializes a Generator with a preconfigured grpc.ClientConn.
func NewGenerator(data *userdata.UserData, port int) (g *Generator, err error) {
if len(data.Services.Trustd.Endpoints) == 0 {
return nil, fmt.Errorf("at least one root of trust endpoint is required")
}
creds := basic.NewCredentials(
data.Security.OS.CA.Crt,
data.Services.Trustd.Username,
data.Services.Trustd.Password,
)
// TODO: In the case of failure, attempt to generate the identity from
// another RoT.
var conn *grpc.ClientConn
conn, err = basic.NewConnection(data.Services.Trustd.Endpoints[0], port, creds)
if err != nil {
return nil, err
}
client := proto.NewTrustdClient(conn)
return &Generator{
client: client,
}, nil
}
// Certificate implements the proto.TrustdClient interface.
func (g *Generator) Certificate(in *proto.CertificateRequest) (resp *proto.CertificateResponse, err error) {
ctx := context.Background()
resp, err = g.client.Certificate(ctx, in)
if err != nil {
return
}
return resp, err
}
// Identity creates a CSR and sends it to a Root of Trust for signing.
// The Root of Trust responds with a signed certificate.
func (g *Generator) Identity(data *userdata.Security) (err error) {
key, err := x509.NewKey()
if err != nil {
return
}
data.OS.Identity = &x509.PEMEncodedCertificateAndKey{}
data.OS.Identity.Key = key.KeyPEM
pemBlock, _ := pem.Decode(key.KeyPEM)
if pemBlock == nil {
return fmt.Errorf("failed to decode key")
}
keyEC, err := stdlibx509.ParseECPrivateKey(pemBlock.Bytes)
if err != nil {
return
}
addr, err := net.IP()
if err != nil {
return
}
opts := []x509.Option{}
ips := []stdlibnet.IP{addr}
opts = append(opts, x509.IPAddresses(ips))
opts = append(opts, x509.NotAfter(time.Now().Add(time.Duration(8760)*time.Hour)))
csr, err := x509.NewCertificateSigningRequest(keyEC, opts...)
if err != nil {
return
}
req := &proto.CertificateRequest{
Csr: csr.X509CertificateRequestPEM,
}
return poll(g, req, data.OS.Identity)
}
func poll(g *Generator, in *proto.CertificateRequest, data *x509.PEMEncodedCertificateAndKey) (err error) {
timeout := time.NewTimer(time.Minute * 5).C
tick := time.NewTicker(time.Second * 5).C
for {
select {
case <-timeout:
return fmt.Errorf("timeout waiting for certificate")
case <-tick:
crt, _err := g.Certificate(in)
if _err != nil {
log.Println(_err)
continue
}
data.Crt = crt.Bytes
return nil
}
}
}