...

Source file src/github.com/linkerd/linkerd2/proxy-identity/main.go

Documentation: github.com/linkerd/linkerd2/proxy-identity

     1  package main
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"crypto/rand"
     6  	"crypto/x509"
     7  	"crypto/x509/pkix"
     8  	"errors"
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  	"syscall"
    13  
    14  	"k8s.io/apimachinery/pkg/util/validation"
    15  	"k8s.io/apimachinery/pkg/util/validation/field"
    16  
    17  	"github.com/linkerd/linkerd2/pkg/tls"
    18  	log "github.com/sirupsen/logrus"
    19  )
    20  
    21  const (
    22  	envDir          = "LINKERD2_PROXY_IDENTITY_DIR"
    23  	envLocalName    = "LINKERD2_PROXY_IDENTITY_LOCAL_NAME"
    24  	envTrustAnchors = "LINKERD2_PROXY_IDENTITY_TRUST_ANCHORS"
    25  )
    26  
    27  func main() {
    28  	dir := os.Getenv(envDir)
    29  	keyPath, csrPath, err := checkEndEntityDir(dir)
    30  	if err != nil {
    31  		log.Fatalf("Invalid end-entity directory: %s", err)
    32  	}
    33  
    34  	if _, err := loadVerifier(os.Getenv(envTrustAnchors)); err != nil {
    35  		log.Fatalf("Failed to load trust anchors: %s", err)
    36  	}
    37  
    38  	key, err := generateAndStoreKey(keyPath)
    39  	if err != nil {
    40  		log.Fatal(err.Error())
    41  	}
    42  
    43  	name := os.Getenv(envLocalName)
    44  	if _, err := generateAndStoreCSR(csrPath, name, key); err != nil {
    45  		log.Fatal(err.Error())
    46  	}
    47  
    48  	runProxy()
    49  }
    50  
    51  func loadVerifier(pem string) (verify x509.VerifyOptions, err error) {
    52  	if pem == "" {
    53  		err = fmt.Errorf("'%s' must be set", envTrustAnchors)
    54  		return
    55  	}
    56  
    57  	verify.Roots, err = tls.DecodePEMCertPool(pem)
    58  	return
    59  }
    60  
    61  // checkEndEntityDir checks that the provided directory path exists and is
    62  // suitable to write key material to, returning the key and CSR paths.
    63  //
    64  // If the directory does not exist, we assume that the directory was specified
    65  // incorrectly and return an error. In practice this directory should be tmpfs
    66  // so that credentials are not written to disk, so we do not want to create new
    67  // directories here.
    68  //
    69  // If the key and/or CSR paths refer to existing files, it will be logged and
    70  // the credentials will be recreated.
    71  func checkEndEntityDir(dir string) (string, string, error) {
    72  	if dir == "" {
    73  		return "", "", errors.New("no end entity directory specified")
    74  	}
    75  
    76  	s, err := os.Stat(dir)
    77  	if err != nil {
    78  		return "", "", err
    79  	}
    80  	if !s.IsDir() {
    81  		return "", "", fmt.Errorf("not a directory: %s", dir)
    82  	}
    83  
    84  	keyPath := filepath.Join(dir, "key.p8")
    85  	if err = checkNotExists(keyPath); err != nil {
    86  		log.Infof("Found pre-existing key: %s", keyPath)
    87  	}
    88  
    89  	csrPath := filepath.Join(dir, "csr.der")
    90  	if err = checkNotExists(csrPath); err != nil {
    91  		log.Infof("Found pre-existing CSR: %s", csrPath)
    92  	}
    93  
    94  	return keyPath, csrPath, nil
    95  }
    96  
    97  func checkNotExists(p string) (err error) {
    98  	_, err = os.Stat(p)
    99  	if err == nil {
   100  		err = fmt.Errorf("already exists: %s", p)
   101  	} else if os.IsNotExist(err) {
   102  		err = nil
   103  	}
   104  	return
   105  }
   106  
   107  func generateAndStoreKey(p string) (key *ecdsa.PrivateKey, err error) {
   108  	// Generate a private key and store it read-only. This is written to the
   109  	// file-system so that the proxy may read this key at startup. The
   110  	// destination path should generally be tmpfs so that the key material is
   111  	// not written to disk.
   112  	key, err = tls.GenerateKey()
   113  	if err != nil {
   114  		return
   115  	}
   116  
   117  	pemb := tls.EncodePrivateKeyP8(key)
   118  	err = os.WriteFile(p, pemb, 0600)
   119  	return
   120  }
   121  
   122  func generateAndStoreCSR(p, id string, key *ecdsa.PrivateKey) ([]byte, error) {
   123  	if id == "" {
   124  		return nil, errors.New("a non-empty identity is required")
   125  	}
   126  
   127  	if err := validation.IsFullyQualifiedDomainName(field.NewPath(""), id).ToAggregate(); err != nil {
   128  		return nil, fmt.Errorf("%s a fully qualified DNS name is required", id)
   129  	}
   130  
   131  	csr := x509.CertificateRequest{
   132  		Subject:  pkix.Name{CommonName: id},
   133  		DNSNames: []string{id},
   134  	}
   135  	csrb, err := x509.CreateCertificateRequest(rand.Reader, &csr, key)
   136  	if err != nil {
   137  		return nil, fmt.Errorf("failed to create CSR: %w", err)
   138  	}
   139  
   140  	if err = os.WriteFile(p, csrb, 0600); err != nil {
   141  		return nil, fmt.Errorf("failed to write CSR: %w", err)
   142  	}
   143  
   144  	return csrb, nil
   145  }
   146  
   147  func runProxy() {
   148  	// The input arguments are static.
   149  	//nolint:gosec
   150  	err := syscall.Exec("/usr/lib/linkerd/linkerd2-proxy", []string{}, os.Environ())
   151  	if err != nil {
   152  		log.Fatalf("Failed to run proxy: %s", err)
   153  	}
   154  }
   155  

View as plain text