diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7f528617..d8495129 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -128,6 +128,15 @@ jobs: VAULT_AWS_REGION: ${{ secrets.INTEGRATIONTEST_VAULT_AWS_REGION }} VAULT_AWS_PKH_TZ2: ${{ secrets.INTEGRATIONTEST_VAULT_AWS_TZ2 }} VAULT_AWS_PKH_TZ3: ${{ secrets.INTEGRATIONTEST_VAULT_AWS_TZ3 }} + VAULT_AZ_CLIENTCERTTHUMB: ${{ secrets.INTEGRATIONTEST_VAULT_AZ_CLIENTCERTTHUMB }} + VAULT_AZ_CLIENTID: ${{ secrets.INTEGRATIONTEST_VAULT_AZ_CLIENTID }} + VAULT_AZ_RESGROUP: ${{ secrets.INTEGRATIONTEST_VAULT_AZ_RESGROUP }} + VAULT_AZ_SP_KEY: ${{ secrets.INTEGRATIONTEST_VAULT_AZ_SP_KEY }} + VAULT_AZ_SUBID: ${{ secrets.INTEGRATIONTEST_VAULT_AZ_SUBID }} + VAULT_AZ_TENANTID: ${{ secrets.INTEGRATIONTEST_VAULT_AZ_TENANTID }} + VAULT_AZ_VAULT: ${{ secrets.INTEGRATIONTEST_VAULT_AZ_VAULT }} + VAULT_AZ_TZ2: ${{ secrets.INTEGRATIONTEST_VAULT_AZ_TZ2 }} + VAULT_AZ_TZ3: ${{ secrets.INTEGRATIONTEST_VAULT_AZ_TZ3 }} VAULT_GCP_PROJECTID: ${{ secrets.INTEGRATIONTEST_VAULT_GCP_PROJECTID }} VAULT_GCP_PRIVATEKEYID: ${{ secrets.INTEGRATIONTEST_VAULT_GCP_PRIVATEKEYID }} VAULT_GCP_PRIVATEKEY: ${{ secrets.INTEGRATIONTEST_VAULT_GCP_PRIVATEKEY }} @@ -141,6 +150,7 @@ jobs: run: > . integration_test/.env.${{ matrix.testenvs }}; envsubst < integration_test/gcp-token-template.json > integration_test/gcp-token.json; + echo $VAULT_AZ_SP_KEY |base64 -d >integration_test/service-principal.key; docker compose -f integration_test/docker-compose.yml up -d --wait --pull always; docker exec octez sudo chown -R tezos /home/tezos/.tezos-client; go test $(go list ./... | grep integration_test); diff --git a/.gitignore b/.gitignore index f51e085c..be8576b3 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ signatory-cli # some integration_tests write secret env var to files integration_test/gcp-token.json integration_test/.env.vaults.cicd +integration_test/service-principal.key diff --git a/integration_test/.env.vaults b/integration_test/.env.vaults index 684750ea..850d6fa0 100644 --- a/integration_test/.env.vaults +++ b/integration_test/.env.vaults @@ -5,6 +5,16 @@ export VAULT_AWS_REGION=${{ secrets.INTEGRATIONTEST_VAULT_AWS_REGION }} export VAULT_AWS_PKH_TZ2=${{ secrets.INTEGRATIONTEST_VAULT_AWS_TZ2 }} export VAULT_AWS_PKH_TZ3=${{ secrets.INTEGRATIONTEST_VAULT_AWS_TZ3 }} +export VAULT_AZ_CLIENTCERTTHUMB=${{ secrets.INTEGRATIONTEST_VAULT_AZ_CLIENTCERTTHUMB }} +export VAULT_AZ_CLIENTID=${{ secrets.INTEGRATIONTEST_VAULT_AZ_CLIENTID }} +export VAULT_AZ_RESGROUP=${{ secrets.INTEGRATIONTEST_VAULT_AZ_RESGROUP }} +export VAULT_AZ_SP_KEY=${{ secrets.INTEGRATIONTEST_VAULT_AZ_SP_KEY }} +export VAULT_AZ_SUBID=${{ secrets.INTEGRATIONTEST_VAULT_AZ_SUBID }} +export VAULT_AZ_TENANTID=${{ secrets.INTEGRATIONTEST_VAULT_AZ_TENANTID }} +export VAULT_AZ_VAULT=${{ secrets.INTEGRATIONTEST_VAULT_AZ_VAULT }} +export VAULT_AZ_TZ2=${{ secrets.INTEGRATIONTEST_VAULT_AZ_TZ2 }} +export VAULT_AZ_TZ3=${{ secrets.INTEGRATIONTEST_VAULT_AZ_TZ3 }} + export VAULT_GCP_PROJECTID=${{ secrets.INTEGRATIONTEST_VAULT_GCP_PROJECTID }} export VAULT_GCP_PRIVATEKEYID=${{ secrets.INTEGRATIONTEST_VAULT_GCP_PRIVATEKEYID }} export VAULT_GCP_PRIVATEKEY=${{ secrets.INTEGRATIONTEST_VAULT_GCP_PRIVATEKEY }} diff --git a/integration_test/README.md b/integration_test/README.md index e32cff4d..6f1e2f0f 100644 --- a/integration_test/README.md +++ b/integration_test/README.md @@ -4,7 +4,7 @@ The tests in this folder use a docker compose file to orchestrate the starting o The version of Signatory that is run is defined by an environment variable named `IMAGE`. -The `octez-client` that is run by the tests is provided by the `tezos` container, not the `octez-client` that is onboard the `flextesa` image, so that official `tezos` image releases can be used. The version of `tezos` container is defined by an environment variable named `OCTEZ_VERSION`. +The `octez-client` that is run by the tests is provided by the `tezos` container. The version of `tezos` container is defined by an environment variable named `OCTEZ_VERSION`. Currently, it is always the `latest` version of the `flextesa` image that is run by the tests. The economic protocol run by flextesa is defined by an environment variable named `PROTOCOL` @@ -76,8 +76,7 @@ Github secrets are used to define vault env var used in github workflows. To run . .env.vaults ``` -<<<<<<< HEAD -### optional: using GCP vault +### using GCP vault If you want to run GCP vault tests you need to substitute GCP vault env var into the GCP token file that gets mounted to Signatory file system: @@ -85,8 +84,12 @@ If you want to run GCP vault tests you need to substitute GCP vault env var into envsubst < gcp-token-template.json > gcp-token.json ``` -======= ->>>>>>> main +### using AZ vault + +```sh +echo $VAULT_AZ_SP_KEY |base64 -d >service-principal.key +``` + Next, start the stack: ```sh @@ -124,3 +127,11 @@ Most tests can be re-run successfully as detailed above. Some tests (like the ` ## Notes to the operator Some tests in this folder make edits to `signatory.yaml` configuration and restart the Signatory service. By design, tests that do this shall clean up after themselves by restoring the copy of the file that is in the code repository. If `git status` after a test run shows you have modifications to the `signatory.yaml` file, then that would mean a test is failing to clean up after itself and should be corrected. Function `backup_then_update_config()` and `defer restore_config()` should be used by tests that edit config. Likewise, `git status` may show you new files in the `.tezos-client` folder, another indication of a test not cleaning up after itself. Function `clean_tezos_folder()` should be used by tests that leave state behind in `.tezos-client`. + +The PEM file that is used for AZ authentication is stored in env var `VAULT_AZ_SP_KEY` which in github actions is supplied via secret `${{ secrets.INTEGRATIONTEST_VAULT_AZ_SP_KEY }}`. Because github secrets do not support multiline values, the PEM file content was base64 encoded before entered as the value of the secret. With the private key in a file named `service-principal.key` the base64 value is generated by: + +```sh +cat service-principal.key|base64 -e >service-principal.base64 +``` + +The string value in file `service-principal.base64` is then used in env var `VAULT_AZ_SP_KEY`. diff --git a/integration_test/docker-compose.yml b/integration_test/docker-compose.yml index 36592ee6..9b89e8c5 100644 --- a/integration_test/docker-compose.yml +++ b/integration_test/docker-compose.yml @@ -79,6 +79,8 @@ services: target: /etc/secret.json - source: gcp-token target: /etc/gcp-token.json + - source: az-sp-key + target: /etc/service-principal.key environment: - GOOGLE_APPLICATION_CREDENTIALS=/etc/gcp-token.json command: serve @@ -96,3 +98,5 @@ configs: file: ./signatory-local-secret.json gcp-token: file: ./gcp-token.json + az-sp-key: + file: ./service-principal.key diff --git a/integration_test/service.go b/integration_test/service.go index ea0e6b6d..5d1fad87 100644 --- a/integration_test/service.go +++ b/integration_test/service.go @@ -1,6 +1,7 @@ package integrationtest import ( + "fmt" "os/exec" ) @@ -9,8 +10,9 @@ func restart_signatory() { if err != nil { panic("failed to stop signatory") } - _, err = exec.Command("docker", "compose", "-f", "./docker-compose.yml", "up", "-d", "--wait", "signatory").CombinedOutput() + out, err := exec.Command("docker", "compose", "-f", "./docker-compose.yml", "up", "-d", "--wait", "signatory").CombinedOutput() if err != nil { + fmt.Println("restart signatory: failed to start: " + string(out)) panic("failed to start signatory during restart") } } diff --git a/integration_test/vault_az_test.go b/integration_test/vault_az_test.go new file mode 100644 index 00000000..d377c9d9 --- /dev/null +++ b/integration_test/vault_az_test.go @@ -0,0 +1,70 @@ +package integrationtest + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAZVault(t *testing.T) { + + spkey := "/etc/service-principal.key" + + thumb := os.Getenv("VAULT_AZ_CLIENTCERTTHUMB") + clientid := os.Getenv("VAULT_AZ_CLIENTID") + resgroup := os.Getenv("VAULT_AZ_RESGROUP") + subid := os.Getenv("VAULT_AZ_SUBID") + tenantid := os.Getenv("VAULT_AZ_TENANTID") + vault := os.Getenv("VAULT_AZ_VAULT") + + tz2 := os.Getenv("VAULT_AZ_TZ2") + tz3 := os.Getenv("VAULT_AZ_TZ3") + + tz2alias := "aztz2" + tz3alias := "aztz3" + + //config + var c Config + c.Read() + var v VaultConfig + v.Driver = "azure" + v.Conf = map[string]*string{"vault": &vault, "tenant_id": &tenantid, "client_id": &clientid, "client_private_key": &spkey, "client_certificate_thumbprint": &thumb, "subscription_id": &subid, "resource_group": &resgroup} + c.Vaults["azure"] = &v + var p TezosPolicy + p.LogPayloads = true + p.Allow = map[string][]string{"generic": {"reveal", "transaction"}} + c.Tezos[tz2] = &p + c.Tezos[tz3] = &p + backup_then_update_config(c) + defer restore_config() + restart_signatory() + + //setup + out, err := OctezClient("import", "secret", "key", tz2alias, "http://signatory:6732/"+tz2) + assert.NoError(t, err) + assert.Contains(t, string(out), "Tezos address added: "+tz2) + defer OctezClient("forget", "address", tz2alias, "--force") + out, err = OctezClient("import", "secret", "key", tz3alias, "http://signatory:6732/"+tz3) + assert.NoError(t, err) + assert.Contains(t, string(out), "Tezos address added: "+tz3) + defer OctezClient("forget", "address", tz3alias, "--force") + + out, err = OctezClient("transfer", "100", "from", "alice", "to", tz2alias, "--burn-cap", "0.06425") + assert.NoError(t, err) + require.Contains(t, string(out), "Operation successfully injected in the node") + out, err = OctezClient("transfer", "100", "from", "alice", "to", tz3alias, "--burn-cap", "0.06425") + assert.NoError(t, err) + require.Contains(t, string(out), "Operation successfully injected in the node") + + //test + /* the tz2 key produces invalid signature 50% of the time from octez-client perspective + out, err = OctezClient("transfer", "1", "from", tz2alias, "to", "alice", "--burn-cap", "0.06425") + assert.NoError(t, err) + require.Contains(t, string(out), "Operation successfully injected in the node") + */ + out, err = OctezClient("transfer", "1", "from", tz3alias, "to", "alice", "--burn-cap", "0.06425") + assert.NoError(t, err) + require.Contains(t, string(out), "Operation successfully injected in the node") +}