mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 18:23:25 +00:00
617afc7e6f
List of fixed items: ``` integration/helpers.go:1279:2 gosimple S1000: should use for range instead of for { select {} } integration/integration_test.go:144:5 gosimple S1009: should omit nil check; len() for nil slices is defined as zero integration/integration_test.go:173:5 gosimple S1009: should omit nil check; len() for nil slices is defined as zero integration/integration_test.go:296:28 gosimple S1019: should use make(chan error) instead integration/integration_test.go:570:41 gosimple S1019: should use make(chan interface{}) instead integration/integration_test.go:685:40 gosimple S1019: should use make(chan interface{}) instead integration/integration_test.go:759:33 gosimple S1019: should use make(chan string) instead lib/auth/init_test.go:62:2 gosimple S1021: should merge variable declaration with assignment on next line lib/auth/tls_test.go:1658:22 gosimple S1024: should use time.Until instead of t.Sub(time.Now()) lib/backend/dynamo/dynamodbbk.go:420:5 gosimple S1004: should use !bytes.Equal(expected.Key, replaceWith.Key) instead lib/backend/dynamo/dynamodbbk.go:656:12 gosimple S1039: unnecessary use of fmt.Sprintf lib/backend/etcdbk/etcd.go:458:5 gosimple S1004: should use !bytes.Equal(expected.Key, replaceWith.Key) instead lib/backend/firestore/firestorebk.go:407:5 gosimple S1004: should use !bytes.Equal(expected.Key, replaceWith.Key) instead lib/backend/lite/lite.go:317:5 gosimple S1004: should use !bytes.Equal(expected.Key, replaceWith.Key) instead lib/backend/lite/lite.go:336:6 gosimple S1004: should use !bytes.Equal(value, expected.Value) instead lib/backend/memory/memory.go:365:5 gosimple S1004: should use !bytes.Equal(expected.Key, replaceWith.Key) instead lib/backend/memory/memory.go:376:5 gosimple S1004: should use !bytes.Equal(existingItem.Value, expected.Value) instead lib/backend/test/suite.go:327:10 gosimple S1024: should use time.Until instead of t.Sub(time.Now()) lib/client/api.go:1410:9 gosimple S1003: should use strings.ContainsRune(name, ':') instead lib/client/api.go:2355:32 gosimple S1019: should use make([]ForwardedPort, len(spec)) instead lib/client/keyagent_test.go:85:2 gosimple S1021: should merge variable declaration with assignment on next line lib/client/player.go:54:33 gosimple S1019: should use make(chan int) instead lib/config/configuration.go:1024:52 gosimple S1019: should use make(services.CommandLabels) instead lib/config/configuration.go:1025:44 gosimple S1019: should use make(map[string]string) instead lib/config/configuration.go:930:21 gosimple S1003: should use strings.Contains(clf.Roles, defaults.RoleNode) instead lib/config/configuration.go:931:22 gosimple S1003: should use strings.Contains(clf.Roles, defaults.RoleAuthService) instead lib/config/configuration.go:932:23 gosimple S1003: should use strings.Contains(clf.Roles, defaults.RoleProxy) instead lib/service/supervisor.go:387:2 gosimple S1001: should use copy() instead of a loop lib/tlsca/parsegen.go:140:9 gosimple S1034: assigning the result of this type assertion to a variable (switch generalKey := generalKey.(type)) could eliminate type assertions in switch cases lib/utils/certs.go:140:9 gosimple S1034: assigning the result of this type assertion to a variable (switch generalKey := generalKey.(type)) could eliminate type assertions in switch cases lib/utils/certs.go:167:40 gosimple S1010: should omit second index in slice, s[a:len(s)] is identical to s[a:] lib/utils/certs.go:204:5 gosimple S1004: should use !bytes.Equal(certificateChain[0].SubjectKeyId, certificateChain[0].AuthorityKeyId) instead lib/utils/parse/parse.go:116:45 gosimple S1003: should use strings.Contains(variable, "}}") instead lib/utils/parse/parse.go:116:6 gosimple S1003: should use strings.Contains(variable, "{{") instead lib/utils/socks/socks.go:192:10 gosimple S1025: should use String() instead of fmt.Sprintf lib/utils/socks/socks.go:199:10 gosimple S1025: should use String() instead of fmt.Sprintf lib/web/apiserver.go:1054:18 gosimple S1024: should use time.Until instead of t.Sub(time.Now()) lib/web/apiserver.go:1954:9 gosimple S1039: unnecessary use of fmt.Sprintf tool/tsh/tsh.go:1193:14 gosimple S1024: should use time.Until instead of t.Sub(time.Now()) ```
246 lines
7.4 KiB
Go
246 lines
7.4 KiB
Go
/*
|
|
Copyright 2016 SPIFFE Authors
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/pem"
|
|
"math/big"
|
|
"time"
|
|
|
|
"github.com/gravitational/teleport"
|
|
|
|
"github.com/gravitational/trace"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ParseSigningKeyStore parses signing key store from PEM encoded key pair
|
|
func ParseSigningKeyStorePEM(keyPEM, certPEM string) (*SigningKeyStore, error) {
|
|
_, err := ParseCertificatePEM([]byte(certPEM))
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
key, err := ParsePrivateKeyPEM([]byte(keyPEM))
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
rsaKey, ok := key.(*rsa.PrivateKey)
|
|
if !ok {
|
|
return nil, trace.BadParameter("key of type %T is not supported, only RSA keys are supported for signatures", key)
|
|
}
|
|
certASN, _ := pem.Decode([]byte(certPEM))
|
|
if certASN == nil {
|
|
return nil, trace.BadParameter("expected PEM-encoded block")
|
|
}
|
|
return &SigningKeyStore{privateKey: rsaKey, cert: certASN.Bytes}, nil
|
|
}
|
|
|
|
// SigningKeyStore is used to sign using X509 digital signatures
|
|
type SigningKeyStore struct {
|
|
privateKey *rsa.PrivateKey
|
|
cert []byte
|
|
}
|
|
|
|
func (ks *SigningKeyStore) GetKeyPair() (*rsa.PrivateKey, []byte, error) {
|
|
return ks.privateKey, ks.cert, nil
|
|
}
|
|
|
|
// GenerateSelfSignedSigningCert generates self-signed certificate used for digital signatures
|
|
func GenerateSelfSignedSigningCert(entity pkix.Name, dnsNames []string, ttl time.Duration) ([]byte, []byte, error) {
|
|
priv, err := rsa.GenerateKey(rand.Reader, teleport.RSAKeySize)
|
|
if err != nil {
|
|
return nil, nil, trace.Wrap(err)
|
|
}
|
|
// to account for clock skew
|
|
notBefore := time.Now().Add(-2 * time.Minute)
|
|
notAfter := notBefore.Add(ttl)
|
|
|
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
|
if err != nil {
|
|
return nil, nil, trace.Wrap(err)
|
|
}
|
|
|
|
template := x509.Certificate{
|
|
SerialNumber: serialNumber,
|
|
Issuer: entity,
|
|
Subject: entity,
|
|
NotBefore: notBefore,
|
|
NotAfter: notAfter,
|
|
KeyUsage: x509.KeyUsageDigitalSignature,
|
|
BasicConstraintsValid: true,
|
|
DNSNames: dnsNames,
|
|
}
|
|
|
|
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
|
if err != nil {
|
|
return nil, nil, trace.Wrap(err)
|
|
}
|
|
|
|
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
|
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
|
|
|
return keyPEM, certPEM, nil
|
|
}
|
|
|
|
// ParseCertificatePEM parses PEM-encoded certificate
|
|
func ParseCertificatePEM(bytes []byte) (*x509.Certificate, error) {
|
|
block, _ := pem.Decode(bytes)
|
|
if block == nil {
|
|
return nil, trace.BadParameter("expected PEM-encoded block")
|
|
}
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
if err != nil {
|
|
return nil, trace.BadParameter(err.Error())
|
|
}
|
|
return cert, nil
|
|
}
|
|
|
|
// ParsePrivateKeyPEM parses PEM-encoded private key
|
|
func ParsePrivateKeyPEM(bytes []byte) (crypto.Signer, error) {
|
|
block, _ := pem.Decode(bytes)
|
|
if block == nil {
|
|
return nil, trace.BadParameter("expected PEM-encoded block")
|
|
}
|
|
return ParsePrivateKeyDER(block.Bytes)
|
|
}
|
|
|
|
// ParsePrivateKeyDER parses unencrypted DER-encoded private key
|
|
func ParsePrivateKeyDER(der []byte) (crypto.Signer, error) {
|
|
generalKey, err := x509.ParsePKCS8PrivateKey(der)
|
|
if err != nil {
|
|
generalKey, err = x509.ParsePKCS1PrivateKey(der)
|
|
if err != nil {
|
|
generalKey, err = x509.ParseECPrivateKey(der)
|
|
if err != nil {
|
|
logrus.Errorf("Failed to parse key: %v.", err)
|
|
return nil, trace.BadParameter("failed parsing private key")
|
|
}
|
|
}
|
|
}
|
|
|
|
switch k := generalKey.(type) {
|
|
case *rsa.PrivateKey:
|
|
return k, nil
|
|
case *ecdsa.PrivateKey:
|
|
return k, nil
|
|
}
|
|
|
|
return nil, trace.BadParameter("unsupported private key type")
|
|
}
|
|
|
|
// VerifyCertificateChain reads in chain of certificates and makes sure the
|
|
// chain from leaf to root is valid. This ensures that clients (web browsers
|
|
// and CLI) won't have problem validating the chain.
|
|
func VerifyCertificateChain(certificateChain []*x509.Certificate) error {
|
|
// chain needs at least one certificate
|
|
if len(certificateChain) == 0 {
|
|
return trace.BadParameter("need at least one certificate in chain")
|
|
}
|
|
|
|
// extract leaf of certificate chain. it is safe to index into the chain here
|
|
// because readCertificateChain always returns a valid chain with at least
|
|
// one certificate.
|
|
leaf := certificateChain[0]
|
|
|
|
// extract intermediate certificate chain.
|
|
intermediates := x509.NewCertPool()
|
|
if len(certificateChain) > 1 {
|
|
for _, v := range certificateChain[1:] {
|
|
intermediates.AddCert(v)
|
|
}
|
|
}
|
|
|
|
// verify certificate chain, roots is nil which will cause us to to use the
|
|
// system roots.
|
|
opts := x509.VerifyOptions{
|
|
Intermediates: intermediates,
|
|
}
|
|
_, err := leaf.Verify(opts)
|
|
if err != nil {
|
|
return trace.Wrap(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsSelfSigned checks if the certificate is a self-signed certificate. To
|
|
// check if a certificate is self signed, we make sure that only one
|
|
// certificate is in the chain and that the SubjectKeyId and AuthorityKeyId
|
|
// match.
|
|
//
|
|
// From RFC5280: https://tools.ietf.org/html/rfc5280#section-4.2.1.1
|
|
//
|
|
// The signature on a self-signed certificate is generated with the private
|
|
// key associated with the certificate's subject public key. (This
|
|
// proves that the issuer possesses both the public and private keys.)
|
|
// In this case, the subject and authority key identifiers would be
|
|
// identical, but only the subject key identifier is needed for
|
|
// certification path building.
|
|
//
|
|
func IsSelfSigned(certificateChain []*x509.Certificate) bool {
|
|
if len(certificateChain) != 1 {
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(certificateChain[0].SubjectKeyId, certificateChain[0].AuthorityKeyId)
|
|
}
|
|
|
|
// ReadCertificateChain parses PEM encoded bytes that can contain one or
|
|
// multiple certificates and returns a slice of x509.Certificate.
|
|
func ReadCertificateChain(certificateChainBytes []byte) ([]*x509.Certificate, error) {
|
|
// build the certificate chain next
|
|
var certificateBlock *pem.Block
|
|
var remainingBytes []byte = bytes.TrimSpace(certificateChainBytes)
|
|
var certificateChain [][]byte
|
|
|
|
for {
|
|
certificateBlock, remainingBytes = pem.Decode(remainingBytes)
|
|
if certificateBlock == nil || certificateBlock.Type != pemBlockCertificate {
|
|
return nil, trace.NotFound("no PEM data found")
|
|
}
|
|
certificateChain = append(certificateChain, certificateBlock.Bytes)
|
|
|
|
if len(remainingBytes) == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
// build a concatenated certificate chain
|
|
var buf bytes.Buffer
|
|
for _, cc := range certificateChain {
|
|
_, err := buf.Write(cc)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
}
|
|
|
|
// parse the chain and get a slice of x509.Certificates.
|
|
x509Chain, err := x509.ParseCertificates(buf.Bytes())
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
return x509Chain, nil
|
|
}
|
|
|
|
const pemBlockCertificate = "CERTIFICATE"
|