mirror of https://github.com/linkerd/linkerd2.git
155 lines
3.8 KiB
Go
155 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
|
|
"k8s.io/apimachinery/pkg/util/validation"
|
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
|
|
"github.com/linkerd/linkerd2/pkg/tls"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
envDir = "LINKERD2_PROXY_IDENTITY_DIR"
|
|
envLocalName = "LINKERD2_PROXY_IDENTITY_LOCAL_NAME"
|
|
envTrustAnchors = "LINKERD2_PROXY_IDENTITY_TRUST_ANCHORS"
|
|
)
|
|
|
|
func main() {
|
|
dir := os.Getenv(envDir)
|
|
keyPath, csrPath, err := checkEndEntityDir(dir)
|
|
if err != nil {
|
|
log.Fatalf("Invalid end-entity directory: %s", err)
|
|
}
|
|
|
|
if _, err := loadVerifier(os.Getenv(envTrustAnchors)); err != nil {
|
|
log.Fatalf("Failed to load trust anchors: %s", err)
|
|
}
|
|
|
|
key, err := generateAndStoreKey(keyPath)
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
|
|
name := os.Getenv(envLocalName)
|
|
if _, err := generateAndStoreCSR(csrPath, name, key); err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
|
|
runProxy()
|
|
}
|
|
|
|
func loadVerifier(pem string) (verify x509.VerifyOptions, err error) {
|
|
if pem == "" {
|
|
err = fmt.Errorf("'%s' must be set", envTrustAnchors)
|
|
return
|
|
}
|
|
|
|
verify.Roots, err = tls.DecodePEMCertPool(pem)
|
|
return
|
|
}
|
|
|
|
// checkEndEntityDir checks that the provided directory path exists and is
|
|
// suitable to write key material to, returning the key and CSR paths.
|
|
//
|
|
// If the directory does not exist, we assume that the directory was specified
|
|
// incorrectly and return an error. In practice this directory should be tmpfs
|
|
// so that credentials are not written to disk, so we do not want to create new
|
|
// directories here.
|
|
//
|
|
// If the key and/or CSR paths refer to existing files, it will be logged and
|
|
// the credentials will be recreated.
|
|
func checkEndEntityDir(dir string) (string, string, error) {
|
|
if dir == "" {
|
|
return "", "", errors.New("no end entity directory specified")
|
|
}
|
|
|
|
s, err := os.Stat(dir)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
if !s.IsDir() {
|
|
return "", "", fmt.Errorf("not a directory: %s", dir)
|
|
}
|
|
|
|
keyPath := filepath.Join(dir, "key.p8")
|
|
if err = checkNotExists(keyPath); err != nil {
|
|
log.Infof("Found pre-existing key: %s", keyPath)
|
|
}
|
|
|
|
csrPath := filepath.Join(dir, "csr.der")
|
|
if err = checkNotExists(csrPath); err != nil {
|
|
log.Infof("Found pre-existing CSR: %s", csrPath)
|
|
}
|
|
|
|
return keyPath, csrPath, nil
|
|
}
|
|
|
|
func checkNotExists(p string) (err error) {
|
|
_, err = os.Stat(p)
|
|
if err == nil {
|
|
err = fmt.Errorf("already exists: %s", p)
|
|
} else if os.IsNotExist(err) {
|
|
err = nil
|
|
}
|
|
return
|
|
}
|
|
|
|
func generateAndStoreKey(p string) (key *ecdsa.PrivateKey, err error) {
|
|
// Generate a private key and store it read-only. This is written to the
|
|
// file-system so that the proxy may read this key at startup. The
|
|
// destination path should generally be tmpfs so that the key material is
|
|
// not written to disk.
|
|
key, err = tls.GenerateKey()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
pemb := tls.EncodePrivateKeyP8(key)
|
|
err = os.WriteFile(p, pemb, 0600)
|
|
return
|
|
}
|
|
|
|
func generateAndStoreCSR(p, id string, key *ecdsa.PrivateKey) ([]byte, error) {
|
|
if id == "" {
|
|
return nil, errors.New("a non-empty identity is required")
|
|
}
|
|
|
|
if err := validation.IsFullyQualifiedDomainName(field.NewPath(""), id).ToAggregate(); err != nil {
|
|
return nil, fmt.Errorf("%s a fully qualified DNS name is required", id)
|
|
}
|
|
|
|
csr := x509.CertificateRequest{
|
|
Subject: pkix.Name{CommonName: id},
|
|
DNSNames: []string{id},
|
|
}
|
|
csrb, err := x509.CreateCertificateRequest(rand.Reader, &csr, key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create CSR: %w", err)
|
|
}
|
|
|
|
if err = os.WriteFile(p, csrb, 0600); err != nil {
|
|
return nil, fmt.Errorf("failed to write CSR: %w", err)
|
|
}
|
|
|
|
return csrb, nil
|
|
}
|
|
|
|
func runProxy() {
|
|
// The input arguments are static.
|
|
//nolint:gosec
|
|
err := syscall.Exec("/usr/lib/linkerd/linkerd2-proxy", []string{}, os.Environ())
|
|
if err != nil {
|
|
log.Fatalf("Failed to run proxy: %s", err)
|
|
}
|
|
}
|