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 validations on EC2 enclave pre-init #1159

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

Conversation

abuabraham-ttd
Copy link
Contributor

@abuabraham-ttd abuabraham-ttd commented Nov 18, 2024

UID2- 4364

Looking for feedback on approach; migrating to python from bash to improve validations, readability and error handling.

Follow up tickets will extend this to all Cloud providers, and add custom exceptions that links to public documentation.


def __init__(self):
super().__init__()
self.config = {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Below the member variable being used appears to be self.configs.

token_response = requests.put(token_url, headers={"X-aws-ec2-metadata-token-ttl-seconds": "3600"}, timeout=2)
return token_response.text
except Exception as e:
return "blank"
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 let the exception propagate through? Why is it valid to swallow the error here?

scripts/aws/ec2.py Outdated Show resolved Hide resolved
scripts/aws/ec2.py Outdated Show resolved Hide resolved
raise Exception("Unable to access secret store")

def __add_defaults(self, configs):
configs.setdefault("enclave_memory_mb", 24576)
Copy link
Contributor

Choose a reason for hiding this comment

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

Where are these default coming from? Why are they reasonable?

def cleanup(self):
describe_output = subprocess.check_output(["nitro-cli", "describe-enclaves"], text=True)
enclaves = json.loads(describe_output)
enclave_id = enclaves[0].get("EnclaveID") if enclaves else None
Copy link
Contributor

Choose a reason for hiding this comment

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

This just kills the first enclave from the list without consideration to whether it is the right one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we only run 1 enclave on a host, right?

can add validation if needed

ec2 = EC2()
if args.operation and args.operation == "stop":
ec2.cleanup()
[ec2.kill_process(process) for process in ["vsockpx", "sockd", "vsock-proxy", "nohup"]]
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is nohup in this list? This could potentially kill completely unrelated processes. And why are there both vsockpx and vsock-proxy in the list?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Validates operator key if following new pattern. Ignores otherwise
"""
api_token = secrets.get('api_token', None)
pattern = r"^(UID2|EUID)-.\-(I|P)-\d+-\*$"
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure all operator keys follow this pattern?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If it doesn't; we ignore since it is an old key

api_token = secrets.get('api_token', None)
pattern = r"^(UID2|EUID)-.\-(I|P)-\d+-\*$"
if bool(re.match(pattern, api_token)):
if secrets.get('debug_mode', False) or secrets.get('environment') == 'integ':
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are operator keys forced to integ in debug mode? If debug mode is allowed only for integ, there should be an explicit check for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok. Can update.

Validates core/optout is accessible.
"""
try:
core_ip = socket.gethostbyname(urlparse(config['core_base_url']).netloc)
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

to provide a meaningful error .

f"Failed to reach required URLs. Consider enabling {core_ip}, {optout_ip} in the egress firewall."


except (requests.ConnectionError, requests.Timeout) as e:
raise Exception(
f"Failed to reach required URLs. Consider enabling {core_ip}, {optout_ip} in the egress firewall."
Copy link
Contributor

Choose a reason for hiding this comment

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

is it worth distinguishing here between DNS resolution failure and the actual request?

Copy link
Contributor Author

@abuabraham-ttd abuabraham-ttd Nov 21, 2024

Choose a reason for hiding this comment

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

Egress rules are written based on IP and not DNS. If a company implements strict egress rule (for security) they usually will need these IP's to be enabled.

@@ -80,6 +80,7 @@ else
exit 1
fi

# DO WE NEED THIS? do we expect customers to change URL?
Copy link
Contributor

Choose a reason for hiding this comment

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

I've never heard of it being done before.

def cleanup(self) -> None:
"""Terminates the Nitro Enclave and auxiliary processes."""
try:
describe_output = subprocess.check_output(["nitro-cli", "describe-enclaves"], text=True)
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 just nitro-cli terminate-enclave --all

env = secrets.get("environment", "").lower()
debug_mode = secrets.get("debug_mode", False)
expected_env = "I" if debug_mode or env == "integ" else "P"
if api_token.split("-")[2] != expected_env:
Copy link
Contributor

Choose a reason for hiding this comment

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

also throw error if debug_mode and env == prod? Before attestation failure happens.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, that can be added as another check 👍

self.__run_config_server(log_level)
self.__run_socks_proxy(log_level)

def _validate_auxiliaries(self) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

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

auxiliaries is meaningless - just say Config Server?

for every function that does validation, add some comments to say what you are testing as std out description.

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.

4 participants