mirror of
https://github.com/slackhq/nebula.git
synced 2024-11-10 17:24:37 +08:00
e0185c4b01
* Support NIST curve P256 This change adds support for NIST curve P256. When you use `nebula-cert ca` or `nebula-cert keygen`, you can specify `-curve P256` to enable it. The curve to use is based on the curve defined in your CA certificate. Internally, we use ECDSA P256 to sign certificates, and ECDH P256 to do Noise handshakes. P256 is not supported natively in Noise Protocol, so we define `DHP256` in the `noiseutil` package to implement support for it. You cannot have a mixed network of Curve25519 and P256 certificates, since the Noise protocol will only attempt to parse using the Curve defined in the host's certificate. * verify the curves match in VerifyPrivateKey This would have failed anyways once we tried to actually use the bytes in the private key, but its better to detect the issue up front with a better error message. * add cert.Curve argument to Sign method * fix mismerge * use crypto/ecdh This is the preferred method for doing ECDH functions now, and also has a boringcrypto specific codepath. * remove other ecdh uses of crypto/elliptic use crypto/ecdh instead
163 lines
4.4 KiB
Go
163 lines
4.4 KiB
Go
package nebula
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/slackhq/nebula/cert"
|
|
"github.com/slackhq/nebula/config"
|
|
)
|
|
|
|
type CertState struct {
|
|
certificate *cert.NebulaCertificate
|
|
rawCertificate []byte
|
|
rawCertificateNoKey []byte
|
|
publicKey []byte
|
|
privateKey []byte
|
|
}
|
|
|
|
func NewCertState(certificate *cert.NebulaCertificate, privateKey []byte) (*CertState, error) {
|
|
// Marshal the certificate to ensure it is valid
|
|
rawCertificate, err := certificate.Marshal()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid nebula certificate on interface: %s", err)
|
|
}
|
|
|
|
publicKey := certificate.Details.PublicKey
|
|
cs := &CertState{
|
|
rawCertificate: rawCertificate,
|
|
certificate: certificate, // PublicKey has been set to nil above
|
|
privateKey: privateKey,
|
|
publicKey: publicKey,
|
|
}
|
|
|
|
cs.certificate.Details.PublicKey = nil
|
|
rawCertNoKey, err := cs.certificate.Marshal()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error marshalling certificate no key: %s", err)
|
|
}
|
|
cs.rawCertificateNoKey = rawCertNoKey
|
|
// put public key back
|
|
cs.certificate.Details.PublicKey = cs.publicKey
|
|
return cs, nil
|
|
}
|
|
|
|
func NewCertStateFromConfig(c *config.C) (*CertState, error) {
|
|
var pemPrivateKey []byte
|
|
var err error
|
|
|
|
privPathOrPEM := c.GetString("pki.key", "")
|
|
|
|
if privPathOrPEM == "" {
|
|
return nil, errors.New("no pki.key path or PEM data provided")
|
|
}
|
|
|
|
if strings.Contains(privPathOrPEM, "-----BEGIN") {
|
|
pemPrivateKey = []byte(privPathOrPEM)
|
|
privPathOrPEM = "<inline>"
|
|
} else {
|
|
pemPrivateKey, err = ioutil.ReadFile(privPathOrPEM)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to read pki.key file %s: %s", privPathOrPEM, err)
|
|
}
|
|
}
|
|
|
|
rawKey, _, curve, err := cert.UnmarshalPrivateKey(pemPrivateKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error while unmarshaling pki.key %s: %s", privPathOrPEM, err)
|
|
}
|
|
|
|
var rawCert []byte
|
|
|
|
pubPathOrPEM := c.GetString("pki.cert", "")
|
|
|
|
if pubPathOrPEM == "" {
|
|
return nil, errors.New("no pki.cert path or PEM data provided")
|
|
}
|
|
|
|
if strings.Contains(pubPathOrPEM, "-----BEGIN") {
|
|
rawCert = []byte(pubPathOrPEM)
|
|
pubPathOrPEM = "<inline>"
|
|
} else {
|
|
rawCert, err = ioutil.ReadFile(pubPathOrPEM)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to read pki.cert file %s: %s", pubPathOrPEM, err)
|
|
}
|
|
}
|
|
|
|
nebulaCert, _, err := cert.UnmarshalNebulaCertificateFromPEM(rawCert)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error while unmarshaling pki.cert %s: %s", pubPathOrPEM, err)
|
|
}
|
|
|
|
if nebulaCert.Expired(time.Now()) {
|
|
return nil, fmt.Errorf("nebula certificate for this host is expired")
|
|
}
|
|
|
|
if len(nebulaCert.Details.Ips) == 0 {
|
|
return nil, fmt.Errorf("no IPs encoded in certificate")
|
|
}
|
|
|
|
if err = nebulaCert.VerifyPrivateKey(curve, rawKey); err != nil {
|
|
return nil, fmt.Errorf("private key is not a pair with public key in nebula cert")
|
|
}
|
|
|
|
return NewCertState(nebulaCert, rawKey)
|
|
}
|
|
|
|
func loadCAFromConfig(l *logrus.Logger, c *config.C) (*cert.NebulaCAPool, error) {
|
|
var rawCA []byte
|
|
var err error
|
|
|
|
caPathOrPEM := c.GetString("pki.ca", "")
|
|
if caPathOrPEM == "" {
|
|
return nil, errors.New("no pki.ca path or PEM data provided")
|
|
}
|
|
|
|
if strings.Contains(caPathOrPEM, "-----BEGIN") {
|
|
rawCA = []byte(caPathOrPEM)
|
|
|
|
} else {
|
|
rawCA, err = ioutil.ReadFile(caPathOrPEM)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to read pki.ca file %s: %s", caPathOrPEM, err)
|
|
}
|
|
}
|
|
|
|
CAs, err := cert.NewCAPoolFromBytes(rawCA)
|
|
if errors.Is(err, cert.ErrExpired) {
|
|
var expired int
|
|
for _, cert := range CAs.CAs {
|
|
if cert.Expired(time.Now()) {
|
|
expired++
|
|
l.WithField("cert", cert).Warn("expired certificate present in CA pool")
|
|
}
|
|
}
|
|
|
|
if expired >= len(CAs.CAs) {
|
|
return nil, errors.New("no valid CA certificates present")
|
|
}
|
|
|
|
} else if err != nil {
|
|
return nil, fmt.Errorf("error while adding CA certificate to CA trust store: %s", err)
|
|
}
|
|
|
|
for _, fp := range c.GetStringSlice("pki.blocklist", []string{}) {
|
|
l.WithField("fingerprint", fp).Info("Blocklisting cert")
|
|
CAs.BlocklistFingerprint(fp)
|
|
}
|
|
|
|
// Support deprecated config for at least one minor release to allow for migrations
|
|
//TODO: remove in 2022 or later
|
|
for _, fp := range c.GetStringSlice("pki.blacklist", []string{}) {
|
|
l.WithField("fingerprint", fp).Info("Blocklisting cert")
|
|
l.Warn("pki.blacklist is deprecated and will not be supported in a future release. Please migrate your config to use pki.blocklist")
|
|
CAs.BlocklistFingerprint(fp)
|
|
}
|
|
|
|
return CAs, nil
|
|
}
|