identity.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package nebula
  2. import (
  3. "fmt"
  4. "io"
  5. "net/netip"
  6. "strings"
  7. "time"
  8. "github.com/slackhq/nebula/cert"
  9. "github.com/slackhq/nebula/commands"
  10. )
  11. type identityFlags struct {
  12. Address string
  13. // addressSet records that -address was given at all, so a line that left it out is
  14. // told to pass it and one that passed it empty is told the value did not parse. Neither
  15. // is answered with this node's identity, which is whoami's to report: a program
  16. // passing a connection's remote address through must get an error for a value it
  17. // failed to fill in, not the daemon's own identity to authorize against.
  18. addressSet bool
  19. }
  20. // setAddress is -address's setter. flag.Func rather than StringVar so that presence is
  21. // known; its String is empty, which is what a client building a line from the schema
  22. // sees as the default.
  23. func (f *identityFlags) setAddress(v string) error {
  24. f.Address = v
  25. f.addressSet = true
  26. return nil
  27. }
  28. // hostIdentity is what identity reports: the identity a certificate asserts for a host,
  29. // and nothing else. Every field is read from a certificate nebula verified when the
  30. // tunnel came up, or from our own, which is what lets a program on this host make an
  31. // authorization decision from it, within one limit. Nebula drops an inbound packet
  32. // whose source address is not in the sender's certificate, and one with an overlay
  33. // source address that arrived through a peer's unsafe network rather than from the node
  34. // holding it, so for a connection that arrived on the nebula interface the source
  35. // address is the sender's and this is the sender's identity. A connection that arrived
  36. // on any other interface with an overlay source address was never checked by nebula at
  37. // all, so a program authorizing on this answer should confirm the connection came in
  38. // on the nebula interface. The field names are an API: the info API serves this shape
  39. // to programs that were not built against nebula.
  40. type hostIdentity struct {
  41. Name string `json:"name"`
  42. VpnAddrs []netip.Addr `json:"vpnAddrs"`
  43. Networks []netip.Prefix `json:"networks"`
  44. UnsafeNetworks []netip.Prefix `json:"unsafeNetworks"`
  45. Groups []string `json:"groups"`
  46. Fingerprint string `json:"fingerprint"`
  47. Issuer string `json:"issuer"`
  48. NotBefore time.Time `json:"notBefore"`
  49. NotAfter time.Time `json:"notAfter"`
  50. CertVersion int `json:"certVersion"`
  51. }
  52. // runIdentity answers for whoever owns the address: this node, when it is one of ours, or
  53. // the peer behind an established tunnel. The address is required. This node's own
  54. // identity is whoami's to report, so a caller that failed to pass an address is told so
  55. // rather than handed an identity it could mistake for its peer's.
  56. func runIdentity(ifce *Interface, args *identityFlags, a []string) (commands.Result, error) {
  57. // print-cert takes its address as an operand; this takes a flag, because the query
  58. // string of the info API names its parameters and an operand has no name there. One
  59. // spelling on every surface, so the mistake is named rather than ignored.
  60. if len(a) > 0 {
  61. return commands.Failed("identity takes the vpn addr as -address, not as an operand: identity -address %s", a[0]), nil
  62. }
  63. if !args.addressSet {
  64. return commands.Failed("No vpn addr was provided, use -address <vpn addr>[:port]; whoami prints this node's own identity"), nil
  65. }
  66. vpnAddr, err := parseVpnAddrWithOptionalPort(args.Address)
  67. if err != nil {
  68. return commands.Failed("The provided vpn addr could not be parsed: %s", args.Address), nil
  69. }
  70. crt, fingerprint := findCertificateForVpnAddr(ifce.pki.getCertState(), ifce.hostMap, vpnAddr)
  71. if crt == nil {
  72. return commands.NotFound("No active tunnel for vpn addr: %s", vpnAddr), nil
  73. }
  74. id, err := newHostIdentity(crt, fingerprint)
  75. if err != nil {
  76. return commands.Result{}, err
  77. }
  78. return commands.Value(id), nil
  79. }
  80. // runWhoami answers with this node's own identity, from its own certificate.
  81. func runWhoami(ifce *Interface) (commands.Result, error) {
  82. crt := ownCertificate(ifce.pki.getCertState())
  83. if crt == nil {
  84. return commands.Result{}, fmt.Errorf("no certificate is loaded")
  85. }
  86. id, err := newHostIdentity(crt, "")
  87. if err != nil {
  88. return commands.Result{}, err
  89. }
  90. return commands.Value(id), nil
  91. }
  92. // textIdentity is the human rendering, shared by every surface.
  93. func textIdentity(id hostIdentity, w io.Writer) error {
  94. _, err := fmt.Fprintf(w,
  95. "name: %s\nvpn addrs: %s\nnetworks: %s\nunsafe networks: %s\ngroups: %s\nfingerprint: %s\nissuer: %s\nnot before: %s\nnot after: %s\ncert version: %d\n",
  96. id.Name,
  97. joinStringers(id.VpnAddrs),
  98. joinStringers(id.Networks),
  99. joinStringers(id.UnsafeNetworks),
  100. strings.Join(id.Groups, ", "),
  101. id.Fingerprint,
  102. id.Issuer,
  103. id.NotBefore.Format(time.RFC3339),
  104. id.NotAfter.Format(time.RFC3339),
  105. id.CertVersion,
  106. )
  107. return err
  108. }
  109. func joinStringers[T fmt.Stringer](in []T) string {
  110. parts := make([]string, 0, len(in))
  111. for _, v := range in {
  112. parts = append(parts, v.String())
  113. }
  114. return strings.Join(parts, ", ")
  115. }
  116. // newHostIdentity reads the identity out of a certificate. fingerprint is the
  117. // certificate's when the caller already holds it, as a verified peer certificate does,
  118. // and "" to have it hashed here. Slices are always allocated so a host with no groups
  119. // reports [] rather than null.
  120. func newHostIdentity(crt cert.Certificate, fingerprint string) (hostIdentity, error) {
  121. if fingerprint == "" {
  122. fp, err := crt.Fingerprint()
  123. if err != nil {
  124. return hostIdentity{}, err
  125. }
  126. fingerprint = fp
  127. }
  128. networks, unsafeNetworks, groups := crt.Networks(), crt.UnsafeNetworks(), crt.Groups()
  129. id := hostIdentity{
  130. Name: crt.Name(),
  131. VpnAddrs: make([]netip.Addr, 0, len(networks)),
  132. Networks: append(make([]netip.Prefix, 0, len(networks)), networks...),
  133. UnsafeNetworks: append(make([]netip.Prefix, 0, len(unsafeNetworks)), unsafeNetworks...),
  134. Groups: append(make([]string, 0, len(groups)), groups...),
  135. Fingerprint: fingerprint,
  136. Issuer: crt.Issuer(),
  137. NotBefore: crt.NotBefore(),
  138. NotAfter: crt.NotAfter(),
  139. CertVersion: int(crt.Version()),
  140. }
  141. for _, n := range networks {
  142. id.VpnAddrs = append(id.VpnAddrs, n.Addr())
  143. }
  144. return id, nil
  145. }
  146. // ownCertificate is this node's certificate: the v2 one when it is loaded, else the v1.
  147. // A v2 certificate is a superset of the v1 and may carry more networks, and it is the
  148. // one newCertState builds myVpnAddrsTable from, so an address that matched that table is
  149. // always listed in the certificate reported here. It is nil only when no certificate is
  150. // loaded, which a running nebula never has.
  151. func ownCertificate(cs *CertState) cert.Certificate {
  152. if cs == nil {
  153. return nil
  154. }
  155. if crt := cs.getCertificate(cert.Version2); crt != nil {
  156. return crt
  157. }
  158. return cs.getCertificate(cert.Version1)
  159. }
  160. // findCertificateForVpnAddr answers who owns a vpn address: this node, from its own
  161. // certificate state, since the hostmap never carries an entry for ourselves, or the peer
  162. // behind an established tunnel. The certificate is nil when the address is unknown,
  163. // when the tunnel is still pending and when it is mid teardown. A peer's comes with the
  164. // fingerprint nebula computed when it verified the certificate; our own comes with "",
  165. // since none is kept for it. The lighthouse's DNS TXT lookup and identity both go
  166. // through it, so the two surfaces cannot disagree about whose address it is.
  167. func findCertificateForVpnAddr(cs *CertState, hostMap *HostMap, ip netip.Addr) (crt cert.Certificate, fingerprint string) {
  168. if cs != nil && cs.myVpnAddrsTable != nil && cs.myVpnAddrsTable.Contains(ip) {
  169. return ownCertificate(cs), ""
  170. }
  171. hostinfo := hostMap.QueryVpnAddr(ip)
  172. if hostinfo == nil {
  173. return nil, ""
  174. }
  175. cc := hostinfo.GetCert()
  176. if cc == nil {
  177. return nil, ""
  178. }
  179. return cc.Certificate, cc.Fingerprint
  180. }
  181. // parseVpnAddrWithOptionalPort reads a vpn address alone or with a port,
  182. // 192.168.100.7:54321 or [fd00::1]:443, so a program can pass a connection's remote
  183. // address through as its runtime reported it. A 4in6 address (::ffff:a.b.c.d) is
  184. // unmapped, which is how an IPv4 peer arrives on a dual stack listener, and a zone is
  185. // dropped, since no vpn address carries one.
  186. func parseVpnAddrWithOptionalPort(s string) (netip.Addr, error) {
  187. ip, err := netip.ParseAddr(s)
  188. if err != nil {
  189. ap, err := netip.ParseAddrPort(s)
  190. if err != nil {
  191. return netip.Addr{}, err
  192. }
  193. ip = ap.Addr()
  194. }
  195. return ip.Unmap().WithZone(""), nil
  196. }