forked from TrueCloudLab/certificates
Add support for identity authentication.
This commit is contained in:
parent
d940ab7c20
commit
d555f310dc
2 changed files with 129 additions and 2 deletions
62
ca/client.go
62
ca/client.go
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/cli/config"
|
"github.com/smallstep/cli/config"
|
||||||
"github.com/smallstep/cli/crypto/x509util"
|
"github.com/smallstep/cli/crypto/x509util"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
"gopkg.in/square/go-jose.v2/jwt"
|
"gopkg.in/square/go-jose.v2/jwt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,9 +39,13 @@ type clientOptions struct {
|
||||||
rootSHA256 string
|
rootSHA256 string
|
||||||
rootFilename string
|
rootFilename string
|
||||||
rootBundle []byte
|
rootBundle []byte
|
||||||
|
certificate tls.Certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *clientOptions) apply(opts []ClientOption) (err error) {
|
func (o *clientOptions) apply(opts []ClientOption) (err error) {
|
||||||
|
if err = o.applyDefaultIdentity(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
for _, fn := range opts {
|
for _, fn := range opts {
|
||||||
if err = fn(o); err != nil {
|
if err = fn(o); err != nil {
|
||||||
return
|
return
|
||||||
|
@ -49,6 +54,32 @@ func (o *clientOptions) apply(opts []ClientOption) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// applyDefaultIdentity sets the options for the default identity if the
|
||||||
|
// identity file is present. The identity is enabled by default.
|
||||||
|
func (o *clientOptions) applyDefaultIdentity() error {
|
||||||
|
b, err := ioutil.ReadFile(IdentityFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var identity Identity
|
||||||
|
if err := json.Unmarshal(b, &identity); err != nil {
|
||||||
|
return errors.Wrapf(err, "error unmarshaling %s", IdentityFile)
|
||||||
|
}
|
||||||
|
if err := identity.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
opts, err := identity.Options()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, fn := range opts {
|
||||||
|
if err := fn(o); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// checkTransport checks if other ways to set up a transport have been provided.
|
// checkTransport checks if other ways to set up a transport have been provided.
|
||||||
// If they have it returns an error.
|
// If they have it returns an error.
|
||||||
func (o *clientOptions) checkTransport() error {
|
func (o *clientOptions) checkTransport() error {
|
||||||
|
@ -85,10 +116,28 @@ func (o *clientOptions) getTransport(endpoint string) (tr http.RoundTripper, err
|
||||||
if tr, err = getTransportFromFile(rootFile); err != nil {
|
if tr, err = getTransportFromFile(rootFile); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return tr, nil
|
|
||||||
}
|
}
|
||||||
return nil, errors.New("a transport, a root cert, or a root sha256 must be used")
|
if tr == nil {
|
||||||
|
return nil, errors.New("a transport, a root cert, or a root sha256 must be used")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add client certificate if available
|
||||||
|
if o.certificate.Certificate != nil {
|
||||||
|
switch tr := tr.(type) {
|
||||||
|
case *http.Transport:
|
||||||
|
if len(tr.TLSClientConfig.Certificates) == 0 && tr.TLSClientConfig.GetClientCertificate == nil {
|
||||||
|
tr.TLSClientConfig.Certificates = []tls.Certificate{o.certificate}
|
||||||
|
}
|
||||||
|
case *http2.Transport:
|
||||||
|
if len(tr.TLSClientConfig.Certificates) == 0 && tr.TLSClientConfig.GetClientCertificate == nil {
|
||||||
|
tr.TLSClientConfig.Certificates = []tls.Certificate{o.certificate}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, errors.Errorf("unsupported transport type %T", tr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return tr, nil
|
return tr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,6 +190,15 @@ func WithCABundle(bundle []byte) ClientOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithCertificate will set the given certificate as the TLS client certificate
|
||||||
|
// in the client.
|
||||||
|
func WithCertificate(crt tls.Certificate) ClientOption {
|
||||||
|
return func(o *clientOptions) error {
|
||||||
|
o.certificate = crt
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getTransportFromFile(filename string) (http.RoundTripper, error) {
|
func getTransportFromFile(filename string) (http.RoundTripper, error) {
|
||||||
data, err := ioutil.ReadFile(filename)
|
data, err := ioutil.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
69
ca/identity.go
Normal file
69
ca/identity.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package ca
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/smallstep/cli/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IdentityType represents the different types of identity files.
|
||||||
|
type IdentityType string
|
||||||
|
|
||||||
|
// MutualTLS represents the identity using mTLS
|
||||||
|
const MutualTLS IdentityType = "mTLS"
|
||||||
|
|
||||||
|
// IdentityFile contains the location of the identity file.
|
||||||
|
var IdentityFile = filepath.Join(config.StepPath(), "config", "identity.json")
|
||||||
|
|
||||||
|
// Identity represents the identity file that can be used to authenticate with
|
||||||
|
// the CA.
|
||||||
|
type Identity struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Certificate string `json:"crt"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kind returns the type for the given identity.
|
||||||
|
func (i *Identity) Kind() IdentityType {
|
||||||
|
switch strings.ToLower(i.Type) {
|
||||||
|
case "mtls":
|
||||||
|
return MutualTLS
|
||||||
|
default:
|
||||||
|
return IdentityType(i.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the identity object.
|
||||||
|
func (i *Identity) Validate() error {
|
||||||
|
switch i.Kind() {
|
||||||
|
case MutualTLS:
|
||||||
|
if i.Certificate == "" {
|
||||||
|
return errors.New("identity.crt cannot be empty")
|
||||||
|
}
|
||||||
|
if i.Key == "" {
|
||||||
|
return errors.New("identity.key cannot be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case "":
|
||||||
|
return errors.New("identity.type cannot be empty")
|
||||||
|
default:
|
||||||
|
return errors.Errorf("unsupported identity type %s", i.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options returns the ClientOptions used for the given identity.
|
||||||
|
func (i *Identity) Options() ([]ClientOption, error) {
|
||||||
|
switch i.Kind() {
|
||||||
|
case MutualTLS:
|
||||||
|
crt, err := tls.LoadX509KeyPair(i.Certificate, i.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error creating identity certificate")
|
||||||
|
}
|
||||||
|
return []ClientOption{WithCertificate(crt)}, nil
|
||||||
|
default:
|
||||||
|
return nil, errors.Errorf("unsupported identity type %s", i.Type)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue