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

add GCP credentials deployment #2118

Merged
merged 12 commits into from
May 19, 2024
Merged

add GCP credentials deployment #2118

merged 12 commits into from
May 19, 2024

Conversation

orouz
Copy link
Collaborator

@orouz orouz commented Apr 10, 2024

  • adds a Deployment Manager template to deploy a new service account and key

  • adds a CI test to verify deployment of service account is useable

  • this is how a successful deployment looks from a user POV after they ran the service account deployment script in a GCP shell:
    Screenshot 2024-04-15 at 18 25 05

  • tested we get findings using the generated key by providing it as a JSON blob credentials option on the manual deployment option:
    Screenshot 2024-05-08 at 15 09 18

  • closes [GCP] Automated credentials creation flow for agentless #2005

Copy link

mergify bot commented Apr 10, 2024

This pull request does not have a backport label. Could you fix it @orouz? 🙏
To fixup this pull request, you need to add the backport labels for the needed
branches, such as:

  • backport-v./d./d./d is the label to automatically backport to the 8./d branch. /d is the digit
    NOTE: backport-skip has been added to this pull request.

@mergify mergify bot mentioned this pull request Apr 10, 2024
1 task
Copy link

github-actions bot commented Apr 10, 2024

📊 Allure Report - 💚 No failures were reported.

Result Count
🟥 Failed 0
🟩 Passed 359
⬜ Skipped 33

@orouz orouz changed the title [test] add GCP credentials deployment add GCP credentials deployment Apr 15, 2024
@orouz orouz force-pushed the gcp_agentless_creds branch 3 times, most recently from 773ec4b to 3a94c87 Compare May 12, 2024 09:12
Copy link

mergify bot commented May 12, 2024

This pull request is now in conflicts. Could you fix it? 🙏
To fixup this pull request, you can check out it locally. See documentation: https://help.github.com/articles/checking-out-pull-requests-locally/

git fetch upstream
git checkout -b gcp_agentless_creds upstream/gcp_agentless_creds
git merge upstream/main
git push upstream gcp_agentless_creds

Comment on lines +24 to +26
# Test a GCP Deployment Manager deployment using Application Default Credentials
gcp_dm_adc:
name: CSPM GCP with ADC
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this job tests the code path that uses GCP's application default credentials

return p.getApplicationDefaultCredentials(ctx, cfg, log)

Comment on lines +130 to +131
# Test a GCP Deployment Manager deployment using a Service Account
gcp_dm_sa:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this job tests the code path that uses a service account json

return p.getCustomCredentials(ctx, cfg, log)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

there is some repetitiveness in this workflow as the two jobs do mostly the same thing. we could abstract some of the steps to an action (like actions/init_env, actions/check_findings, actions/deploy_ec)

Comment on lines +201 to +218
run: |
# Deploys a GCP Service Account
cd ${{ env.DEPLOYMENT_MANAGER_DIR }}
export DEPLOYMENT_NAME="${{ env.GCP_SA_DEPLOYMENT_NAME }}"
export SERVICE_ACCOUNT_NAME="${{ env.GCP_SA_DEPLOYMENT_NAME }}-sa"
./deploy_service_account.sh
mv KEY_FILE.json ../../${{ env.INTEGRATIONS_SETUP_DIR }}

# Installs CSPM GCP integration
cd ../../${{ env.INTEGRATIONS_SETUP_DIR }}
export SERVICE_ACCOUNT_JSON_PATH="KEY_FILE.json"
export DEPLOYMENT_NAME="${{ env.GCP_AGENT_DEPLOYMENT_NAME }}"
poetry install
poetry run python ./install_cspm_gcp_integration.py

# Deploys the agent using an existing service account (SERVICE_ACCOUNT_NAME)
cd ../../${{ env.DEPLOYMENT_MANAGER_DIR }}
. ./set_env.sh && ./deploy.sh
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

there's some trickery going on here:

  1. we first deploy a service account using the new deployment manager template, which generates a KEY_FILE.json with the said service account
  2. we then install a CSPM GCP integration that uses gcp.credentials.json variable to set the JSON content
  3. then we deploy the agent, but because we've defined a service account name, it won't deploy a service account and instead use the one we deployed earlier

this is all combined into a single step because otherwise we won't be able to share the JSON file

@@ -52,7 +51,7 @@ def generate_config(context):
),
"serviceAccounts": [
{
"email": f"$(ref.{sa_name}.email)",
"email": get_service_account_email(sa_name, project),
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

we can't use ref because we might be using an already-deployed service account

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is mostly copied from the original ./deploy.sh script

Copy link
Contributor

Choose a reason for hiding this comment

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

why not create a common.sh file?

Comment on lines +12 to +13
# pylint: disable=duplicate-code
service_account = {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it'd be nice to define a ./common.py file and import it to both deployment templates. not sure why that didn't work tbh

Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't manage to do it as well.
It's because we're using deployment manager templates and other py files can not be imported to the file.

@orouz orouz marked this pull request as ready for review May 12, 2024 10:32
@orouz orouz requested a review from a team as a code owner May 12, 2024 10:32
@orouz orouz requested a review from uri-weisman May 12, 2024 10:32
Copy link
Contributor

Choose a reason for hiding this comment

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

why not create a common.sh file?

Comment on lines +12 to +13
# pylint: disable=duplicate-code
service_account = {
Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't manage to do it as well.
It's because we're using deployment manager templates and other py files can not be imported to the file.

@@ -115,7 +116,7 @@ fi
# Apply the deployment manager templates
run_command "gcloud deployment-manager deployments create --automatic-rollback-on-error ${DEPLOYMENT_NAME} --project ${PROJECT_NAME} \
--template compute_engine.py \
--properties elasticAgentVersion:${STACK_VERSION},fleetUrl:${FLEET_URL},enrollmentToken:${ENROLLMENT_TOKEN},allowSSH:${ALLOW_SSH},zone:${ZONE},elasticArtifactServer:${ELASTIC_ARTIFACT_SERVER},scope:${SCOPE},parentId:${PARENT_ID}"
--properties elasticAgentVersion:${STACK_VERSION},fleetUrl:${FLEET_URL},enrollmentToken:${ENROLLMENT_TOKEN},allowSSH:${ALLOW_SSH},zone:${ZONE},elasticArtifactServer:${ELASTIC_ARTIFACT_SERVER},scope:${SCOPE},parentId:${PARENT_ID},serviceAccountName:${SERVICE_ACCOUNT_NAME}"

Copy link
Contributor

Choose a reason for hiding this comment

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

Will the SERVICE_ACCOUNT_NAME param be propagated from the UI and if not we'll create one?
In case the user provides a wrong SA name, the deployment will not succeed and an error will be printed to the user.
Are we ok with it? do we want to add validation that the SA exists and if not to create one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Will the SERVICE_ACCOUNT_NAME param be propagated from the UI and if not we'll create one?

no, SERVICE_ACCOUNT_NAME is not user-facing.

the addition of SERVICE_ACCOUNT_NAME to the deploy.sh script is done purely for testing purposes of deploying an agent with an existing service account. it simulates the new agentless flow, where a user will deploy a service account and we'd use its key in the integration credentials input.

to test the deployment of a service account is working properly, i've used the existing agent deployment script to make it so that it can operate either by deploying the full resources: VM + SA, or just a VM, while using an existing SA, which is currently passed only during tests.

In case the user provides a wrong SA name, the deployment will not succeed and an error will be printed to the user. Are we ok with it? do we want to add validation that the SA exists and if not to create one?

as mentioned, SERVICE_ACCOUNT_NAME is only used in tests, so we don't want to deploy a new service account if SERVICE_ACCOUNT_NAME doesn't exists. if for some reason SERVICE_ACCOUNT_NAME is provided but doesn't exists, failing the deployment would be the right outcome.

Comment on lines 51 to 62
def read_json_file(file_path):
"""Reads a json file and returns its content"""
try:
with open(file_path, "r") as json_file:
return json_file.read()
except FileNotFoundError:
logger.error(f"Error: File '{file_path}' not found.")
sys.exit(1)
except IOError as e:
logger.error(f"Error reading file '{file_path}': {e}")
sys.exit(1)

Copy link
Collaborator

Choose a reason for hiding this comment

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

@orouz, I think there's a bit of confusion here. The function read_json_file() and the variable json_file might suggest that they specifically handle JSON files, but they're actually generic and can read any type of file. For reading JSON files, we have a separate function defined here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@gurevichdmitry i would prefer using read_json but it returns an empty dict when it fails (also missing decode exception) and i don't want to check for an empty dict in my use case and then exit, as it's kinda dirty. i'd prefer updating read_json to remove the try and let consumers do it and act on errors. in my case, i would sys.exit(1) if reading the file failed. can we do the same in load_data() ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@orouz considering that read_json is only used in load_data, we can modify read_json to exit on error. This approach fulfills your requirement, and the reuse will be valid in your case.

Copy link
Contributor

@uri-weisman uri-weisman left a comment

Choose a reason for hiding this comment

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

Approving deployment manager files

Copy link
Collaborator

@gurevichdmitry gurevichdmitry left a comment

Choose a reason for hiding this comment

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

@orouz Workflow and tests are approved. One more question: did you test the environment destruction when the service account is used?

@orouz
Copy link
Collaborator Author

orouz commented May 19, 2024

@orouz Workflow and tests are approved. One more question: did you test the environment destruction when the service account is used?

yes, deletion of GCP env works

@orouz orouz merged commit a2be352 into main May 19, 2024
26 checks passed
@orouz orouz deleted the gcp_agentless_creds branch May 19, 2024 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[GCP] Automated credentials creation flow for agentless
3 participants