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

Simplify server certificate and known hosts management #1807

Merged
merged 70 commits into from
Jul 11, 2019

Conversation

jannfis
Copy link
Member

@jannfis jannfis commented Jun 22, 2019

This PR aims to make it more simple to connect SSH and HTTPS repositories to ArgoCD. It addresses #1514 and the following topics:

  • Make it more easy to add SSH host keys to the list of known hosts across all ArgoCD instances. This is accomplished by outsourcing the known hosts data into a ConfigMap, which in turn gets mounted as volume to the pods. There is no management interface yet, neither via CLI nor via UI, so to add new host keys one has to edit the ConfigMap (argocd-runtime-cm).
  • Make it possible to connect HTTPS repositories with self-signed certificates, or those signed by unknown CAs. This PR introduces a new switch, "--insecure-skip-server-validation", which should replace the current "--insecure-ignore-host-key" switch. The current switch is kept for backwards compatibilty, but can possibly be removed.
  • When adding repositories via CLI, the client now lets the server test the connection instead of performing a connection to the repository itself. The server now exposes a verification endpoint.

A management interface for SSH known hosts management has yet to be introduced, as well as an interface for managing HTTPS certificates.

@jannfis
Copy link
Member Author

jannfis commented Jun 22, 2019

This PR is work in progress. I have yet to write tests and polish the change. And since the change is quite substantial, I want to know your thoughts on the general direction this is going.

Copy link
Member Author

@jannfis jannfis left a comment

Choose a reason for hiding this comment

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

I explicitly chose not to use a subPath mount for this ConfigMap since with subPath, changes to the ConfigMap do not get propagated to the pods automatically (i.e. you have to restart pods for changes to become effective). Using a non-subPath mount, the pods can pick up the change instantly once the ConfigMap has been propagated across the cluster.

@alexec
Copy link
Contributor

alexec commented Jun 24, 2019

looks like you're making good progress - let us know if you need any help

@jessesuen
Copy link
Member

This PR introduces a new switch, "--insecure-skip-server-validation", which should replace the current "--insecure-ignore-host-key" switch. The current switch is kept for backwards compatibilty, but can possibly be removed.

So is this PR basically munging both the disabling of (a) TLS host certificate verification and (b) SSH host key verification into a single option --insecure-skip-server-validation?

I think I'm on-board with this change, however, I think the flag should be named insecure-skip-server-verification instead of validation.

@jannfis
Copy link
Member Author

jannfis commented Jun 25, 2019

So is this PR basically munging both the disabling of (a) TLS host certificate verification and (b) SSH host key verification into a single option --insecure-skip-server-validation?

I think I'm on-board with this change, however, I think the flag should be named insecure-skip-server-verification instead of validation.

@jessesuen Yes, correct, I think it would be best to provide a single option for both cases. I am with you that verification is a better term than validation for this process, I will change the name of the switch and also the code symbols accordingly.

I'm also in the process to extend the API to provide some kind of rudimentary certificate and known hosts management, so that we can attach host keys or TLS information to repository FQDNs. I'm a little unsure where this would belong, I see two possibilities:

  • a dedicated CertificateService with endpoints at e.g. /api/v1/certificate
  • an extension of RepositoryService

I think the first would make more sense, since certs/known hosts are valid for multiple repositories, when served from same server.

Third possibilities is one I didn't see yet, but maybe someone else has an idea?

Copy link
Contributor

@alexec alexec left a comment

Choose a reason for hiding this comment

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

I'd like to request changes. It wasn't clear to me when reviewing what insecure flags. It appears to combine both flags into one uber flag.

How we deal with SSH and HTTPS is usually quite independent in the code, and we know even experienced users find it confusing what flags to use.

What are the options about having a separate flag named similarly to kubectl? E.g. --insecure-skip-tls-verify?

I also wonder, how should we document updating the list of known hosts?

for _, r := range repos.Items {
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", r.Repo, r.Username, r.ConnectionState.Status, r.ConnectionState.Message)
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", r.Repo, strconv.FormatBool((r.InsecureIgnoreHostKey || r.Insecure)), r.Username, r.ConnectionState.Status, r.ConnectionState.Message)
Copy link
Contributor

Choose a reason for hiding this comment

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

minor - you can use %v

// RepoAccessResponse is the response to RepoAccessQuery
message RepoAccessResponse {
// Whether access is possible
bool accessPossible = 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these returned? I can't find mention elsewhere in the PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Supposed to be used with the next commits - I think it's better to have a dedicated response rather than to display the raw error to the user, even if it'll contain basically the same message later on.

// explicitly replace it with default client for repositories without the
// insecure flag set.
if IsHTTPSURL(rawRepoURL) {
if insecure {
Copy link
Contributor

Choose a reason for hiding this comment

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

can we always use this library? it's got to work, so lets just always use 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.

Sure, it's basically the counterpart to IsSSHURL() - but as of know, just a wrapper around a regexp's MatchString(), and not thoroughly tested yet.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think I was clear, I'd like to avoid the if/else here on line 79. I think you can always use the customHttpClient?

@alexec
Copy link
Contributor

alexec commented Jun 25, 2019

Oh - I know I've given a lot of feedback on this, but I think you're doing a splendid job on a tricky problem.

@jannfis
Copy link
Member Author

jannfis commented Jun 25, 2019

Oh - I know I've given a lot of feedback on this, but I think you're doing a splendid job on a tricky problem.

Your feedback is much appreciated!

@jannfis
Copy link
Member Author

jannfis commented Jul 10, 2019

This requires some more work than expected. So, apologies, I'm working on a good fix for native Git client supporting custom CA bundle.

@jannfis
Copy link
Member Author

jannfis commented Jul 11, 2019

Found a good solution that works perfectly and is not dirty by using GIT_SSL_CAINFO environment variable instead of using -c http.sslCAInfo command line option. This was well hidden in the documentation of Git. Also, gotta improve my QA processes ;-)

PR now feature complete and hopefully bug-free. Ready for another review or merge, as you see fit.

@alexmt
Copy link
Collaborator

alexmt commented Jul 11, 2019

Great, I'm glad you found GIT_SSL_CAINFO. Kustomize honors env variable while cloning remote bases, so configured known hosts should work for Kustomize remote bases as well.

Thank you for completing it @jannfis !

@jannfis
Copy link
Member Author

jannfis commented Jul 11, 2019

I think Kustomize remote bases are not covered by the feature-set in this PR, @alexmt - I had a quick look at the code implementing Kustomize in util/kustomize and imho, it would need adaption. Current solution is focused on Git repositories connected to ArgoCD. However, I think it'd be not that hard to teach our Kustomize implementation to use the certificates and/or known hosts as used by the Git repository connectors. But I guess that's in scope for a next PR, and I already see a next challenge :)

@@ -16,6 +17,9 @@ p, role:admin, applications, update, */*, allow
p, role:admin, applications, delete, */*, allow
p, role:admin, applications, sync, */*, allow
p, role:admin, applications, override, */*, allow
p, role:admin, certificates, create, *, allow
Copy link
Contributor

Choose a reason for hiding this comment

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

good

}
},
}
command.Flags().BoolVar(&removeAllCerts, "remove-all", false, "Remove all configured certificates of all types from server (DANGER: use with care!)")
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a nuke all, I feel like I would never want to have the risk of using this. Also, I initially thought this, incorrectly, this was remove all the certificates for the REPOSERVER - could there be a risk others think the same. How much do we need this?

Copy link
Member Author

@jannfis jannfis Jul 11, 2019

Choose a reason for hiding this comment

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

It was convenient during test cycles, but you are right, it's quite a dangerous option (therefore the fat DANGER sign). However, the same could be accomplished by using a * wildcard as the repo server name, which is not even really documented.

I see two options:

  1. Ask the user with a prompt if he really wants to perform that dangerous operation when --remove-all is specified or * was given as the repo-server name without any other selector such as --cert-type
  2. Discard both possibilities for good, and have the user delete data from ConfigMap objects if they really want to get rid of all certificates.

Thinking of it, sh*t happens too often and I guess variant 2 is the better one. Will remove the code from the CLI. Do you think we should check on server side as well, whether '*' was specified in case someone writes against the API?

Example for adding a HTTPS repository to ArgoCD without verifying the server's certificate (**Caution:** This is **not** recommended for production use):

```bash
argocd repo add --insecure-repository https://git.example.com/test-repo
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure about the name of this flag, it's not the repository that is insecure, it is that we do not check the security of the connection. Hmmmmmm....

Copy link
Member Author

Choose a reason for hiding this comment

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

In the CLI, it still is --insecure-skip-server-verification. But I guess there is still some disagreement on the name of the switch. --insecure is already occupied for the connection between CLI and ArgoCD server component.

But personally I think, when the connection to the repository is insecure, the repository itself should be considered insecure as well - just for the fact that you don't know if ArgoCD is really connecting to the right repo, and not a MITM-installed one. If the flag is --insecure-repository, it should be clear to not trust that repository. And to not use it for production purposes.

if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceCertificates, rbacpolicy.ActionGet, ""); err != nil {
return nil, err
}
certList, err := s.db.ListRepoCertificates(ctx, &db.CertificateListSelector{
Copy link
Contributor

Choose a reason for hiding this comment

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

minor - you could just return certList, err here (same below)

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks! Will fix.


service CertificateService {
// List all available certificates
rpc List(RepositoryCertificateQuery) returns (github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.RepositoryCertificateList) {
Copy link
Contributor

@alexec alexec Jul 11, 2019

Choose a reason for hiding this comment

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

Ok. You need to rename these as they'll get weird swagger operation IDs (something like Mixin5).

I recommend "verb+nou"n names so you get sensible REST IDs.

E.g.

rpc ListCertificates(RepositoryCertificateQuery) 
rpc CreateCertificate(RepositoryCertificateCreateRequest)
rpc DeleteCertificate(RepositoryCertificateQuery) 

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, that is the reason for the swagger going wild. Thanks for the clarification, will rename those methods to the names you proposed, they sound clear & concise to me.


certificateList := make([]string, 0)

// TODO: Implement maximum amount of data to parse
Copy link
Contributor

Choose a reason for hiding this comment

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

TODO?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it's still something that I have to do but deemed not important enough for now. I guess the maximum POST size is already limited by gRPC anyway.

@@ -37,6 +37,13 @@ type ArgoDB interface {

// ListHelmRepoURLs lists configured helm repositories
ListHelmRepos(ctx context.Context) ([]*appv1.HelmRepository, error)

// ListRepoCerticifates lists all configured certificates
ListRepoCertificates(ctx context.Context, selector *CertificateListSelector) (*appv1.RepositoryCertificateList, error)
Copy link
Contributor

Choose a reason for hiding this comment

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

sure, I don't feel strongly about it

PasswordSecret *apiv1.SecretKeySelector `json:"passwordSecret,omitempty"`
SSHPrivateKeySecret *apiv1.SecretKeySelector `json:"sshPrivateKeySecret,omitempty"`
InsecureIgnoreHostKey bool `json:"insecureIgnoreHostKey,omitempty"`
// The URL to the repository
Copy link
Contributor

Choose a reason for hiding this comment

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

good stuff

// Returns the ConfigMap with the given name from the cluster.
// The ConfigMap must be labeled with "app.kubernetes.io/part-of: argocd" in
// order to be retrievable.
func (mgr *SettingsManager) GetNamedConfigMap(configMapName string) (*apiv1.ConfigMap, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

minor - all config map are named - aren't they - how about GetConfigMapByName?

Copy link
Member Author

Choose a reason for hiding this comment

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

Much better, clear & concise. Will rename it, thanks!


If you are connecting a repository on a HTTPS server using a self-signed certificate, or a certificate signed by a custom Certificate Authority (CA) which are not known to ArgoCD, the repository will not be added due to security reasons. This is indicated by an error message such as `x509: certificate signed by unknown authority`.

1. You can let ArgoCD connect the repository in an insecure way, without verifying the server's certificate at all. This can be accomplished by using the `--insecure-repository` flag when adding the repository with the `argocd` CLI utility. However, this should be done only for non-production setups, as it imposes a serious security issue through possible man-in-the-middle attacks.
Copy link
Contributor

Choose a reason for hiding this comment

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

this is incorrect?

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'm unsure about what you mean. What exactly is incorrect?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think @alexec means it supposed to be insecure-ignore-host-key instead of --insecure

Copy link
Contributor

@alexec alexec left a comment

Choose a reason for hiding this comment

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

Conditional approval - Alex M will be following up.

@alexec alexec merged commit 9cf744f into argoproj:master Jul 11, 2019
@alexmt
Copy link
Collaborator

alexmt commented Jul 11, 2019

Trying to implement e2e tests for it

jannfis added a commit to jannfis/argo-cd that referenced this pull request Jul 12, 2019
This change addresses some of the remaining minor changes that
were requested during development/review cycles of argoproj#1807:

- Rename GetNamedConfigMap() to GetConfigMapByName()
- Rename CRUD methods of CertificateService to something more
  sensible, so that swagger doesn't get confused.
- Get rid of possibly dangerous --remove-all switch in "cert rm"
  command, and also prevent user from accidentally specifying a
  single wildcard as repo-server name
jannfis added a commit to jannfis/argo-cd that referenced this pull request Jul 12, 2019
jannfis added a commit to jannfis/argo-cd that referenced this pull request Jul 12, 2019
jannfis referenced this pull request Jul 19, 2019
…es (#1934)

* Make sure insecure flag works for remote Kustomize bases
@alexec alexec added this to the v1.2 milestone Jul 24, 2019
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.

4 participants