secrets.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. Copyright 2020 Docker Compose CLI authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package compose
  14. import (
  15. "archive/tar"
  16. "bytes"
  17. "context"
  18. "fmt"
  19. "strconv"
  20. "time"
  21. "github.com/compose-spec/compose-go/v2/types"
  22. "github.com/docker/docker/api/types/container"
  23. )
  24. type mountType string
  25. const (
  26. secretMount mountType = "secret"
  27. configMount mountType = "config"
  28. )
  29. func (s *composeService) injectSecrets(ctx context.Context, project *types.Project, service types.ServiceConfig, id string) error {
  30. return s.injectFileReferences(ctx, project, service, id, secretMount)
  31. }
  32. func (s *composeService) injectConfigs(ctx context.Context, project *types.Project, service types.ServiceConfig, id string) error {
  33. return s.injectFileReferences(ctx, project, service, id, configMount)
  34. }
  35. func (s *composeService) injectFileReferences(ctx context.Context, project *types.Project, service types.ServiceConfig, id string, mountType mountType) error {
  36. mounts, sources := s.getFilesAndMap(project, service, mountType)
  37. for _, mount := range mounts {
  38. content, err := s.resolveFileContent(project, sources[mount.Source], mountType)
  39. if err != nil {
  40. return err
  41. }
  42. if content == "" {
  43. continue
  44. }
  45. if service.ReadOnly {
  46. return fmt.Errorf("cannot create %s %q in read-only service %s: `file` is the sole supported option", mountType, sources[mount.Source].Name, service.Name)
  47. }
  48. s.setDefaultTarget(&mount, mountType)
  49. if err := s.copyFileToContainer(ctx, id, content, mount); err != nil {
  50. return err
  51. }
  52. }
  53. return nil
  54. }
  55. func (s *composeService) getFilesAndMap(project *types.Project, service types.ServiceConfig, mountType mountType) ([]types.FileReferenceConfig, map[string]types.FileObjectConfig) {
  56. var files []types.FileReferenceConfig
  57. var fileMap map[string]types.FileObjectConfig
  58. switch mountType {
  59. case secretMount:
  60. files = make([]types.FileReferenceConfig, len(service.Secrets))
  61. for i, config := range service.Secrets {
  62. files[i] = types.FileReferenceConfig(config)
  63. }
  64. fileMap = make(map[string]types.FileObjectConfig)
  65. for k, v := range project.Secrets {
  66. fileMap[k] = types.FileObjectConfig(v)
  67. }
  68. case configMount:
  69. files = make([]types.FileReferenceConfig, len(service.Configs))
  70. for i, config := range service.Configs {
  71. files[i] = types.FileReferenceConfig(config)
  72. }
  73. fileMap = make(map[string]types.FileObjectConfig)
  74. for k, v := range project.Configs {
  75. fileMap[k] = types.FileObjectConfig(v)
  76. }
  77. }
  78. return files, fileMap
  79. }
  80. func (s *composeService) resolveFileContent(project *types.Project, source types.FileObjectConfig, mountType mountType) (string, error) {
  81. if source.Content != "" {
  82. // inlined, or already resolved by include
  83. return source.Content, nil
  84. }
  85. if source.Environment != "" {
  86. env, ok := project.Environment[source.Environment]
  87. if !ok {
  88. return "", fmt.Errorf("environment variable %q required by %s %q is not set", source.Environment, mountType, source.Name)
  89. }
  90. return env, nil
  91. }
  92. return "", nil
  93. }
  94. func (s *composeService) setDefaultTarget(file *types.FileReferenceConfig, mountType mountType) {
  95. if file.Target == "" {
  96. if mountType == secretMount {
  97. file.Target = "/run/secrets/" + file.Source
  98. } else {
  99. file.Target = "/" + file.Source
  100. }
  101. } else if mountType == secretMount && !isAbsTarget(file.Target) {
  102. file.Target = "/run/secrets/" + file.Target
  103. }
  104. }
  105. func (s *composeService) copyFileToContainer(ctx context.Context, id, content string, file types.FileReferenceConfig) error {
  106. b, err := createTar(content, file)
  107. if err != nil {
  108. return err
  109. }
  110. return s.apiClient().CopyToContainer(ctx, id, "/", &b, container.CopyToContainerOptions{
  111. CopyUIDGID: file.UID != "" || file.GID != "",
  112. })
  113. }
  114. func createTar(env string, config types.FileReferenceConfig) (bytes.Buffer, error) {
  115. value := []byte(env)
  116. b := bytes.Buffer{}
  117. tarWriter := tar.NewWriter(&b)
  118. mode := types.FileMode(0o444)
  119. if config.Mode != nil {
  120. mode = *config.Mode
  121. }
  122. var uid, gid int
  123. if config.UID != "" {
  124. v, err := strconv.Atoi(config.UID)
  125. if err != nil {
  126. return b, err
  127. }
  128. uid = v
  129. }
  130. if config.GID != "" {
  131. v, err := strconv.Atoi(config.GID)
  132. if err != nil {
  133. return b, err
  134. }
  135. gid = v
  136. }
  137. header := &tar.Header{
  138. Name: config.Target,
  139. Size: int64(len(value)),
  140. Mode: int64(mode),
  141. ModTime: time.Now(),
  142. Uid: uid,
  143. Gid: gid,
  144. }
  145. err := tarWriter.WriteHeader(header)
  146. if err != nil {
  147. return bytes.Buffer{}, err
  148. }
  149. _, err = tarWriter.Write(value)
  150. if err != nil {
  151. return bytes.Buffer{}, err
  152. }
  153. err = tarWriter.Close()
  154. return b, err
  155. }