Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: GPG commit signature verification (#2492) #3242

Merged
merged 132 commits into from
Jun 22, 2020

Conversation

jannfis
Copy link
Member

@jannfis jannfis commented Mar 16, 2020

Summary

This PR addresses #2492 and adds support for GPG signature verification on Git commits as well as basic management of GPG public keys to ArgoCD. GPG signature verification can be controlled on a per-project level by defining a list of key IDs in the project spec. The controller will only sync to those commits that are signed by one of the allowed key IDs, otherwise will refuse to sync.

Feature toggle and knobs

In order for this feature to be active, it must be explicitly switched on. To switch on GPG features, the environment variable ARGOCD_GPG_ENABLED must be non-empty and set to values other than false or no. The environment needs to be set for the containers of the argocd-server, argocd-controller and argocd-repo-server pods. It is not enabled by default for now.

The following additional environment variables control the features' behaviour:

  • ARGOCD_GPG_DATA_PATH (default: /app/config/gpg/source) must be set to the path where the ConfigMap containing the GPG public keys is mounted at (valid only for the argocd-repo-server pod)
  • ARGOCD_GNUPGHOME (default: /app/config/gpg/keys) can be set to where the GPG keyring will be stored. This path must be available r/w and must not reside on a persistent volume.

Completeness

  • Feature toggle
  • API for key GPG public key management
  • RBAC implementation and default rules for GPG public key API
  • Adaptions to AppProject CRD
  • New type SignatureKey for specifying valid keys for signature verification
  • GPG key management (create/read/delete) using CLI
  • GPG key management using web UI
  • GPG key management the declarative way
  • Signature verification on commits
  • Signature verification on tags
  • CLI adaptions to argocd proj for manipulating signature keys and signature verification enforcement
  • Web UI adaptions to project settings for manipulating signature keys and signature verification enforcement
  • Unit tests
  • End-to-End tests
  • Adaptions to installation manifests
  • Manual tests within a real cluster
  • Finishing touch
  • User documentation
  • Containerize test suite so toolchain is similar (chore: Containerize complete build & test toolchain #3245)

General design principles

Functionalities of this feature have been designed to follow the least-privilege concept of ArgoCD, and are split across the API server, Repository Server and Application Controller components.

  • API server handles all CRUD operations for GPG public keys assets by updating a ConfigMap containing all GPG keys
  • ConfigMap is mounted to the repo server pod(s), so data is available to repo server(s) via file system
  • Repo server is the only component which actually maintains a valid GPG key ring
  • Repo server watches the path where the ConfigMap is mounted at for added/deleted files, as the ConfigMap is update in the cluster
  • Repo server creates new public keys in the keyring (new file added) or removes existing keys from the keyring (file removed)
  • Repo server runs git verify-commit on the target revision it checks out on behalf of application controller, if instructed by controller to do so
  • Repo server sends back verification result (output of git verify-commit) in the answer to the controller
  • Controller parses the verification result and decides whether the signature is valid and performed by an allowed GPG key as per the configuration

Each repository server instance will create a GPG keyring and a private key upon initialization, both of which are transient and only valid
for the lifecycle of the pod instance. The private key is used for building the GPG trust database within the pod, and for nothing else. It is
not intended to be used elsewhere.

This design assumes trust between all ArgoCD components - especially the controller trusting the repo server - and also assumes trust into the K8s cluster ArgoCD is running in.

Introduced changes

New API for key management

The API supports the basic operations of adding, removing and listing configured GPG keys.

New gpg command in the CLI

GPG keys can be managed through the following new commands:

  • argocd gpg list lists all configured GPG public keys
  • argocd gpg get <keyid> retrieves the configured GPG public key with key ID <keyid>
  • argocd gpg add --from <path> adds one or more GPG public keys stored in file <path> to ArgoCD. The data in <path> can be either binary or ASCII-armored.
  • argocd gpg rm <keyid> removes the GPG public key from server's configuration

New ConfigMap resource

ArgoCD stores the GPG public key data in a new ConfigMap resource named argocd-gpg-keys-cm. The ConfigMap contains entries in the form of <KeyID>: <KeyData> and must be mounted to the argocd-repo-server pod, to make the public keys consumable by the repository server.

New property "signatureKeys" in AppProject CRD

The property signatureKeys in custom resource AppProject is used to enforce verification of GPG commit signatures on a per-project level. The property takes an array of SignatureKey objects, which currently only has a single property KeyID of type string. To have a project enforce signature validation, use the following

Other

New dependencies

This code uses fsnotify package from https://github.com/fsnotify/fsnotify, which is released under revised BSD license.

Checklist:

  • Either (a) I've created an enhancement proposal and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
  • The title of the PR states what changed and the related issues number (used for the release note).
  • I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
  • Optional. My organization is added to USERS.md.
  • I've signed the CLA and my build is green (troubleshooting builds).

jannfis added 30 commits March 6, 2020 21:45
@jannfis
Copy link
Member Author

jannfis commented Jun 15, 2020

PR is now feature complete. Will conduct a few more tests before declaring ready for review state.

@jannfis jannfis marked this pull request as ready for review June 16, 2020 19:20
@jannfis jannfis changed the title feat: (WIP) GPG commit signature verification (#2492) feat: GPG commit signature verification (#2492) Jun 16, 2020
Copy link
Collaborator

@alexmt alexmt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Added, several comments but I think we can resolve them later.

util/git/client.go Outdated Show resolved Hide resolved
controller/state.go Show resolved Hide resolved
controller/state.go Outdated Show resolved Hide resolved
@@ -24,6 +24,9 @@ spec:
- "20"
- --operation-processors
- "10"
env:
- name: ARGOCD_GPG_ENABLED
value: "true"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we enable by GPG by default and allow user to use ARGOCD_GPG_ENABLED to disable it? In this case we don't have to touch manifests.

Additionally GPG feature is visible in UI even if feature is disabled. Why should we let user disable it at all?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a kill switch for the GPG feature is good for various reasons. For one, there is a slight performance impact of the feature, so people can switch it off if they won't require it. Second reason is, if there is a severe security issue found in Git's GnuPG implementation, or in the way we verify commits, disabling the feature for good by the kill switch could be a viable workaround until a fix is available.

But I agree, we can change semantics to enable the feature by default without touching the manifests. Shall I change it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed the default behaviour to GPG feature being enabled and reverted the manifest changes. It still can be disabled using kill switch ARGOCD_GPG_ENABLED=false, for whatever reasons there might be.

Comment on lines +65 to +86
// Remove files that do not exist in ConfigMap anymore
err = filepath.Walk(destPath, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if err != nil {
log.Warnf("Error walking path %s: %v", path, err)
}
p := filepath.Base(path)
if _, ok := cm.Data[p]; !ok {
log.Infof("Removing file '%s'", path)
err := os.Remove(path)
if err != nil {
log.Warnf("Failed to remove file %s: %v", path, err)
}
}
return nil
})
if err != nil {
log.Fatalf("Error: %v", err)
}
// Create or update files that are specified in ConfigMap
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexmt FYI, I've also added functionality to delete files from the file system which do not exist in the config map anymore. This makes local/manual testing possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants