pki.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. package nebula
  2. import (
  3. "encoding/binary"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "log/slog"
  9. "net"
  10. "net/netip"
  11. "os"
  12. "slices"
  13. "strings"
  14. "sync/atomic"
  15. "time"
  16. "github.com/flynn/noise"
  17. "github.com/gaissmai/bart"
  18. "github.com/slackhq/nebula/cert"
  19. "github.com/slackhq/nebula/config"
  20. "github.com/slackhq/nebula/handshake"
  21. "github.com/slackhq/nebula/noiseutil"
  22. "github.com/slackhq/nebula/util"
  23. )
  24. type PKI struct {
  25. cs atomic.Pointer[CertState]
  26. caPool atomic.Pointer[cert.CAPool]
  27. l *slog.Logger
  28. }
  29. type CertState struct {
  30. v1Cert cert.Certificate
  31. v1Credential *handshake.Credential
  32. v2Cert cert.Certificate
  33. v2Credential *handshake.Credential
  34. initiatingVersion cert.Version
  35. privateKey []byte
  36. pkcs11Backed bool
  37. cipher string
  38. myVpnNetworks []netip.Prefix
  39. myVpnNetworksTable *bart.Lite
  40. myVpnAddrs []netip.Addr
  41. myVpnAddrsTable *bart.Lite
  42. myVpnBroadcastAddrsTable *bart.Lite
  43. }
  44. func NewPKIFromConfig(l *slog.Logger, c *config.C) (*PKI, error) {
  45. pki := &PKI{l: l}
  46. err := pki.reload(c, true)
  47. if err != nil {
  48. return nil, err
  49. }
  50. c.RegisterReloadCallback(func(c *config.C) {
  51. rErr := pki.reload(c, false)
  52. if rErr != nil {
  53. util.LogWithContextIfNeeded("Failed to reload PKI from config", rErr, l)
  54. }
  55. })
  56. return pki, nil
  57. }
  58. func (p *PKI) GetCAPool() *cert.CAPool {
  59. return p.caPool.Load()
  60. }
  61. func (p *PKI) getCertState() *CertState {
  62. return p.cs.Load()
  63. }
  64. func (p *PKI) reload(c *config.C, initial bool) error {
  65. err := p.reloadCerts(c, initial)
  66. if err != nil {
  67. if initial {
  68. return err
  69. }
  70. err.Log(p.l)
  71. }
  72. err = p.reloadCAPool(c)
  73. if err != nil {
  74. if initial {
  75. return err
  76. }
  77. err.Log(p.l)
  78. }
  79. return nil
  80. }
  81. func (p *PKI) reloadCerts(c *config.C, initial bool) *util.ContextualError {
  82. var cipher string
  83. var currentState *CertState
  84. if initial {
  85. cipher = c.GetString("cipher", "aes")
  86. switch cipher {
  87. case "aes", "chachapoly":
  88. // Each post-handshake CipherState in noiseutil hardcodes its own
  89. // nonce endianness now, so there's nothing to set up here.
  90. default:
  91. return util.NewContextualError(
  92. "unknown cipher",
  93. m{"cipher": cipher},
  94. nil,
  95. )
  96. }
  97. } else {
  98. // Cipher cant be hot swapped so just leave it at what it was before
  99. currentState = p.cs.Load()
  100. cipher = currentState.cipher
  101. }
  102. newState, err := newCertStateFromConfig(c, cipher)
  103. if err != nil {
  104. return util.NewContextualError("Could not load client cert", nil, err)
  105. }
  106. if currentState != nil {
  107. if newState.v1Cert != nil {
  108. if currentState.v1Cert == nil {
  109. //adding certs is fine, actually. Networks-in-common confirmed in newCertState().
  110. } else {
  111. // did IP in cert change? if so, don't set
  112. if !slices.Equal(currentState.v1Cert.Networks(), newState.v1Cert.Networks()) {
  113. return util.NewContextualError(
  114. "Networks in new cert was different from old",
  115. m{"new_networks": newState.v1Cert.Networks(), "old_networks": currentState.v1Cert.Networks(), "cert_version": cert.Version1},
  116. nil,
  117. )
  118. }
  119. if currentState.v1Cert.Curve() != newState.v1Cert.Curve() {
  120. return util.NewContextualError(
  121. "Curve in new v1 cert was different from old",
  122. m{"new_curve": newState.v1Cert.Curve(), "old_curve": currentState.v1Cert.Curve(), "cert_version": cert.Version1},
  123. nil,
  124. )
  125. }
  126. }
  127. }
  128. if newState.v2Cert != nil {
  129. if currentState.v2Cert == nil {
  130. //adding certs is fine, actually
  131. } else {
  132. // did IP in cert change? if so, don't set
  133. if !slices.Equal(currentState.v2Cert.Networks(), newState.v2Cert.Networks()) {
  134. return util.NewContextualError(
  135. "Networks in new cert was different from old",
  136. m{"new_networks": newState.v2Cert.Networks(), "old_networks": currentState.v2Cert.Networks(), "cert_version": cert.Version2},
  137. nil,
  138. )
  139. }
  140. if currentState.v2Cert.Curve() != newState.v2Cert.Curve() {
  141. return util.NewContextualError(
  142. "Curve in new cert was different from old",
  143. m{"new_curve": newState.v2Cert.Curve(), "old_curve": currentState.v2Cert.Curve(), "cert_version": cert.Version2},
  144. nil,
  145. )
  146. }
  147. }
  148. } else if currentState.v2Cert != nil {
  149. //newState.v1Cert is non-nil bc empty certstates aren't permitted
  150. if newState.v1Cert == nil {
  151. return util.NewContextualError("v1 and v2 certs are nil, this should be impossible", nil, err)
  152. }
  153. //if we're going to v1-only, we need to make sure we didn't orphan any v2-cert vpnaddrs
  154. if !slices.Equal(currentState.v2Cert.Networks(), newState.v1Cert.Networks()) {
  155. return util.NewContextualError(
  156. "Removing a V2 cert is not permitted unless it has identical networks to the new V1 cert",
  157. m{"new_v1_networks": newState.v1Cert.Networks(), "old_v2_networks": currentState.v2Cert.Networks()},
  158. nil,
  159. )
  160. }
  161. }
  162. }
  163. p.cs.Store(newState)
  164. if initial {
  165. p.l.Debug("Client nebula certificate(s)", "cert", newState)
  166. } else {
  167. p.l.Info("Client certificate(s) refreshed from disk", "cert", newState)
  168. }
  169. return nil
  170. }
  171. func (p *PKI) reloadCAPool(c *config.C) *util.ContextualError {
  172. caPool, err := loadCAPoolFromConfig(p.l, c)
  173. if err != nil {
  174. return util.NewContextualError("Failed to load ca from config", nil, err)
  175. }
  176. p.caPool.Store(caPool)
  177. p.l.Debug("Trusted CA fingerprints", "fingerprints", caPool.GetFingerprints())
  178. return nil
  179. }
  180. func (cs *CertState) GetDefaultCertificate() cert.Certificate {
  181. c := cs.getCertificate(cs.initiatingVersion)
  182. if c == nil {
  183. panic("No default certificate found")
  184. }
  185. return c
  186. }
  187. // DefaultVersion returns the preferred cert version for initiating handshakes.
  188. func (cs *CertState) DefaultVersion() cert.Version { return cs.initiatingVersion }
  189. // GetCredential returns the pre-computed handshake credential for the given version, or nil.
  190. func (cs *CertState) GetCredential(v cert.Version) *handshake.Credential {
  191. switch v {
  192. case cert.Version1:
  193. return cs.v1Credential
  194. case cert.Version2:
  195. return cs.v2Credential
  196. }
  197. return nil
  198. }
  199. func (cs *CertState) getCertificate(v cert.Version) cert.Certificate {
  200. switch v {
  201. case cert.Version1:
  202. return cs.v1Cert
  203. case cert.Version2:
  204. return cs.v2Cert
  205. }
  206. return nil
  207. }
  208. func newCipherSuite(curve cert.Curve, pkcs11backed bool, cipher string) (noise.CipherSuite, error) {
  209. var dhFunc noise.DHFunc
  210. switch curve {
  211. case cert.Curve_CURVE25519:
  212. dhFunc = noise.DH25519
  213. case cert.Curve_P256:
  214. if pkcs11backed {
  215. dhFunc = noiseutil.DHP256PKCS11
  216. } else {
  217. dhFunc = noiseutil.DHP256
  218. }
  219. default:
  220. return nil, fmt.Errorf("unsupported curve: %s", curve)
  221. }
  222. if cipher == "chachapoly" {
  223. return noise.NewCipherSuite(dhFunc, noise.CipherChaChaPoly, noise.HashSHA256), nil
  224. }
  225. return noise.NewCipherSuite(dhFunc, noiseutil.CipherAESGCM, noise.HashSHA256), nil
  226. }
  227. func (cs *CertState) String() string {
  228. b, err := cs.MarshalJSON()
  229. if err != nil {
  230. return fmt.Sprintf("error marshaling certificate state: %v", err)
  231. }
  232. return string(b)
  233. }
  234. func (cs *CertState) MarshalJSON() ([]byte, error) {
  235. msg := []json.RawMessage{}
  236. if cs.v1Cert != nil {
  237. b, err := cs.v1Cert.MarshalJSON()
  238. if err != nil {
  239. return nil, err
  240. }
  241. msg = append(msg, b)
  242. }
  243. if cs.v2Cert != nil {
  244. b, err := cs.v2Cert.MarshalJSON()
  245. if err != nil {
  246. return nil, err
  247. }
  248. msg = append(msg, b)
  249. }
  250. return json.Marshal(msg)
  251. }
  252. func newCertStateFromConfig(c *config.C, cipher string) (*CertState, error) {
  253. var err error
  254. privPathOrPEM := c.GetString("pki.key", "")
  255. if privPathOrPEM == "" {
  256. return nil, errors.New("no pki.key path or PEM data provided")
  257. }
  258. rawKey, curve, isPkcs11, err := loadPrivateKey(privPathOrPEM)
  259. if err != nil {
  260. return nil, err
  261. }
  262. var rawCert []byte
  263. pubPathOrPEM := c.GetString("pki.cert", "")
  264. if pubPathOrPEM == "" {
  265. return nil, errors.New("no pki.cert path or PEM data provided")
  266. }
  267. if strings.Contains(pubPathOrPEM, "-----BEGIN") {
  268. rawCert = []byte(pubPathOrPEM)
  269. pubPathOrPEM = "<inline>"
  270. } else {
  271. rawCert, err = os.ReadFile(pubPathOrPEM)
  272. if err != nil {
  273. return nil, fmt.Errorf("unable to read pki.cert file %s: %s", pubPathOrPEM, err)
  274. }
  275. }
  276. var crt, v1, v2 cert.Certificate
  277. for {
  278. // Load the certificate
  279. crt, rawCert, err = loadCertificate(rawCert)
  280. if err != nil {
  281. return nil, err
  282. }
  283. switch crt.Version() {
  284. case cert.Version1:
  285. if v1 != nil {
  286. return nil, fmt.Errorf("v1 certificate already found in pki.cert")
  287. }
  288. v1 = crt
  289. case cert.Version2:
  290. if v2 != nil {
  291. return nil, fmt.Errorf("v2 certificate already found in pki.cert")
  292. }
  293. v2 = crt
  294. default:
  295. return nil, fmt.Errorf("unknown certificate version %v", crt.Version())
  296. }
  297. if len(rawCert) == 0 || strings.TrimSpace(string(rawCert)) == "" {
  298. break
  299. }
  300. }
  301. if v1 == nil && v2 == nil {
  302. return nil, errors.New("no certificates found in pki.cert")
  303. }
  304. useInitiatingVersion := uint32(1)
  305. if v1 == nil {
  306. // The only condition that requires v2 as the default is if only a v2 certificate is present
  307. // We do this to avoid having to configure it specifically in the config file
  308. useInitiatingVersion = 2
  309. }
  310. rawInitiatingVersion := c.GetUint32("pki.initiating_version", useInitiatingVersion)
  311. var initiatingVersion cert.Version
  312. switch rawInitiatingVersion {
  313. case 1:
  314. if v1 == nil {
  315. return nil, fmt.Errorf("can not use pki.initiating_version 1 without a v1 certificate in pki.cert")
  316. }
  317. initiatingVersion = cert.Version1
  318. case 2:
  319. initiatingVersion = cert.Version2
  320. default:
  321. return nil, fmt.Errorf("unknown pki.initiating_version: %v", rawInitiatingVersion)
  322. }
  323. return newCertState(initiatingVersion, v1, v2, isPkcs11, curve, rawKey, cipher)
  324. }
  325. func newCertState(dv cert.Version, v1, v2 cert.Certificate, pkcs11backed bool, privateKeyCurve cert.Curve, privateKey []byte, cipher string) (*CertState, error) {
  326. cs := CertState{
  327. privateKey: privateKey,
  328. pkcs11Backed: pkcs11backed,
  329. cipher: cipher,
  330. myVpnNetworksTable: new(bart.Lite),
  331. myVpnAddrsTable: new(bart.Lite),
  332. myVpnBroadcastAddrsTable: new(bart.Lite),
  333. }
  334. if v1 != nil && v2 != nil {
  335. if !slices.Equal(v1.PublicKey(), v2.PublicKey()) {
  336. return nil, util.NewContextualError("v1 and v2 public keys are not the same, ignoring", nil, nil)
  337. }
  338. if v1.Curve() != v2.Curve() {
  339. return nil, util.NewContextualError("v1 and v2 curve are not the same, ignoring", nil, nil)
  340. }
  341. if v1.Networks()[0] != v2.Networks()[0] {
  342. return nil, util.NewContextualError("v1 and v2 networks are not the same", nil, nil)
  343. }
  344. cs.initiatingVersion = dv
  345. }
  346. if v1 != nil {
  347. if pkcs11backed {
  348. //NOTE: We do not currently have a method to verify a public private key pair when the private key is in an hsm
  349. } else {
  350. if err := v1.VerifyPrivateKey(privateKeyCurve, privateKey); err != nil {
  351. return nil, fmt.Errorf("private key is not a pair with public key in nebula cert")
  352. }
  353. }
  354. v1hs, err := v1.MarshalForHandshakes()
  355. if err != nil {
  356. return nil, fmt.Errorf("error marshalling v1 certificate for handshake: %w", err)
  357. }
  358. ncs, err := newCipherSuite(v1.Curve(), pkcs11backed, cipher)
  359. if err != nil {
  360. return nil, err
  361. }
  362. cs.v1Cert = v1
  363. cs.v1Credential = handshake.NewCredential(v1, v1hs, privateKey, ncs)
  364. if cs.initiatingVersion == 0 {
  365. cs.initiatingVersion = cert.Version1
  366. }
  367. }
  368. if v2 != nil {
  369. if pkcs11backed {
  370. //NOTE: We do not currently have a method to verify a public private key pair when the private key is in an hsm
  371. } else {
  372. if err := v2.VerifyPrivateKey(privateKeyCurve, privateKey); err != nil {
  373. return nil, fmt.Errorf("private key is not a pair with public key in nebula cert")
  374. }
  375. }
  376. v2hs, err := v2.MarshalForHandshakes()
  377. if err != nil {
  378. return nil, fmt.Errorf("error marshalling v2 certificate for handshake: %w", err)
  379. }
  380. ncs, err := newCipherSuite(v2.Curve(), pkcs11backed, cipher)
  381. if err != nil {
  382. return nil, err
  383. }
  384. cs.v2Cert = v2
  385. cs.v2Credential = handshake.NewCredential(v2, v2hs, privateKey, ncs)
  386. if cs.initiatingVersion == 0 {
  387. cs.initiatingVersion = cert.Version2
  388. }
  389. }
  390. var crt cert.Certificate
  391. crt = cs.getCertificate(cert.Version2)
  392. if crt == nil {
  393. // v2 certificates are a superset, only look at v1 if its all we have
  394. crt = cs.getCertificate(cert.Version1)
  395. }
  396. for _, network := range crt.Networks() {
  397. cs.myVpnNetworks = append(cs.myVpnNetworks, network)
  398. cs.myVpnNetworksTable.Insert(network)
  399. cs.myVpnAddrs = append(cs.myVpnAddrs, network.Addr())
  400. cs.myVpnAddrsTable.Insert(netip.PrefixFrom(network.Addr(), network.Addr().BitLen()))
  401. if network.Addr().Is4() {
  402. addr := network.Masked().Addr().As4()
  403. mask := net.CIDRMask(network.Bits(), network.Addr().BitLen())
  404. binary.BigEndian.PutUint32(addr[:], binary.BigEndian.Uint32(addr[:])|^binary.BigEndian.Uint32(mask))
  405. cs.myVpnBroadcastAddrsTable.Insert(netip.PrefixFrom(netip.AddrFrom4(addr), network.Addr().BitLen()))
  406. }
  407. }
  408. return &cs, nil
  409. }
  410. func loadPrivateKey(privPathOrPEM string) (rawKey []byte, curve cert.Curve, isPkcs11 bool, err error) {
  411. var pemPrivateKey []byte
  412. if strings.Contains(privPathOrPEM, "-----BEGIN") {
  413. pemPrivateKey = []byte(privPathOrPEM)
  414. privPathOrPEM = "<inline>"
  415. rawKey, _, curve, err = cert.UnmarshalPrivateKeyFromPEM(pemPrivateKey)
  416. if err != nil {
  417. return nil, curve, false, fmt.Errorf("error while unmarshaling pki.key %s: %s", privPathOrPEM, err)
  418. }
  419. } else if strings.HasPrefix(privPathOrPEM, "pkcs11:") {
  420. rawKey = []byte(privPathOrPEM)
  421. return rawKey, cert.Curve_P256, true, nil
  422. } else {
  423. pemPrivateKey, err = os.ReadFile(privPathOrPEM)
  424. if err != nil {
  425. return nil, curve, false, fmt.Errorf("unable to read pki.key file %s: %s", privPathOrPEM, err)
  426. }
  427. rawKey, _, curve, err = cert.UnmarshalPrivateKeyFromPEM(pemPrivateKey)
  428. if err != nil {
  429. return nil, curve, false, fmt.Errorf("error while unmarshaling pki.key %s: %s", privPathOrPEM, err)
  430. }
  431. }
  432. return
  433. }
  434. func loadCertificate(b []byte) (cert.Certificate, []byte, error) {
  435. c, b, err := cert.UnmarshalCertificateFromPEM(b)
  436. if err != nil {
  437. return nil, b, fmt.Errorf("error while unmarshaling pki.cert: %w", err)
  438. }
  439. if c.Expired(time.Now()) {
  440. return nil, b, fmt.Errorf("nebula certificate for this host is expired")
  441. }
  442. if len(c.Networks()) == 0 {
  443. return nil, b, fmt.Errorf("no networks encoded in certificate")
  444. }
  445. if c.IsCA() {
  446. return nil, b, fmt.Errorf("host certificate is a CA certificate")
  447. }
  448. return c, b, nil
  449. }
  450. func loadCAPoolFromConfig(l *slog.Logger, c *config.C) (*cert.CAPool, error) {
  451. caPathOrPEM := c.GetString("pki.ca", "")
  452. if caPathOrPEM == "" {
  453. return nil, errors.New("no pki.ca path or PEM data provided")
  454. }
  455. var caReader io.ReadCloser
  456. var err error
  457. if strings.Contains(caPathOrPEM, "-----BEGIN") {
  458. caReader = io.NopCloser(strings.NewReader(caPathOrPEM))
  459. } else {
  460. caReader, err = os.Open(caPathOrPEM)
  461. if err != nil {
  462. return nil, fmt.Errorf("unable to read pki.ca file %s: %s", caPathOrPEM, err)
  463. }
  464. }
  465. defer caReader.Close()
  466. caPool, err := cert.NewCAPoolFromPEMReader(caReader)
  467. if errors.Is(err, cert.ErrExpired) {
  468. var expired int
  469. for _, crt := range caPool.CAs {
  470. if crt.Certificate.Expired(time.Now()) {
  471. expired++
  472. l.Warn("expired certificate present in CA pool", "cert", crt)
  473. }
  474. }
  475. if expired >= len(caPool.CAs) {
  476. return nil, errors.New("no valid CA certificates present")
  477. }
  478. } else if err != nil {
  479. return nil, fmt.Errorf("error while adding CA certificate to CA trust store: %s", err)
  480. }
  481. bl := c.GetStringSlice("pki.blocklist", []string{})
  482. if len(bl) > 0 {
  483. for _, fp := range bl {
  484. caPool.BlocklistFingerprint(fp)
  485. }
  486. l.Info("Blocklisted certificates", "fingerprintCount", len(bl))
  487. }
  488. return caPool, nil
  489. }