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: Add optional capability to seed service secrets #276

Merged
merged 9 commits into from
Sep 28, 2021

Conversation

lenny-goodell
Copy link
Member

@lenny-goodell lenny-goodell commented Sep 22, 2021

Secrets are seeded from a JSON file specified by the SecretStore.SecretsFile setting
If SecretsFile setting is blank, seeding is skipped.

closes #273

This PR depends on edgexfoundry/go-mod-secrets#126 to be merged first.

PR Checklist

Please check if your PR fulfills the following requirements:

  • I am not introducing a breaking change (if you are, flag in conventional commit message with BREAKING CHANGE: describing the break)
  • I am not introducing a new dependency (add notes below if you are)
  • I have added unit tests for the new feature or bug fix (if not, why?)
  • I have fully tested (add details below) this the new feature or bug fix (if not, why?)
  • I have opened a PR for the related docs change (if not, why?)
    feat: Document new Seeding Service Secrets capability edgex-docs#587

Testing Instructions

  1. Start edgex using compose builder make run ds-camera
    • This will create the SecretStore for device camera
  2. Stop the Device Camera container
  3. Clone branch for this PR and branch for go-mod-secrets PR feat: Add SecretsFile config setting go-mod-secrets#126
  4. Add the following to device-sdk-go go.mod
replace (
	github.com/edgexfoundry/go-mod-bootstrap/v2 => ../go-mod-bootstrap
)
  1. Add following the device-camera-go go.mod
replace (
	github.com/edgexfoundry/device-sdk-go/v2 => ../device-sdk-go
	github.com/edgexfoundry/go-mod-bootstrap/v2 => ../go-mod-bootstrap
)
  1. Build device-camera
  2. cd to cmd folder in device-camera-go
  3. create file /tmp/camera-secrets.json with the following JSON:
    {
        "secrets": [
            {
                "path": "credentials001",
                "imported": false,
                "secretData": [
                    {
                        "key": "username",
                        "value": "my-user-1"
                    },
                                    {
                        "key": "password",
                        "value": "password-001"
                    }
                ]
            }
        ]
    }
  4. run sudo EDGEX_SECURITY_SECRET_STORE=true SECRETSTORE_SECRETSFILE=/tmp/camera-secrets.json ./device-camera
  5. Device Camera logs will have the following:
level=INFO ts=2021-09-27T18:22:23.1861784Z app=device-camera source=secret.go:52 msg="Creating SecretClient"
level=INFO ts=2021-09-27T18:22:23.1862282Z app=device-camera source=secret.go:59 msg="Reading secret store configuration and authentication token"
level=INFO ts=2021-09-27T18:22:23.1878451Z app=device-camera source=secret.go:71 msg="Attempting to create secret client"
level=INFO ts=2021-09-27T18:22:23.2092923Z app=device-camera source=secrets.go:277 msg="kick off token renewal with interval: 30m0s"
level=INFO ts=2021-09-27T18:22:23.209413Z app=device-camera source=secure.go:229 msg="Seeding 1 Service Secrets"
level=INFO ts=2021-09-27T18:22:23.2115168Z app=device-camera source=secure.go:248 msg="Secret for 'credentials001' successfully stored."
level=INFO ts=2021-09-27T18:22:23.2117384Z app=device-camera source=secure.go:218 msg="Scrubbing of secrets file complete."
level=INFO ts=2021-09-27T18:22:23.2117899Z app=device-camera source=secret.go:88 msg="Created SecretClient"

And will NOT display any error getting secrets, but will fail to initialize the camera since no camera exists.
9. The /tmp/camera-secrets.json file contents are now:

"secrets":[{"path":"credentials001","imported":true,"secretData":[]}]}
  1. recreate /tmp/camera-secrets.json file contents as above with secret data.
  2. run sudo EDGEX_SECURITY_SECRET_STORE=true SECRETSTORE_SECRETSFILE=/tmp/camera-secrets.json SECRETSTORE_DISABLESCRUBSECRETSFILE=true ./device-camera
  3. Device Camera logs will have the following additional message:
level=INFO ts=2021-09-27T18:18:12.3870583Z app=device-camera source=secure.go:210 msg="Scrubbing of secrets file disable."
  1. The /tmp/camera-secrets.json file contents will not be changed. I.e. secret data will still be present in the file

New Dependency Instructions (If applicable)

N/A

Secrets are seeded from a JSON file specifed by the SecretStore.SecretsFile setting
If SecretsFile setting is blank, seeding is skipped.

closes #273

Signed-off-by: Leonard Goodell <[email protected]>
@siggiskulason
Copy link

LGTM. Thanks for the testing instructions. I tested this with my ONVIF camera, the edgexfoundry snap and a natively running device-camera, setting the following in the camera configuration.toml file:

SecretsFile = "/home/sidar/tmp/camera-secrets.json"
TokenFile = "/var/snap/edgexfoundry/current/secrets/device-camera/secrets-token.json"

with camera-secrets.json being

{
    "secrets": [
        {
            "path": "credentials001",
            "imported": false,
            "secretData": [
                {
                    "key": "username",
                    "value": "admin"
                },
                                {
                    "key": "password",
                    "value": "admin"
                }
            ]
        }
    ]
}

and then running with

sudo EDGEX_SECURITY_SECRET_STORE=true ./device-camera

that works fine:

  • The secrets are seeded correctly
  • the JSON file gets overwritten as expected
  • The service authenticates with the camera and I see readings appear with images.

@lenny-goodell
Copy link
Member Author

I tested this with my ONVIF camera, the edgexfoundry snap and a natively running device-camera,

@siggiskulason Excellent!! Thanks!

Signed-off-by: Leonard Goodell <[email protected]>
Copy link
Contributor

@jim-wang-intel jim-wang-intel left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@tonyespy tonyespy left a comment

Choose a reason for hiding this comment

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

A couple of inline comments...

bootstrap/secret/secure.go Outdated Show resolved Hide resolved
bootstrap/secret/secure.go Show resolved Hide resolved
bootstrap/secret/secure.go Outdated Show resolved Hide resolved
Signed-off-by: Leonard Goodell <[email protected]>
Signed-off-by: Leonard Goodell <[email protected]>
Copy link
Collaborator

@bnevis-i bnevis-i left a comment

Choose a reason for hiding this comment

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

Please tell us about the new dependency.

@lenny-goodell
Copy link
Member Author

Please tell us about the new dependency.

@bnevis-i , it is new to this module, but not EdgeX. It is on the approved list here:
https://wiki.edgexfoundry.org/display/FA/Vetting+Process+for+3rd+Party+Dependencies

Leonard Goodell added 2 commits September 27, 2021 08:44
Signed-off-by: Leonard Goodell <[email protected]>

lc.Debugf("SecretsFile is '%s'", secretConfig.SecretsFile)

if len(strings.TrimSpace(secretConfig.SecretsFile)) > 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The code would be less complex if this was changed to if len == 0.

Copy link
Member Author

Choose a reason for hiding this comment

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

How? still an if/else just reversing the logic.

Copy link
Collaborator

@bnevis-i bnevis-i Sep 27, 2021

Choose a reason for hiding this comment

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

  secureProvider.SetClient(secretClient)
  lc.Debugf("SecretsFile is '%s'", secretConfig.SecretsFile)
  if len(strings.TrimSpace(secretConfig.SecretsFile)) > 0 {
    err = secureProvider.LoadServiceSecrets(secretStoreConfig)
    if err != nil {
      return nil, err
    }
  } else {
    lc.Infof("SecretsFile not set, skipping seeding of service secrets.")
  }
  provider = secureProvider


  secureProvider.SetClient(secretClient)
  lc.Debugf("SecretsFile is '%s'", secretConfig.SecretsFile)
  if len(strings.TrimSpace(secretConfig.SecretsFile)) == 0 {
    lc.Infof("SecretsFile not set, skipping seeding of service secrets.")
    break
  }
  err = secureProvider.LoadServiceSecrets(secretStoreConfig)
  if err != nil {
    return nil, err
  }
  provider = secureProvider

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep! :-)

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 am always an advocate of early return to avoid indentation, just didn't see it here. THX!

Copy link
Member Author

Choose a reason for hiding this comment

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

Opps, not so fast. This skips the following important code when you do the break that you added, so still need the if/else of have repeated code.

					provider = secureProvider
					lc.Info("Created SecretClient")

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, was able to move that code up before handling of the secrets file and now the break you added will work.

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated

bootstrap/secret/secure.go Show resolved Hide resolved
bootstrap/secret/secure.go Show resolved Hide resolved
Copy link
Contributor

@jim-wang-intel jim-wang-intel left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@tonyespy tonyespy left a comment

Choose a reason for hiding this comment

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

I just want to clarify what's expected if the scrub fails, should the secrets provisioned into vault also be scrubbed? You also could structure the code such that the write to vault doesn't happen until after the JSON file gets scrubbed... If @bnevis-i you're OK with the way this is implemented, then I'll just approve, but as implemented this isn't being handled as a single transaction.

@bnevis-i
Copy link
Collaborator

I just want to clarify what's expected if the scrub fails, should the secrets provisioned into vault also be scrubbed? You also could structure the code such that the write to vault doesn't happen until after the JSON file gets scrubbed... If @bnevis-i you're OK with the way this is implemented, then I'll just approve, but as implemented this isn't being handled as a single transaction.

Yes, if the scrub fails the secrets will still be in the secret store. It is impossible to truly have a single transaction here as neither Vault nor the file system support a two-phase commit protocol. I'd rather have this behavior than the possibility that the JSON is scrubbed and then Vault fails, because there is no recovery from that.

@lenny-goodell
Copy link
Member Author

I just want to clarify what's expected if the scrub fails, should the secrets provisioned into vault also be scrubbed? You also could structure the code such that the write to vault doesn't happen until after the JSON file gets scrubbed... If @bnevis-i you're OK with the way this is implemented, then I'll just approve, but as implemented this isn't being handled as a single transaction.

@tonyespy, The secrets do not need to be scrubbed from vault if file write fails. No harm in them still being there. Once write issue is resolved they will be over written. Trying to scrub them out of vault will add unneeded complexity.

@lenny-goodell lenny-goodell merged commit a4676a4 into edgexfoundry:main Sep 28, 2021
@lenny-goodell lenny-goodell deleted the secrets-file branch September 28, 2021 16:38
judehung pushed a commit to IOTechSystems/go-mod-bootstrap that referenced this pull request Nov 15, 2021
* feat: Add optional capability to seed service secrets

Secrets are seeded from a JSON file specified by the SecretStore.SecretsFile setting
If SecretsFile setting is blank, seeding is skipped.

* feat: Add DisableScrubSecretsFile setting to control scrubbing of secrets file

closes edgexfoundry#273

Signed-off-by: Leonard Goodell <[email protected]>
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.

Add capability to seed unknown secrets during bootstrap
5 participants