Skip to content

Commit 94b4333

Browse files
iapershinalexey-igrychev
authored andcommitted
fix(build, secrets): fix secrets validation error when rendering config
Signed-off-by: Yaroslav Pershin <62902094+iapershin@users.noreply.github.com>
1 parent 59acd8c commit 94b4333

File tree

12 files changed

+221
-169
lines changed

12 files changed

+221
-169
lines changed

Diff for: go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ require (
273273
github.com/mattn/go-isatty v0.0.20 // indirect
274274
github.com/mattn/go-runewidth v0.0.15 // indirect
275275
github.com/mattn/go-shellwords v1.0.12 // indirect
276-
github.com/mattn/go-sqlite3 v1.14.22 // indirect
276+
github.com/mattn/go-sqlite3 v2.0.1+incompatible // indirect
277277
github.com/miekg/pkcs11 v1.1.1 // indirect
278278
github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect
279279
github.com/mitchellh/go-homedir v1.1.0 // indirect

Diff for: go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -951,8 +951,8 @@ github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOq
951951
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
952952
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
953953
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
954-
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
955-
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
954+
github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw=
955+
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
956956
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
957957
github.com/mattn/go-zglob v0.0.6 h1:mP8RnmCgho4oaUYDIDn6GNxYk+qJGUs8fJLn+twYj2A=
958958
github.com/mattn/go-zglob v0.0.6/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY=

Diff for: pkg/build/builder/ansible.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
"github.com/werf/common-go/pkg/util"
1818
"github.com/werf/logboek"
19+
"github.com/werf/werf/v2/pkg/build/secrets"
1920
"github.com/werf/werf/v2/pkg/config"
2021
"github.com/werf/werf/v2/pkg/container_backend"
2122
"github.com/werf/werf/v2/pkg/container_backend/stage_builder"
@@ -247,7 +248,7 @@ func (b *Ansible) stageHostWorkDir(userStageName string) (string, error) {
247248

248249
func (b *Ansible) addBuildSecretsVolumes(stageHostTmpDir string, fn func(string)) error {
249250
for _, s := range b.secrets {
250-
secretPath, err := s.GetMountPath(stageHostTmpDir)
251+
secretPath, err := secrets.GetMountPath(s, stageHostTmpDir)
251252
if err != nil {
252253
return err
253254
}

Diff for: pkg/build/builder/shell.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/werf/common-go/pkg/util"
1313
"github.com/werf/logboek"
14+
"github.com/werf/werf/v2/pkg/build/secrets"
1415
"github.com/werf/werf/v2/pkg/config"
1516
"github.com/werf/werf/v2/pkg/container_backend"
1617
"github.com/werf/werf/v2/pkg/container_backend/stage_builder"
@@ -200,7 +201,7 @@ func (b *Shell) containerTmpDir() string {
200201

201202
func (b *Shell) addBuildSecretsVolumes(stageHostTmpDir string, fn func(string)) error {
202203
for _, s := range b.secrets {
203-
secretPath, err := s.GetMountPath(stageHostTmpDir)
204+
secretPath, err := secrets.GetMountPath(s, stageHostTmpDir)
204205
if err != nil {
205206
return err
206207
}

Diff for: pkg/build/image/dockerfile.go

+21-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/werf/common-go/pkg/util"
1515
"github.com/werf/logboek"
16+
"github.com/werf/werf/v2/pkg/build/secrets"
1617
"github.com/werf/werf/v2/pkg/build/stage"
1718
stage_instruction "github.com/werf/werf/v2/pkg/build/stage/instruction"
1819
"github.com/werf/werf/v2/pkg/config"
@@ -86,6 +87,15 @@ func mapDockerfileToImagesSets(ctx context.Context, cfg *dockerfile.Dockerfile,
8687
}{WerfImageName: werfImageName, Stage: stage, Level: level})
8788
}
8889

90+
buildSecrets := make([]string, 0, len(dockerfileImageConfig.Secrets))
91+
for _, s := range dockerfileImageConfig.Secrets {
92+
secret, err := secrets.GetSecretStringArg(s)
93+
if err != nil {
94+
return nil, fmt.Errorf("unable to get build secrets: %w", err)
95+
}
96+
buildSecrets = append(buildSecrets, secret)
97+
}
98+
8999
for len(queue) > 0 {
90100
item := queue[0]
91101
queue = queue[1:]
@@ -219,7 +229,7 @@ func mapDockerfileToImagesSets(ctx context.Context, cfg *dockerfile.Dockerfile,
219229
case *dockerfile.DockerfileStageInstruction[*instructions.OnbuildCommand]:
220230
stg = stage_instruction.NewOnBuild(typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, &baseStageOptions)
221231
case *dockerfile.DockerfileStageInstruction[*instructions.RunCommand]:
222-
stg = stage_instruction.NewRun(typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, &baseStageOptions, dockerfileImageConfig.Secrets, dockerfileImageConfig.SSH)
232+
stg = stage_instruction.NewRun(typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, &baseStageOptions, buildSecrets, dockerfileImageConfig.SSH)
223233
case *dockerfile.DockerfileStageInstruction[*instructions.ShellCommand]:
224234
stg = stage_instruction.NewShell(typedInstr, dockerfileImageConfig.Dependencies, !isFirstStage, &baseStageOptions)
225235
case *dockerfile.DockerfileStageInstruction[*instructions.StopSignalCommand]:
@@ -320,6 +330,15 @@ func mapLegacyDockerfileToImage(ctx context.Context, metaConfig *config.Meta, do
320330
ProjectName: opts.ProjectName,
321331
}
322332

333+
buildSecrets := make([]string, 0, len(dockerfileImageConfig.Secrets))
334+
for _, s := range dockerfileImageConfig.Secrets {
335+
secret, err := secrets.GetSecretStringArg(s)
336+
if err != nil {
337+
return nil, fmt.Errorf("unable to get build secrets: %w", err)
338+
}
339+
buildSecrets = append(buildSecrets, secret)
340+
}
341+
323342
imageCacheVersion := option.ValueOrDefault(dockerfileImageConfig.CacheVersion(), metaConfig.Build.CacheVersion)
324343

325344
dockerfileStage := stage.GenerateFullDockerfileStage(stage.NewDockerRunArgs(
@@ -332,7 +351,7 @@ func mapLegacyDockerfileToImage(ctx context.Context, metaConfig *config.Meta, do
332351
dockerfileImageConfig.AddHost,
333352
dockerfileImageConfig.Network,
334353
dockerfileImageConfig.SSH,
335-
dockerfileImageConfig.Secrets,
354+
buildSecrets,
336355
), ds, stage.NewContextChecksum(dockerIgnorePathMatcher), baseStageOptions, dockerfileImageConfig.Dependencies, imageCacheVersion)
337356

338357
img.stages = append(img.stages, dockerfileStage)

Diff for: pkg/build/secrets/build_secrets.go

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package secrets
2+
3+
import (
4+
"fmt"
5+
"math"
6+
"math/rand/v2"
7+
"os"
8+
9+
"github.com/werf/common-go/pkg/util"
10+
"github.com/werf/werf/v2/pkg/config"
11+
)
12+
13+
type SecretFromEnv struct {
14+
Id string
15+
Value string
16+
}
17+
18+
type SecretFromSrc struct {
19+
Id string
20+
Value string
21+
}
22+
23+
type SecretFromPlainValue struct {
24+
Id string
25+
Value string
26+
}
27+
28+
type Secret interface {
29+
GetSecretStringArg() (string, error)
30+
GetMountPath(stageHostTmpDir string) (string, error)
31+
}
32+
33+
func GetSecretStringArg(secret config.Secret) (string, error) {
34+
s, err := parseSecret(secret)
35+
if err != nil {
36+
return "", fmt.Errorf("error parsing secrets: %w", err)
37+
}
38+
return s.GetSecretStringArg()
39+
}
40+
41+
func (s *SecretFromEnv) GetSecretStringArg() (string, error) {
42+
return fmt.Sprintf("id=%s,env=%s", s.Id, s.Value), nil
43+
}
44+
45+
func (s *SecretFromSrc) GetSecretStringArg() (string, error) {
46+
return fmt.Sprintf("id=%s,src=%s", s.Id, s.Value), nil
47+
}
48+
49+
func (s *SecretFromPlainValue) GetSecretStringArg() (string, error) {
50+
secret, err := s.setPlainValueAsEnv()
51+
if err != nil {
52+
return "", err
53+
}
54+
return secret.GetSecretStringArg()
55+
}
56+
57+
func (s *SecretFromPlainValue) setPlainValueAsEnv() (*SecretFromEnv, error) {
58+
envKey := fmt.Sprintf("tmpbuild%d_%s", rand.IntN(math.MaxInt32), s.Id) // generate unique value
59+
if _, e := os.LookupEnv(envKey); e {
60+
return nil, fmt.Errorf("can't set secret %s: id is not unique", s.Id) // should never be here
61+
}
62+
63+
err := os.Setenv(envKey, s.Value)
64+
if err != nil {
65+
return nil, fmt.Errorf("can't set value")
66+
}
67+
68+
return &SecretFromEnv{
69+
Id: s.Id,
70+
Value: envKey,
71+
}, nil
72+
}
73+
74+
func GetMountPath(secret config.Secret, stageHostTmpDir string) (string, error) {
75+
s, err := parseSecret(secret)
76+
if err != nil {
77+
return "", fmt.Errorf("unable to get secret mount path: %w", err)
78+
}
79+
return s.GetMountPath(stageHostTmpDir)
80+
}
81+
82+
func parseSecret(secret config.Secret) (Secret, error) {
83+
if secret.ValueFromEnv != "" {
84+
return newSecretFromEnv(secret)
85+
} else if secret.ValueFromSrc != "" {
86+
return newSecretFromSrc(secret)
87+
} else if secret.ValueFromPlain != "" {
88+
return newSecretFromPlainValue(secret)
89+
}
90+
return nil, fmt.Errorf("unknown secret type")
91+
}
92+
93+
func newSecretFromEnv(s config.Secret) (*SecretFromEnv, error) {
94+
if _, exists := os.LookupEnv(s.ValueFromEnv); !exists {
95+
return nil, fmt.Errorf("specified env variable `%s` is not set", s.ValueFromEnv)
96+
}
97+
return &SecretFromEnv{Id: s.Id, Value: s.ValueFromEnv}, nil
98+
}
99+
100+
func newSecretFromSrc(s config.Secret) (*SecretFromSrc, error) {
101+
absPath, err := util.ExpandPath(s.ValueFromSrc)
102+
if err != nil {
103+
return nil, fmt.Errorf("error load secret from src: %w", err)
104+
}
105+
106+
if exists, _ := util.FileExists(absPath); !exists {
107+
return nil, fmt.Errorf("error load secret from src: path %s doesn't exist", absPath)
108+
}
109+
return &SecretFromSrc{Id: s.Id, Value: absPath}, nil
110+
}
111+
112+
func newSecretFromPlainValue(s config.Secret) (*SecretFromPlainValue, error) {
113+
return &SecretFromPlainValue{Id: s.Id, Value: s.ValueFromPlain}, nil
114+
}
115+
116+
func (s *SecretFromEnv) GetMountPath(stageHostTmpDir string) (string, error) {
117+
data := []byte(os.Getenv(s.Value))
118+
return getMountPath(s.Id, stageHostTmpDir, data)
119+
}
120+
121+
func (s *SecretFromSrc) GetMountPath(stageHostTmpDir string) (string, error) {
122+
return generateMountPath(s.Id, s.Value), nil
123+
}
124+
125+
func (s *SecretFromPlainValue) GetMountPath(stageHostTmpDir string) (string, error) {
126+
return getMountPath(s.Id, stageHostTmpDir, []byte(s.Value))
127+
}
128+
129+
func getMountPath(secretId, stageHostTmpDir string, data []byte) (string, error) {
130+
tmpFile, err := writeToTmpFile(stageHostTmpDir, data)
131+
if err != nil {
132+
return "", fmt.Errorf("unable to mount secret: %w", err)
133+
}
134+
return generateMountPath(secretId, tmpFile), nil
135+
}
136+
137+
func writeToTmpFile(stageHostTmpDir string, data []byte) (string, error) {
138+
tmpFile, err := os.CreateTemp(stageHostTmpDir, "stapel*")
139+
if err != nil {
140+
return "", err
141+
}
142+
143+
tmpFilePath := tmpFile.Name()
144+
145+
if err := os.WriteFile(tmpFilePath, data, 0o400); err != nil {
146+
return "", err
147+
}
148+
149+
return tmpFilePath, nil
150+
}
151+
152+
func generateMountPath(id, filepath string) string {
153+
containerPath := fmt.Sprintf("/run/secrets/%s", id)
154+
return fmt.Sprintf("%s:%s:ro", filepath, containerPath)
155+
}

Diff for: pkg/config/image_from_dockerfile.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type ImageFromDockerfile struct {
2020
SSH string
2121
Dependencies []*Dependency
2222
Staged bool
23-
Secrets []string
23+
Secrets []Secret
2424
ImageSpec *ImageSpec
2525

2626
cacheVersion string

Diff for: pkg/config/raw_image_from_dockerfile.go

+1-10
Original file line numberDiff line numberDiff line change
@@ -145,16 +145,7 @@ func (c *rawImageFromDockerfile) toImageFromDockerfileDirective(giterminismManag
145145
return nil, err
146146
}
147147

148-
secretsArgs := make([]string, 0, len(secrets))
149-
for _, s := range secrets {
150-
secret, err := s.GetSecretStringArg()
151-
if err != nil {
152-
return nil, err
153-
}
154-
secretsArgs = append(secretsArgs, secret)
155-
}
156-
157-
image.Secrets = secretsArgs
148+
image.Secrets = secrets
158149

159150
if c.RawImageSpec != nil {
160151
image.ImageSpec = c.RawImageSpec.toDirective()

Diff for: pkg/config/raw_image_from_dockerfile_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ var _ = Describe("rawImageFromDockerfile", func() {
5353
Name: "image1",
5454
ContextAddFiles: []string{},
5555
AddHost: []string{},
56-
Secrets: []string{},
56+
Secrets: []Secret{},
5757

5858
cacheVersion: "docker-cache-version",
5959
platform: []string{},

Diff for: pkg/config/raw_secrets.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,6 @@ func (s *rawSecret) toDirective() (Secret, error) {
5959
case s.PlainValue != "":
6060
return newSecretFromPlainValue(s)
6161
default:
62-
return nil, newDetailedConfigError("secret should be defined as `env`, `src` or `value`", s, s.parent.getDoc())
62+
return Secret{}, newDetailedConfigError("secret should be defined as `env`, `src` or `value`", s, s.parent.getDoc())
6363
}
6464
}

0 commit comments

Comments
 (0)