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

Allow dot and colon in variable path #5898

Merged
merged 8 commits into from
Jul 29, 2020
Merged

Conversation

xtremerui
Copy link
Contributor

@xtremerui xtremerui commented Jul 21, 2020

What does this PR accomplish?

Bug Fix | Feature | Documentation

closes #4249 .

Changes proposed by this PR:

Allow dot . and : in variable path if quoted with ". Parse variable name in ((var_name)) to VariableReference so underneath implementation of Variable interface doesn't need to.

((foo:"a_path_with.dot".bar.zoo)) ->

VariableReference{
  Name: "foo:\"a_path_with.dot\".bar.zoo",
  Source: "foo",
  Path: "a_path_with.dot",
  Fields: ["bar","zoo"],
}

Release Note

  • You can now interpolate variables with special characters . and : in the name by wrapping them in double quotes
  • e.g. (("some.secret".field1)) accesses field1 of the secret some.secret

Notes to reviewer:

Contributor Checklist

Reviewer Checklist

  • Code reviewed
  • Tests reviewed
  • Documentation reviewed
  • Release notes reviewed
  • PR acceptance performed
  • New config flags added? Ensure that they are added to the
    BOSH and
    Helm packaging; otherwise, ignored for
    the integration
    tests

    (for example, if they are Garden configs that are not displayed in the
    --help text).

atc/creds/conjur/conjur.go Outdated Show resolved Hide resolved
var cred credentials.Credential
var found bool
var err error

cred, found, err = c.findCred(secretPath)
cred, found, err = c.findCred(ref.Name)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same issue as Conjur, looking below it seems like it only cares about Path and not Fields. But I haven't actually used Credhub so can't say for sure

atc/creds/kubernetes/kubernetes_test.go Outdated Show resolved Hide resolved
atc/creds/kubernetes/secrets.go Outdated Show resolved Hide resolved
atc/creds/secret_var_lookup_rules.go Outdated Show resolved Hide resolved

var ErrEmptyVar = errors.New("empty var")
varRef := VariableReference{Name: name}
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 beginning to think that the Name shouldn't include Source, that way it'll just be Path + Fields and won't break all of our existing cred managers

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem is if we do path + fields we will need to concatenate them back with . and wrap them properly with ". I think the parsing of (()) makes us to use ref.Path for creds manager.

And the logic in template will take care of looking up fields.

Copy link
Contributor

Choose a reason for hiding this comment

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

We can probably use parts[1]

concourse/vars/template.go

Lines 144 to 145 in 46efd51

parts := strings.SplitN(name, ":", 2)
varRef.Source = parts[0]

Copy link
Contributor

Choose a reason for hiding this comment

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

And the logic in template will take care of looking up fields.

True, I guess another question is how much processing should the cred manager do vs Concourse. They technically have all the information (e.g. Fields) required to fully resolve the secret, but it might be less code duplication if Concourse handles that part

vars/template.go Outdated Show resolved Hide resolved
// Get value of a var. Name can be the following formats: 1) 'foo', where foo
// is var name; 2) 'foo:bar', where foo is var source name, and bar is var name;
// 3) '.:foo', where . means a local var, foo is var name.
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment needs updating, the main purpose now is to extract Fields from a map[string]interface{}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the end goal of this Get is still finding a string value? Extracting fields is the first part. It then use the fields to do recursive look up.

@aoldershaw
Copy link
Contributor

After a bunch of fiddling, I eventually got a local Conjur connected to Concourse. I ended up mostly following the quick start, but made the few modifications to the quick start repo. These are the changes I made

  1. Don't use SSL (was having trouble getting self-signed certs to pair with concourse)
diff --git a/conf/default.conf b/conf/default.conf
index 8aa7e18..0832396 100755
--- a/conf/default.conf
+++ b/conf/default.conf
@@ -1,11 +1,8 @@
 server {
-    listen              443 ssl;
+    listen              80;
     server_name         proxy;
     access_log          /var/log/nginx/access.log;

-    ssl_certificate     /etc/nginx/tls/nginx.crt;
-    ssl_certificate_key /etc/nginx/tls/nginx.key;
-
     location / {
       proxy_pass http://conjur;
     }
diff --git a/docker-compose.yml b/docker-compose.yml
index 1332b86..56ad583 100755
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -53,10 +53,9 @@ services:
     image: nginx:1.13.6-alpine
     container_name: nginx_proxy
     ports:
-      - "8443:443"
+      - "8081:80"
     volumes:
       - ./conf/:/etc/nginx/conf.d/:ro
-      - ./conf/tls/:/etc/nginx/tls/:ro
     depends_on:
     - conjur
     - openssl
  1. Use the following policy, rather than the provided BotApp.yml:
- !policy
  id: concourse
  body:
  - !variable "secret.value"
  - !variable "secret"
  1. Use the following docker-compose override for running concourse:
version: '3'

services:
  web:
    environment:
      CONCOURSE_CONJUR_APPLIANCE_URL: http://docker.for.mac.localhost:8081
      CONCOURSE_CONJUR_ACCOUNT: myConjurAccount
      CONCOURSE_CONJUR_AUTHN_LOGIN: admin
      CONCOURSE_CONJUR_AUTHN_API_KEY: 1xkf4xh2efa4ja7k3ng0rhdqg52dajps63ks08h511q08ks1d8a4pd
      CONCOURSE_CONJUR_SECRET_TEMPLATE: concourse/{{.Secret}}

where the API_KEY is the admin key (in the admin_data file from the quick start).


Anyway, I tested it a bit with the changes in this PR and without. Something about this change seems to not work with (at least) Conjur, even in the case where there are no dots or colons in the path. e.g. consider the following pipeline:

jobs:
- name: job
  plan:
  - task: echo
    config:
      platform: linux
      image_resource:
        type: registry-image
        source: {repository: busybox}
      run:
        path: echo
        args: [((secret))]

On master, this works correctly (i.e. echos the value of secret in Conjur), but with this change, I get the following error:

failed to interpolate task config: 404 Not Found. Variable 'concourse/main/echo/secret' not found in account 'myConjurAccount'.

(note: I had to modify to modify this line

return nil, nil, false, nil
to get the error message to show up, since currently we just ignore it and assume the var wasn't found)

@xtremerui
Copy link
Contributor Author

xtremerui commented Jul 23, 2020

Thx for the detail of integration test.

(note: I had to modify to modify this line

return nil, nil, false, nil

to get the error message to show up, since currently we just ignore it and assume the var wasn't found)

This error is strange since it outputs the correct path path for looking up secret under path concourse/team_name/pipeline_name,which matches the unit test. Concourse doesn't implement RetrieveSecret so there is no change in that part. I wonder what the argument of RetrieveSecret is in master when it success to fetch the secret. Will try this locally.

Updated: the working path in master is concourse/secret, which matches the policy defined in conjur. So the problem is concourse is using the wrong path to fetch the secret (or it doesn't try each kind of template for path)

Rui Yang and others added 6 commits July 23, 2020 11:38
* extract source, path and fields from secrect name instead of treating it
as black box
* move nested variables lookup out of template into static_vars
* refactor trackers error handling

Signed-off-by: Rui Yang <ryang@pivotal.io>
Co-authored-by: Bohan Chen <bochen@pivotal.io>
put back fields look up logic from static_vars to tempalte

Signed-off-by: Rui Yang <ruiya@vmware.com>
Signed-off-by: Rui Yang <ruiya@vmware.com>
Signed-off-by: Rui Yang <ruiya@vmware.com>
Signed-off-by: Rui Yang <ruiya@vmware.com>
Signed-off-by: Rui Yang <ruiya@vmware.com>
@xtremerui
Copy link
Contributor Author

@aoldershaw the way that creds manager looking up secret is

  • generate secret looks up path. If no templating ENV is set, it will use the default path template concourse/team_name/pipeline_name/secret, concourse/team_name/secert, concourse/secret in Conjur's case.
  • try fetching the secret by EVERY path here
    for _, rule := range sl.LookupPaths {

    notice how it relies on the return of Get
    result, _, found, err := sl.Secrets.Get(secretRef)

    . It always expect no error so it could go to next iteration. So in your case if you change the line in Conjur to return an error. It will failed in the first attempt and stop the loop, while the correct path concourse/secret is ignored. Not sure if that is your case or not.

In my local testing, I rebased master and run the test pretty much follow your instruction. I have these values set up in Conjur.

docker-compose exec client conjur variable value concourse/secret 
blah
 docker-compose exec client conjur variable value concourse/secret.value
foo

In my test both ((secret)) and (("secret.value")) revolved to blah and foo, respectively. And ((secret.value)) give back error like
failed to interpolate task config: cannot access field 'value' of non-map value ('string') from var: secret.value.

Additionally, I use the setup to test static variable by fly -t local sp -p echo -c echo.yml -v secret.value=buzz.

I also used this pipeline to test named variable

var_sources:
- name: dumb
  type: dummy
  config:
    vars:
      simple: hello!
      user.value:
        username: big
        password: sekrit

jobs:
- name: job
  plan:
  - task: echo
    config:
      platform: linux
      image_resource:
        type: registry-image
        source: {repository: busybox}
      run:
        path: echo
        args: [((dumb:"user.value".password))]

All seems working. Could you try this rebased version again? Thx!

@aoldershaw
Copy link
Contributor

aoldershaw commented Jul 27, 2020

@xtremerui just tried it out again (with both Conjur and Vault), and it works as expected. Will take a closer look through the code now

Copy link
Contributor

@aoldershaw aoldershaw left a comment

Choose a reason for hiding this comment

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

Have some questions

}

// var ErrEmptyVar = errors.New("empty var")

type varsTracker struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just realized there's both a varsTracker and a credVarsTracker (which happens to live in vars_tracker.go). Doesn't have to be on this PR to change this, but it's a bit confusing

vars/template.go Outdated Show resolved Hide resolved
vars/template.go Outdated Show resolved Hide resolved
vars/template.go Show resolved Hide resolved
atc/creds/cached_secrets.go Outdated Show resolved Hide resolved
passing variable reference is unnecessary.

Signed-off-by: Rui Yang <ruiya@vmware.com>
@xtremerui xtremerui force-pushed the issue-4249-dot-in-var-path branch 2 times, most recently from 8e2f773 to d8818bd Compare July 28, 2020 13:51
@aoldershaw
Copy link
Contributor

Looks pretty good but FYI some of the unit tests are broken - missed reverting some test code in dfcc427 I think

use source+path as unique identifier for visited all error

Signed-off-by: Rui Yang <ruiya@vmware.com>
Copy link
Contributor

@aoldershaw aoldershaw left a comment

Choose a reason for hiding this comment

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

LGTM!

@xtremerui xtremerui merged commit 721bfa4 into master Jul 29, 2020
@xtremerui xtremerui deleted the issue-4249-dot-in-var-path branch July 29, 2020 21:04
@TheSecMaven
Copy link

what version of concourse will this roll into?

@aoldershaw
Copy link
Contributor

@mkkeffeler should be the next release (6.5.0), which will probably be in around 2-3 weeks

aoldershaw added a commit to concourse/docs that referenced this pull request Aug 18, 2020
documents concourse/concourse#5898

Signed-off-by: Izabela Gomes <igomes@pivotal.io>
Co-authored-by: Aidan Oldershaw <aoldershaw@pivotal.io>
@TheSecMaven
Copy link

was this test case tested? seems to be faiiling for us on 6.6

params:
  CP_CREDENTIALS: (("me/Nonprod/N_A_GOG_GCP_SVC_PFOP_NL/CloudService-N_A_GOG_GCP_NL-me.me.com-test@me.com/password"))

@xtremerui
Copy link
Contributor Author

was it working before? Seems to me the last part com-test@me.com needs to be quoted?

@TheSecMaven
Copy link

actually no. its a conjur credential. all of those dots are dots in the actual secret name, not a sub attribute or anything like that

@xtremerui
Copy link
Contributor Author

do you mean the var is not evaluated to a value by conjur when you say "failed"? or there is an error.

@TheSecMaven
Copy link

the var is not evaluated to a value in conjur, conjur logs show it isn't being looked up. when we print "env" we see the variable that we typed in the pipeline, rather than the value. other variables work though.

@xtremerui
Copy link
Contributor Author

@mkkeffeler Thx for the info. I think the interpolation regex failed to match the var name in your particular case. This is a bug we will fix it.

@TheSecMaven
Copy link

hmm not sure i follow. Can you explain how it does that? and is there a workaround?

@xtremerui
Copy link
Contributor Author

we have a regex to first check if the node in yml file is a var that needed to be interpolated or not. For example, one condition is it needs to be surrounded by ((..)). There are some other conditions as well.

The example you gave by current regex is not considered a var at all so it is treated as normal text. By allowing @ in the regex will fix the problem. If you are more interested you can check the PR I submit to fix this later.

@xtremerui xtremerui mentioned this pull request Oct 6, 2020
7 tasks
@TheSecMaven
Copy link

got it

we also will have spaces in some vars. not sure if thats already working, but worth calling out :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement release/documented Documentation and release notes have been updated.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

No way to reference credentials with . in their name
5 participants