Skip to content

Commit ae73f74

Browse files
authored
feat: Support for signing commits with gpg and ssh (#710)
Signed-off-by: Dustin Lactin <[email protected]>
1 parent 43f579e commit ae73f74

File tree

10 files changed

+212
-57
lines changed

10 files changed

+212
-57
lines changed

.github/actions/spelling/expect.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
aeece
33
Artifactory
44
applicationid
5+
atlassian
6+
Bitbucket
7+
bitbucketserver
58
bacd
69
CVE
710
credref
@@ -11,11 +14,14 @@ eec
1114
fbd
1215
ffb
1316
gitlab
17+
GPG
1418
helmvalues
19+
html
1520
installationid
1621
jfrog
1722
mep
1823
myregistry
24+
openpgp
1925
PRIVATEKEYDATA
2026
repocreds
2127
rollbacked

cmd/main.go

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,28 @@ const applicationsAPIKindArgoCD = "argocd"
2828

2929
// ImageUpdaterConfig contains global configuration and required runtime data
3030
type ImageUpdaterConfig struct {
31-
ApplicationsAPIKind string
32-
ClientOpts argocd.ClientOptions
33-
ArgocdNamespace string
34-
DryRun bool
35-
CheckInterval time.Duration
36-
ArgoClient argocd.ArgoCD
37-
LogLevel string
38-
KubeClient *kube.KubernetesClient
39-
MaxConcurrency int
40-
HealthPort int
41-
MetricsPort int
42-
RegistriesConf string
43-
AppNamePatterns []string
44-
AppLabel string
45-
GitCommitUser string
46-
GitCommitMail string
47-
GitCommitMessage *template.Template
48-
DisableKubeEvents bool
49-
GitCreds git.CredsStore
31+
ApplicationsAPIKind string
32+
ClientOpts argocd.ClientOptions
33+
ArgocdNamespace string
34+
DryRun bool
35+
CheckInterval time.Duration
36+
ArgoClient argocd.ArgoCD
37+
LogLevel string
38+
KubeClient *kube.KubernetesClient
39+
MaxConcurrency int
40+
HealthPort int
41+
MetricsPort int
42+
RegistriesConf string
43+
AppNamePatterns []string
44+
AppLabel string
45+
GitCommitUser string
46+
GitCommitMail string
47+
GitCommitMessage *template.Template
48+
GitCommitSigningKey string
49+
GitCommitSigningMethod string
50+
GitCommitSignOff bool
51+
DisableKubeEvents bool
52+
GitCreds git.CredsStore
5053
}
5154

5255
// newRootCommand implements the root command of argocd-image-updater

cmd/run.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ func newRunCommand() *cobra.Command {
240240
runCmd.Flags().BoolVar(&warmUpCache, "warmup-cache", true, "whether to perform a cache warm-up on startup")
241241
runCmd.Flags().StringVar(&cfg.GitCommitUser, "git-commit-user", env.GetStringVal("GIT_COMMIT_USER", "argocd-image-updater"), "Username to use for Git commits")
242242
runCmd.Flags().StringVar(&cfg.GitCommitMail, "git-commit-email", env.GetStringVal("GIT_COMMIT_EMAIL", "[email protected]"), "E-Mail address to use for Git commits")
243+
runCmd.Flags().StringVar(&cfg.GitCommitSigningKey, "git-commit-signing-key", env.GetStringVal("GIT_COMMIT_SIGNING_KEY", ""), "GnuPG key ID or path to Private SSH Key used to sign the commits")
244+
runCmd.Flags().StringVar(&cfg.GitCommitSigningMethod, "git-commit-signing-method", env.GetStringVal("GIT_COMMIT_SIGNING_METHOD", "openpgp"), "Method used to sign Git commits ('openpgp' or 'ssh')")
245+
runCmd.Flags().BoolVar(&cfg.GitCommitSignOff, "git-commit-sign-off", env.GetBoolVal("GIT_COMMIT_SIGN_OFF", false), "Whether to sign-off git commits")
243246
runCmd.Flags().StringVar(&commitMessagePath, "git-commit-message-path", defaultCommitTemplatePath, "Path to a template to use for Git commit messages")
244247
runCmd.Flags().BoolVar(&cfg.DisableKubeEvents, "disable-kube-events", env.GetBoolVal("IMAGE_UPDATER_KUBE_EVENTS", false), "Disable kubernetes events")
245248

@@ -319,16 +322,19 @@ func runImageUpdater(cfg *ImageUpdaterConfig, warmUp bool) (argocd.ImageUpdaterR
319322
defer sem.Release(1)
320323
log.Debugf("Processing application %s", app)
321324
upconf := &argocd.UpdateConfiguration{
322-
NewRegFN: registry.NewClient,
323-
ArgoClient: cfg.ArgoClient,
324-
KubeClient: cfg.KubeClient,
325-
UpdateApp: &curApplication,
326-
DryRun: dryRun,
327-
GitCommitUser: cfg.GitCommitUser,
328-
GitCommitEmail: cfg.GitCommitMail,
329-
GitCommitMessage: cfg.GitCommitMessage,
330-
DisableKubeEvents: cfg.DisableKubeEvents,
331-
GitCreds: cfg.GitCreds,
325+
NewRegFN: registry.NewClient,
326+
ArgoClient: cfg.ArgoClient,
327+
KubeClient: cfg.KubeClient,
328+
UpdateApp: &curApplication,
329+
DryRun: dryRun,
330+
GitCommitUser: cfg.GitCommitUser,
331+
GitCommitEmail: cfg.GitCommitMail,
332+
GitCommitMessage: cfg.GitCommitMessage,
333+
GitCommitSigningKey: cfg.GitCommitSigningKey,
334+
GitCommitSigningMethod: cfg.GitCommitSigningMethod,
335+
GitCommitSignOff: cfg.GitCommitSignOff,
336+
DisableKubeEvents: cfg.DisableKubeEvents,
337+
GitCreds: cfg.GitCreds,
332338
}
333339
res := argocd.UpdateApplication(upconf, syncState)
334340
result.NumApplicationsProcessed += 1

docs/basics/update-methods.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,58 @@ as the author. You can override the author using the
247247
`git.user` and `git.email`
248248
in the `argocd-image-updater-config` ConfigMap.
249249

250+
## <a name="method-git-commit-signing"></a>Enabling commit signature signing using an SSH or GPG key
251+
252+
### 1. SCM branch protection rules require signed commits
253+
Commit signing for SCM branch protection rules require the repository be accessed using HTTPS or SSH with a user account.
254+
Repositories accessed using a GitHub App can not be verified when using the git command line at this time.
255+
256+
Each Git commit associated with an author's name and email address can be signed via a private SSH key or GPG key.
257+
258+
Commit signing requires a bot account with a GPG or SSH key and the username and email address configured to match the bot account.
259+
260+
Your preferred signing key must be associated with your bot account. See SCM provider documentation for further details:
261+
* [GitHub](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)
262+
* [GitLab](https://docs.gitlab.com/ee/user/project/repository/signed_commits/)
263+
* [Bitbucket](https://confluence.atlassian.com/bitbucketserver/controlling-access-to-code-776639770.html)
264+
265+
### 2. Signing commits for future use with ArgoCD Source Verification Policies
266+
Commits can also be signed for use with source verification.
267+
In this case signing keys do not need to be associated with an SCM user account.
268+
269+
**SSH:**
270+
271+
The private key must be mounted and accessible on the `argocd-image-updater` pod.
272+
273+
Set `git.commit-signing-key` `argocd-image-updater-config` ConfigMap to the path of your private key:
274+
275+
```yaml
276+
data:
277+
git.commit-sign-off: "true"
278+
git.commit-signing-key: /app/.ssh/id_rsa
279+
git.commit-signing-method: "ssh"
280+
```
281+
282+
Create a new SSH secret or use your existing SSH secret:
283+
```bash
284+
kubectl -n argocd-image-updater create secret generic ssh-git-creds \
285+
--from-file=sshPrivateKey=~/.ssh/id_rsa
286+
```
287+
288+
**GPG:**
289+
290+
The GPG private key must be installed and available in the `argocd-image-updater` pod.
291+
The `git.commit-signing-method` defaults to `openpgp`.
292+
Set `git.commit-signing-key` in the `argocd-image-updater-config` ConfigMap to the GPG key ID you want to use:
293+
294+
```yaml
295+
data:
296+
git.commit-sign-off: "true"
297+
git.commit-signing-key: 3AA5C34371567BD2
298+
```
299+
300+
#### Commit Sign Off can be enabled by setting `git.commit-sign-off: "true"`
301+
250302
### <a name="method-git-commit-message"></a>Changing the Git commit message
251303

252304
You can change the default commit message used by Argo CD Image Updater to some

ext/git/mocks/Client.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/git/writer.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ type CommitOptions struct {
1414
CommitMessageText string
1515
// CommitMessagePath holds the path to a file to be used for the commit message (-F option)
1616
CommitMessagePath string
17-
// SigningKey holds a GnuPG key ID used to sign the commit with (-S option)
17+
// SigningKey holds a GnuPG key ID or path to a Private SSH Key used to sign the commit with (-S option)
1818
SigningKey string
19+
// SigningMethod holds the signing method used to sign commits. (git -c gpg.format=ssh option)
20+
SigningMethod string
1921
// SignOff specifies whether to sign-off a commit (-s option)
2022
SignOff bool
2123
}
@@ -25,16 +27,18 @@ type CommitOptions struct {
2527
// changes will be commited. If message is not the empty string, it will be
2628
// used as the commit message, otherwise a default commit message will be used.
2729
// If signingKey is not the empty string, commit will be signed with the given
28-
// GPG key.
30+
// GPG or SSH key.
2931
func (m *nativeGitClient) Commit(pathSpec string, opts *CommitOptions) error {
3032
defaultCommitMsg := "Update parameters"
31-
args := []string{"commit"}
33+
// Git configuration
34+
config := "gpg.format=" + opts.SigningMethod
35+
args := []string{"-c", config, "commit"}
3236
if pathSpec == "" || pathSpec == "*" {
3337
args = append(args, "-a")
3438
}
35-
if opts.SigningKey != "" {
36-
args = append(args, "-S", opts.SigningKey)
37-
}
39+
// Commit fails with a space between -S flag and path to SSH key
40+
// -S/user/test/.ssh/signingKey or -SAAAAAAAA...
41+
args = append(args, fmt.Sprintf("-S%s", opts.SigningKey))
3842
if opts.SignOff {
3943
args = append(args, "-s")
4044
}

manifests/base/deployment/argocd-image-updater-deployment.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,24 @@ spec:
7777
name: argocd-image-updater-config
7878
key: git.email
7979
optional: true
80+
- name: GIT_COMMIT_SIGNING_KEY
81+
valueFrom:
82+
configMapKeyRef:
83+
key: git.commit-signing-key
84+
name: argocd-image-updater-config
85+
optional: true
86+
- name: GIT_COMMIT_SIGNING_METHOD
87+
valueFrom:
88+
configMapKeyRef:
89+
key: git.commit-signing-method
90+
name: argocd-image-updater-config
91+
optional: true
92+
- name: GIT_COMMIT_SIGN_OFF
93+
valueFrom:
94+
configMapKeyRef:
95+
key: git.commit-sign-off
96+
name: argocd-image-updater-config
97+
optional: true
8098
- name: IMAGE_UPDATER_KUBE_EVENTS
8199
valueFrom:
82100
configMapKeyRef:
@@ -116,6 +134,10 @@ spec:
116134
name: ssh-config
117135
- mountPath: /tmp
118136
name: tmp
137+
- name: ssh-signing-key
138+
mountPath: /app/.ssh/id_rsa
139+
readOnly: true
140+
subPath: sshPrivateKey
119141
serviceAccountName: argocd-image-updater
120142
volumes:
121143
- configMap:
@@ -135,5 +157,9 @@ spec:
135157
name: argocd-image-updater-ssh-config
136158
optional: true
137159
name: ssh-config
160+
- name: ssh-signing-key
161+
secret:
162+
secretName: ssh-git-creds
163+
optional: true
138164
- emptyDir: {}
139165
name: tmp

manifests/install.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,24 @@ spec:
158158
key: git.email
159159
name: argocd-image-updater-config
160160
optional: true
161+
- name: GIT_COMMIT_SIGNING_KEY
162+
valueFrom:
163+
configMapKeyRef:
164+
key: git.commit-signing-key
165+
name: argocd-image-updater-config
166+
optional: true
167+
- name: GIT_COMMIT_SIGNING_METHOD
168+
valueFrom:
169+
configMapKeyRef:
170+
key: git.commit-signing-method
171+
name: argocd-image-updater-config
172+
optional: true
173+
- name: GIT_COMMIT_SIGN_OFF
174+
valueFrom:
175+
configMapKeyRef:
176+
key: git.commit-sign-off
177+
name: argocd-image-updater-config
178+
optional: true
161179
- name: IMAGE_UPDATER_KUBE_EVENTS
162180
valueFrom:
163181
configMapKeyRef:
@@ -199,6 +217,10 @@ spec:
199217
name: ssh-config
200218
- mountPath: /tmp
201219
name: tmp
220+
- mountPath: /app/.ssh/id_rsa
221+
name: ssh-signing-key
222+
readOnly: true
223+
subPath: sshPrivateKey
202224
serviceAccountName: argocd-image-updater
203225
volumes:
204226
- configMap:
@@ -218,5 +240,9 @@ spec:
218240
name: argocd-image-updater-ssh-config
219241
optional: true
220242
name: ssh-config
243+
- name: ssh-signing-key
244+
optional: true
245+
secret:
246+
secretName: ssh-git-creds
221247
- emptyDir: {}
222248
name: tmp

pkg/argocd/git.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,13 @@ func commitChangesGit(app *v1alpha1.Application, wbc *WriteBackConfig, changeLis
234234
defer os.Remove(cm.Name())
235235
}
236236

237+
if wbc.GitCommitSigningKey != "" {
238+
commitOpts.SigningKey = wbc.GitCommitSigningKey
239+
}
240+
241+
commitOpts.SigningMethod = wbc.GitCommitSigningMethod
242+
commitOpts.SignOff = wbc.GitCommitSignOff
243+
237244
err = gitC.Commit("", commitOpts)
238245
if err != nil {
239246
return err

0 commit comments

Comments
 (0)