helpers.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. package client
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "time"
  7. "github.com/Sirupsen/logrus"
  8. "github.com/docker/notary/client/changelist"
  9. store "github.com/docker/notary/storage"
  10. "github.com/docker/notary/tuf"
  11. "github.com/docker/notary/tuf/data"
  12. "github.com/docker/notary/tuf/utils"
  13. )
  14. // Use this to initialize remote HTTPStores from the config settings
  15. func getRemoteStore(baseURL, gun string, rt http.RoundTripper) (store.RemoteStore, error) {
  16. s, err := store.NewHTTPStore(
  17. baseURL+"/v2/"+gun+"/_trust/tuf/",
  18. "",
  19. "json",
  20. "key",
  21. rt,
  22. )
  23. if err != nil {
  24. return store.OfflineStore{}, err
  25. }
  26. return s, err
  27. }
  28. func applyChangelist(repo *tuf.Repo, invalid *tuf.Repo, cl changelist.Changelist) error {
  29. it, err := cl.NewIterator()
  30. if err != nil {
  31. return err
  32. }
  33. index := 0
  34. for it.HasNext() {
  35. c, err := it.Next()
  36. if err != nil {
  37. return err
  38. }
  39. isDel := data.IsDelegation(c.Scope()) || data.IsWildDelegation(c.Scope())
  40. switch {
  41. case c.Scope() == changelist.ScopeTargets || isDel:
  42. err = applyTargetsChange(repo, invalid, c)
  43. case c.Scope() == changelist.ScopeRoot:
  44. err = applyRootChange(repo, c)
  45. default:
  46. return fmt.Errorf("scope not supported: %s", c.Scope())
  47. }
  48. if err != nil {
  49. logrus.Debugf("error attempting to apply change #%d: %s, on scope: %s path: %s type: %s", index, c.Action(), c.Scope(), c.Path(), c.Type())
  50. return err
  51. }
  52. index++
  53. }
  54. logrus.Debugf("applied %d change(s)", index)
  55. return nil
  56. }
  57. func applyTargetsChange(repo *tuf.Repo, invalid *tuf.Repo, c changelist.Change) error {
  58. switch c.Type() {
  59. case changelist.TypeTargetsTarget:
  60. return changeTargetMeta(repo, c)
  61. case changelist.TypeTargetsDelegation:
  62. return changeTargetsDelegation(repo, c)
  63. case changelist.TypeWitness:
  64. return witnessTargets(repo, invalid, c.Scope())
  65. default:
  66. return fmt.Errorf("only target meta and delegations changes supported")
  67. }
  68. }
  69. func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
  70. switch c.Action() {
  71. case changelist.ActionCreate:
  72. td := changelist.TUFDelegation{}
  73. err := json.Unmarshal(c.Content(), &td)
  74. if err != nil {
  75. return err
  76. }
  77. // Try to create brand new role or update one
  78. // First add the keys, then the paths. We can only add keys and paths in this scenario
  79. err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, []string{}, td.NewThreshold)
  80. if err != nil {
  81. return err
  82. }
  83. return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, []string{}, false)
  84. case changelist.ActionUpdate:
  85. td := changelist.TUFDelegation{}
  86. err := json.Unmarshal(c.Content(), &td)
  87. if err != nil {
  88. return err
  89. }
  90. if data.IsWildDelegation(c.Scope()) {
  91. return repo.PurgeDelegationKeys(c.Scope(), td.RemoveKeys)
  92. }
  93. delgRole, err := repo.GetDelegationRole(c.Scope())
  94. if err != nil {
  95. return err
  96. }
  97. // We need to translate the keys from canonical ID to TUF ID for compatibility
  98. canonicalToTUFID := make(map[string]string)
  99. for tufID, pubKey := range delgRole.Keys {
  100. canonicalID, err := utils.CanonicalKeyID(pubKey)
  101. if err != nil {
  102. return err
  103. }
  104. canonicalToTUFID[canonicalID] = tufID
  105. }
  106. removeTUFKeyIDs := []string{}
  107. for _, canonID := range td.RemoveKeys {
  108. removeTUFKeyIDs = append(removeTUFKeyIDs, canonicalToTUFID[canonID])
  109. }
  110. err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, removeTUFKeyIDs, td.NewThreshold)
  111. if err != nil {
  112. return err
  113. }
  114. return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, td.RemovePaths, td.ClearAllPaths)
  115. case changelist.ActionDelete:
  116. return repo.DeleteDelegation(c.Scope())
  117. default:
  118. return fmt.Errorf("unsupported action against delegations: %s", c.Action())
  119. }
  120. }
  121. func changeTargetMeta(repo *tuf.Repo, c changelist.Change) error {
  122. var err error
  123. switch c.Action() {
  124. case changelist.ActionCreate:
  125. logrus.Debug("changelist add: ", c.Path())
  126. meta := &data.FileMeta{}
  127. err = json.Unmarshal(c.Content(), meta)
  128. if err != nil {
  129. return err
  130. }
  131. files := data.Files{c.Path(): *meta}
  132. // Attempt to add the target to this role
  133. if _, err = repo.AddTargets(c.Scope(), files); err != nil {
  134. logrus.Errorf("couldn't add target to %s: %s", c.Scope(), err.Error())
  135. }
  136. case changelist.ActionDelete:
  137. logrus.Debug("changelist remove: ", c.Path())
  138. // Attempt to remove the target from this role
  139. if err = repo.RemoveTargets(c.Scope(), c.Path()); err != nil {
  140. logrus.Errorf("couldn't remove target from %s: %s", c.Scope(), err.Error())
  141. }
  142. default:
  143. err = fmt.Errorf("action not yet supported: %s", c.Action())
  144. }
  145. return err
  146. }
  147. func applyRootChange(repo *tuf.Repo, c changelist.Change) error {
  148. var err error
  149. switch c.Type() {
  150. case changelist.TypeRootRole:
  151. err = applyRootRoleChange(repo, c)
  152. default:
  153. err = fmt.Errorf("type of root change not yet supported: %s", c.Type())
  154. }
  155. return err // might be nil
  156. }
  157. func applyRootRoleChange(repo *tuf.Repo, c changelist.Change) error {
  158. switch c.Action() {
  159. case changelist.ActionCreate:
  160. // replaces all keys for a role
  161. d := &changelist.TUFRootData{}
  162. err := json.Unmarshal(c.Content(), d)
  163. if err != nil {
  164. return err
  165. }
  166. err = repo.ReplaceBaseKeys(d.RoleName, d.Keys...)
  167. if err != nil {
  168. return err
  169. }
  170. default:
  171. return fmt.Errorf("action not yet supported for root: %s", c.Action())
  172. }
  173. return nil
  174. }
  175. func nearExpiry(r data.SignedCommon) bool {
  176. plus6mo := time.Now().AddDate(0, 6, 0)
  177. return r.Expires.Before(plus6mo)
  178. }
  179. func warnRolesNearExpiry(r *tuf.Repo) {
  180. //get every role and its respective signed common and call nearExpiry on it
  181. //Root check
  182. if nearExpiry(r.Root.Signed.SignedCommon) {
  183. logrus.Warn("root is nearing expiry, you should re-sign the role metadata")
  184. }
  185. //Targets and delegations check
  186. for role, signedTOrD := range r.Targets {
  187. //signedTOrD is of type *data.SignedTargets
  188. if nearExpiry(signedTOrD.Signed.SignedCommon) {
  189. logrus.Warn(role, " metadata is nearing expiry, you should re-sign the role metadata")
  190. }
  191. }
  192. //Snapshot check
  193. if nearExpiry(r.Snapshot.Signed.SignedCommon) {
  194. logrus.Warn("snapshot is nearing expiry, you should re-sign the role metadata")
  195. }
  196. //do not need to worry about Timestamp, notary signer will re-sign with the timestamp key
  197. }
  198. // Fetches a public key from a remote store, given a gun and role
  199. func getRemoteKey(url, gun, role string, rt http.RoundTripper) (data.PublicKey, error) {
  200. remote, err := getRemoteStore(url, gun, rt)
  201. if err != nil {
  202. return nil, err
  203. }
  204. rawPubKey, err := remote.GetKey(role)
  205. if err != nil {
  206. return nil, err
  207. }
  208. pubKey, err := data.UnmarshalPublicKey(rawPubKey)
  209. if err != nil {
  210. return nil, err
  211. }
  212. return pubKey, nil
  213. }
  214. // Rotates a private key in a remote store and returns the public key component
  215. func rotateRemoteKey(url, gun, role string, rt http.RoundTripper) (data.PublicKey, error) {
  216. remote, err := getRemoteStore(url, gun, rt)
  217. if err != nil {
  218. return nil, err
  219. }
  220. rawPubKey, err := remote.RotateKey(role)
  221. if err != nil {
  222. return nil, err
  223. }
  224. pubKey, err := data.UnmarshalPublicKey(rawPubKey)
  225. if err != nil {
  226. return nil, err
  227. }
  228. return pubKey, nil
  229. }
  230. // signs and serializes the metadata for a canonical role in a TUF repo to JSON
  231. func serializeCanonicalRole(tufRepo *tuf.Repo, role string) (out []byte, err error) {
  232. var s *data.Signed
  233. switch {
  234. case role == data.CanonicalRootRole:
  235. s, err = tufRepo.SignRoot(data.DefaultExpires(role))
  236. case role == data.CanonicalSnapshotRole:
  237. s, err = tufRepo.SignSnapshot(data.DefaultExpires(role))
  238. case tufRepo.Targets[role] != nil:
  239. s, err = tufRepo.SignTargets(
  240. role, data.DefaultExpires(data.CanonicalTargetsRole))
  241. default:
  242. err = fmt.Errorf("%s not supported role to sign on the client", role)
  243. }
  244. if err != nil {
  245. return
  246. }
  247. return json.Marshal(s)
  248. }