slackhq/nebula · error

error while unmarshaling pki.key %s: %s

Error message

error while unmarshaling pki.key %s: %s

What it means

loadPrivateKey treats pki.key values not starting with '-----BEGIN' or 'pkcs11:' as file paths and calls os.ReadFile; the file could not be read. The %s fields carry the path and the OS error (e.g. no such file, permission denied).

Source

Thrown at pki.go:500

		if network.Addr().Is4() {
			addr := network.Masked().Addr().As4()
			mask := net.CIDRMask(network.Bits(), network.Addr().BitLen())
			binary.BigEndian.PutUint32(addr[:], binary.BigEndian.Uint32(addr[:])|^binary.BigEndian.Uint32(mask))
			cs.myVpnBroadcastAddrsTable.Insert(netip.PrefixFrom(netip.AddrFrom4(addr), network.Addr().BitLen()))
		}
	}

	return &cs, nil
}

func loadPrivateKey(privPathOrPEM string) (rawKey []byte, curve cert.Curve, isPkcs11 bool, err error) {
	var pemPrivateKey []byte
	if strings.Contains(privPathOrPEM, "-----BEGIN") {
		pemPrivateKey = []byte(privPathOrPEM)
		privPathOrPEM = "<inline>"
		rawKey, _, curve, err = cert.UnmarshalPrivateKeyFromPEM(pemPrivateKey)
		if err != nil {
			return nil, curve, false, fmt.Errorf("error while unmarshaling pki.key %s: %s", privPathOrPEM, err)
		}
	} else if strings.HasPrefix(privPathOrPEM, "pkcs11:") {
		rawKey = []byte(privPathOrPEM)
		return rawKey, cert.Curve_P256, true, nil
	} else {
		pemPrivateKey, err = os.ReadFile(privPathOrPEM)
		if err != nil {
			return nil, curve, false, fmt.Errorf("unable to read pki.key file %s: %s", privPathOrPEM, err)
		}
		rawKey, _, curve, err = cert.UnmarshalPrivateKeyFromPEM(pemPrivateKey)
		if err != nil {
			return nil, curve, false, fmt.Errorf("error while unmarshaling pki.key %s: %s", privPathOrPEM, err)
		}
	}

	return
}

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Fix the pki.key path or create/copy the key file there
  2. Fix permissions so the nebula process user can read the file (chmod 600, correct owner)
  3. If the key is inline PEM, ensure it starts with '-----BEGIN' so it isn't treated as a path

Example fix

// before
pki:
  key: /etc/nebula/pki.key   # file absent
// after
nebula-cert keygen -out-key /etc/nebula/pki.key
chown nebula:nebula /etc/nebula/pki.key && chmod 600 /etc/nebula/pki.key
Defensive patterns

Strategy: validation

Validate before calling

// validate inline or file-based key before handing it to nebula
func validatePrivateKey(keyValue string) error {
    if strings.Contains(keyValue, "-----BEGIN") {
        _, _, _, err := cert.UnmarshalPrivateKeyFromPEM([]byte(keyValue))
        return err
    }
    if strings.HasPrefix(keyValue, "pkcs11:") { return nil }
    b, err := os.ReadFile(keyValue)
    if err != nil { return err }
    _, _, _, err = cert.UnmarshalPrivateKeyFromPEM(b)
    return err
}

Type guard

func isInlinePEMKey(v string) bool { return strings.Contains(v, "-----BEGIN") }
func isPkcs11URI(v string) bool { return strings.HasPrefix(v, "pkcs11:") }

Try / catch

if err := loadPrivateKey(key); err != nil {
    if strings.Contains(err.Error(), "error while unmarshaling pki.key") {
        log.Fatalf("pki.key is not a valid unencrypted nebula private key PEM: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: loadPrivateKey (from newCertStateFromConfig): pki.key is a path and os.ReadFile fails — file missing, wrong path, unreadable permissions, or directory instead of file.

Common situations: Deployed config references a key path that wasn't mounted; systemd service lacks read permission on the key; typo in path; container secret not mounted.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/a4bd43dd05eeb159. Report an issue: GitHub.