github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/container/lxd/shared.go (about) 1 // Copyright 2023 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Licensed under the Apache 2.0 license. See github.com/lxc/lxd COPYING file for details. 5 6 package lxd 7 8 import ( 9 "crypto/ecdsa" 10 "crypto/elliptic" 11 "crypto/rand" 12 "crypto/x509" 13 "crypto/x509/pkix" 14 "encoding/pem" 15 "fmt" 16 "math/big" 17 "net" 18 "os" 19 "os/user" 20 "time" 21 ) 22 23 // The following file exists because we can no longer import the 24 // github.com/lxc/lxd/shared package when we build with CGO_ENABLED=1. Using 25 // CGO will then compile in some lxc/lxd C code, which causes problems when 26 // attempting to cross-compile. 27 28 // IsUnixSocket returns true if the given path is either a Unix socket 29 // or a symbolic link pointing at a Unix socket. 30 func IsUnixSocket(path string) bool { 31 stat, err := os.Stat(path) 32 if err != nil { 33 return false 34 } 35 36 return (stat.Mode() & os.ModeSocket) == os.ModeSocket 37 } 38 39 // GenerateMemCert creates client or server certificate and key pair, 40 // returning them as byte arrays in memory. 41 func GenerateMemCert(client bool, addHosts bool) ([]byte, []byte, error) { 42 privk, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) 43 if err != nil { 44 return nil, nil, fmt.Errorf("Failed to generate key: %w", err) 45 } 46 47 validFrom := time.Now() 48 validTo := validFrom.Add(10 * 365 * 24 * time.Hour) 49 50 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 51 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 52 if err != nil { 53 return nil, nil, fmt.Errorf("Failed to generate serial number: %w", err) 54 } 55 56 userEntry, err := user.Current() 57 var username string 58 if err == nil { 59 username = userEntry.Username 60 if username == "" { 61 username = "UNKNOWN" 62 } 63 } else { 64 username = "UNKNOWN" 65 } 66 67 hostname, err := os.Hostname() 68 if err != nil { 69 hostname = "UNKNOWN" 70 } 71 72 template := x509.Certificate{ 73 SerialNumber: serialNumber, 74 Subject: pkix.Name{ 75 Organization: []string{"linuxcontainers.org"}, 76 CommonName: fmt.Sprintf("%s@%s", username, hostname), 77 }, 78 NotBefore: validFrom, 79 NotAfter: validTo, 80 81 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 82 BasicConstraintsValid: true, 83 } 84 85 if client { 86 template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth} 87 } else { 88 template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} 89 } 90 91 if addHosts { 92 hosts, err := mynames() 93 if err != nil { 94 return nil, nil, fmt.Errorf("Failed to get my hostname: %w", err) 95 } 96 97 for _, h := range hosts { 98 ip, _, err := net.ParseCIDR(h) 99 if err == nil { 100 if !ip.IsLinkLocalUnicast() && !ip.IsLinkLocalMulticast() { 101 template.IPAddresses = append(template.IPAddresses, ip) 102 } 103 } else { 104 template.DNSNames = append(template.DNSNames, h) 105 } 106 } 107 } else if !client { 108 template.DNSNames = []string{"unspecified"} 109 } 110 111 derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privk.PublicKey, privk) 112 if err != nil { 113 return nil, nil, fmt.Errorf("Failed to create certificate: %w", err) 114 } 115 116 data, err := x509.MarshalECPrivateKey(privk) 117 if err != nil { 118 return nil, nil, err 119 } 120 121 cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) 122 key := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: data}) 123 124 return cert, key, nil 125 } 126 127 // mynames a list of names for which the certificate will be valid. 128 // This will include the hostname and ip address. 129 func mynames() ([]string, error) { 130 h, err := os.Hostname() 131 if err != nil { 132 return nil, err 133 } 134 135 ret := []string{h, "127.0.0.1/8", "::1/128"} 136 return ret, nil 137 }