Skip to content

Commit

Permalink
feat(security): Add sentinel file config and also address PR feedback
Browse files Browse the repository at this point in the history
- add Sentinel filepath configuration in toml

Address Bryon's PR feedback/comments

Signed-off-by: Jim Wang <[email protected]>
  • Loading branch information
jim-wang-intel committed Mar 4, 2021
1 parent 7c9b397 commit 191f7d1
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ if [ "${ENABLE_REGISTRY_ACL}" == "true" ]; then
echo "$(date) failed to set up Consul ACL"
fi
set -e
# no need to wait for Consul's port since it is in ready state after all ACL stuff
else
echo "$(date) Starting edgex-core-consul with ACL disabled ..."
docker-entrypoint.sh agent \
Expand All @@ -101,6 +102,7 @@ else
-server \
-client 0.0.0.0 &
# wait for the consul port
# this waitFor is not necessary in the other ACL case, as it is already in the ready state
echo "$(date) Executing waitFor on Consul with waiting on its own port \
tcp://${STAGEGATE_REGISTRY_HOST}:${STAGEGATE_REGISTRY_PORT}"
/edgex-init/security-bootstrapper --confdir=/edgex-init/res waitFor \
Expand Down
4 changes: 3 additions & 1 deletion cmd/security-bootstrapper/res/configuration.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ LogLevel = 'INFO'
BootstrapTokenPath = "/tmp/edgex/secrets/edgex-consul/admin/bootstrap_token.json"
# this is the filepath for the Vault token created from secretstore-setup
SecretsAdminTokenPath = "/tmp/edgex/secrets/edgex-consul/admin/token.json"
# this is the filepath for the sentinel file to indicate the registry ACL is set up successfully
SentinelFilePath = "/edgex-init/consul-bootstrapper/consul_acl_done"
[StageGate.KongDb]
Host = "kong-db"
Port = 5432
Expand All @@ -41,5 +43,5 @@ LogLevel = 'INFO'
[SecretStore]
Type = 'vault'
Protocol = 'http'
Host = 'edgex-vault'
Host = 'localhost'
Port = 8200
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ LogLevel = 'DEBUG'
[SecretStore]
Type = "vault"
Protocol = "http"
Host = "edgex-vault"
Host = "localhost"
Port = 8200
ServerName = ""
CaFilePath = ""
Expand Down
2 changes: 1 addition & 1 deletion cmd/security-secretstore-setup/res/configuration.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ LogLevel = 'DEBUG'
[SecretStore]
Type = "vault"
Protocol = "http"
Host = "edgex-vault"
Host = "localhost"
Port = 8200
CertPath = ""
CaFilePath = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,8 @@ import (

// BootStrapACLTokenInfo is the key portion of the response metadata from consulACLBootstrapAPI
type BootStrapACLTokenInfo struct {
AccessorID string `json:"AccessorID"`
SecretID string `json:"SecretID"`
Policies []Policy `json:"Policies"`
CreateTime string `json:"CreateTime"`
SecretID string `json:"SecretID"`
Policies []Policy `json:"Policies"`
}

// Policy is the metadata for ACL policy
Expand Down
65 changes: 28 additions & 37 deletions internal/security/bootstrapper/command/setupacl/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import (
"github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/startup"

"github.com/edgexfoundry/go-mod-secrets/v2/pkg"
"github.com/edgexfoundry/go-mod-secrets/v2/pkg/token/authtokenloader"
"github.com/edgexfoundry/go-mod-secrets/v2/pkg/token/fileioperformer"

"github.com/edgexfoundry/go-mod-core-contracts/v2/clients"
"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger"
Expand All @@ -55,7 +57,6 @@ const (
defaultRetryTimeout = 30 * time.Second
emptyLeader = `""`
emptyToken = ""
sentinelFile = "consul_acl_done"
)

type cmd struct {
Expand Down Expand Up @@ -102,7 +103,12 @@ func (c *cmd) Execute() (statusCode int, err error) {
// need to have a sentinel file to guard against the re-run of the command once we have successfully bootstrap ACL
// if we already have a sentinelFile exists then skip this whole process since we already done this
// process successfully before, otherwise Consul's ACL bootstrap will cause a panic
if c.hasSentinelFile() {
sentinelFileAbsPath, err := filepath.Abs(c.configuration.StageGate.Registry.ACL.SentinelFilePath)
if err != nil {
return interfaces.StatusCodeExitWithError, fmt.Errorf("failed to get the absolute path of the sentinel file: %v", err)
}

if helper.CheckIfFileExists(sentinelFileAbsPath) {
c.loggingClient.Info("Registry ACL had been setup successfully already, skip")
return
}
Expand Down Expand Up @@ -260,27 +266,16 @@ func (c *cmd) getSecretStoreTokenFromFile() (string, error) {
return emptyToken, fmt.Errorf("secretstore token file %s not found", tokenFileAbsPath)
}

type SecretStoreToken struct {
Authentication struct {
Token string `json:"client_token"`
} `json:"auth"`
}

tokenJSONFile, err := os.Open(trimmedFilePath)
defer func() { tokenJSONFile.Close() }()

fileOpener := fileioperformer.NewDefaultFileIoPerformer()
tokenLoader := authtokenloader.NewAuthTokenLoader(fileOpener)
secretStoreToken, err := tokenLoader.Load(tokenFileAbsPath)
if err != nil {
return emptyToken, fmt.Errorf("failed to open secretstore token file: %v", err)
}

var tokenData SecretStoreToken
if err := json.NewDecoder(tokenJSONFile).Decode(&tokenData); err != nil {
return emptyToken, fmt.Errorf("failed to decode secretstore json token file: %v", err)
return emptyToken, fmt.Errorf("tokenLoader failed to load secretstore token: %v", err)
}

c.loggingClient.Infof("successfully retrieved secretstore management token from %s", trimmedFilePath)

return tokenData.Authentication.Token, nil
return secretStoreToken, nil
}

// configureConsulAccess is to enable the Consul config access to the SecretStore via consul/config/access API
Expand Down Expand Up @@ -343,30 +338,26 @@ func (c *cmd) configureConsulAccess(secretStoreToken string, bootstrapACLToken s
}

func (c *cmd) writeSentinelFile() error {
filePathToSave := c.getSentinelFilePath()
fileHandle, err := os.OpenFile(filePathToSave, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
defer func() { fileHandle.Close() }()

absPath, err := filepath.Abs(c.configuration.StageGate.Registry.ACL.SentinelFilePath)
if err != nil {
return fmt.Errorf("failed to open sentinel file %s: %v", filePathToSave, err)
return fmt.Errorf("failed to get the absolute path of the sentinel file: %v", err)
}

if _, err := fileHandle.Write([]byte("done")); err != nil {
return fmt.Errorf("failed to write out to sentinel file %s: %v", filePathToSave, err)
dirToWrite := filepath.Dir(absPath)
filePerformer := fileioperformer.NewDefaultFileIoPerformer()
if err := filePerformer.MkdirAll(dirToWrite, 0700); err != nil {
return fmt.Errorf("failed to create sentinel base dir: %s", err.Error())
}

return nil
}
writer, err := filePerformer.OpenFileWriter(absPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
if err != nil {
return fmt.Errorf("failed to open file writer %s: %s", absPath, err.Error())
}
defer func() { _ = writer.Close() }()

func (c *cmd) hasSentinelFile() bool {
sentinelFilePath := c.getSentinelFilePath()
return helper.CheckIfFileExists(sentinelFilePath)
}
if _, err := writer.Write([]byte("done")); err != nil {
return fmt.Errorf("failed to write out to sentinel file %s: %v", absPath, err)
}

func (c *cmd) getSentinelFilePath() string {
// use the same directory as the bootstrap acl token for this sentinel file directory
// as it is the only writable directory in terms of volume mount in docker
absPath, _ := filepath.Abs(c.configuration.StageGate.Registry.ACL.BootstrapTokenPath)
sentinelDir := filepath.Dir(absPath)
return filepath.Join(sentinelDir, sentinelFile)
return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func TestExecute(t *testing.T) {
// setup token related configs
conf.StageGate.Registry.ACL.SecretsAdminTokenPath = filepath.Join(test.adminDir, "secret_token.json")
conf.StageGate.Registry.ACL.BootstrapTokenPath = filepath.Join(test.adminDir, "bootstrap_token.json")
conf.StageGate.Registry.ACL.SentinelFilePath = filepath.Join(test.adminDir, "sentinel_test_file")

setupRegistryACL, err := NewCommand(ctx, wg, lc, conf, []string{})
require.NoError(t, err)
Expand Down Expand Up @@ -162,7 +163,7 @@ func TestExecute(t *testing.T) {
require.Equal(t, interfaces.StatusCodeExitNormal, statusCode)
require.FileExists(t, conf.StageGate.Registry.ACL.BootstrapTokenPath)
require.FileExists(t, conf.StageGate.Registry.ACL.SecretsAdminTokenPath)
require.FileExists(t, localcmd.getSentinelFilePath())
require.FileExists(t, conf.StageGate.Registry.ACL.SentinelFilePath)
}
})
}
Expand Down
2 changes: 2 additions & 0 deletions internal/security/bootstrapper/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ type ACLInfo struct {
BootstrapTokenPath string
// filepath for the secretstore's token created from secretstore-setup
SecretsAdminTokenPath string
// filepath for the sentinel file to indicate the registry ACL is set up successfully
SentinelFilePath string
}

// KongDBInfo defines the fields related to
Expand Down

0 comments on commit 191f7d1

Please sign in to comment.