Skip to content

Commit 22d241b

Browse files
authored
feat: Add smbios check to detect GCE residency (#1276)
* feat: smbios check for GCE residency * Refresh test token
1 parent c708522 commit 22d241b

File tree

8 files changed

+95
-10
lines changed

8 files changed

+95
-10
lines changed

google/auth/_default.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ def _get_gce_credentials(request=None, quota_project_id=None):
281281
if request is None:
282282
request = google.auth.transport._http_client.Request()
283283

284-
if _metadata.ping(request=request):
284+
if _metadata.is_on_gce(request=request):
285285
# Get the project ID.
286286
try:
287287
project_id = _metadata.get_project_id(request=request)

google/auth/compute_engine/_metadata.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,47 @@
5959
except ValueError: # pragma: NO COVER
6060
_METADATA_DEFAULT_TIMEOUT = 3
6161

62+
# Detect GCE Residency
63+
_GOOGLE = "Google"
64+
_GCE_PRODUCT_NAME_FILE = "/sys/class/dmi/id/product_name"
65+
66+
67+
def is_on_gce(request):
68+
"""Checks to see if the code runs on Google Compute Engine
69+
70+
Args:
71+
request (google.auth.transport.Request): A callable used to make
72+
HTTP requests.
73+
74+
Returns:
75+
bool: True if the code runs on Google Compute Engine, False otherwise.
76+
"""
77+
if ping(request):
78+
return True
79+
80+
if os.name == "nt":
81+
# TODO: implement GCE residency detection on Windows
82+
return False
83+
84+
# Detect GCE residency on Linux
85+
return detect_gce_residency_linux()
86+
87+
88+
def detect_gce_residency_linux():
89+
"""Detect Google Compute Engine residency by smbios check on Linux
90+
91+
Returns:
92+
bool: True if the GCE product name file is detected, False otherwise.
93+
"""
94+
try:
95+
with open(_GCE_PRODUCT_NAME_FILE, "r") as file_obj:
96+
content = file_obj.read().strip()
97+
98+
except Exception:
99+
return False
100+
101+
return content.startswith(_GOOGLE)
102+
62103

63104
def ping(request, timeout=_METADATA_DEFAULT_TIMEOUT, retry_count=3):
64105
"""Checks to see if the metadata server is available.

system_tests/secrets.tar.enc

0 Bytes
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Google Compute Engine
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ABC Compute Engine

tests/compute_engine/test__metadata.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@
2929

3030
PATH = "instance/service-accounts/default"
3131

32+
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
33+
SMBIOS_PRODUCT_NAME_FILE = os.path.join(DATA_DIR, "smbios_product_name")
34+
SMBIOS_PRODUCT_NAME_NONEXISTENT_FILE = os.path.join(
35+
DATA_DIR, "smbios_product_name_nonexistent"
36+
)
37+
SMBIOS_PRODUCT_NAME_NON_GOOGLE = os.path.join(
38+
DATA_DIR, "smbios_product_name_non_google"
39+
)
40+
3241

3342
def make_request(data, status=http_client.OK, headers=None, retry=False):
3443
response = mock.create_autospec(transport.Response, instance=True)
@@ -45,6 +54,39 @@ def make_request(data, status=http_client.OK, headers=None, retry=False):
4554
return request
4655

4756

57+
def test_detect_gce_residency_linux_success():
58+
_metadata._GCE_PRODUCT_NAME_FILE = SMBIOS_PRODUCT_NAME_FILE
59+
assert _metadata.detect_gce_residency_linux()
60+
61+
62+
def test_detect_gce_residency_linux_non_google():
63+
_metadata._GCE_PRODUCT_NAME_FILE = SMBIOS_PRODUCT_NAME_NON_GOOGLE
64+
assert not _metadata.detect_gce_residency_linux()
65+
66+
67+
def test_detect_gce_residency_linux_nonexistent():
68+
_metadata._GCE_PRODUCT_NAME_FILE = SMBIOS_PRODUCT_NAME_NONEXISTENT_FILE
69+
assert not _metadata.detect_gce_residency_linux()
70+
71+
72+
def test_is_on_gce_ping_success():
73+
request = make_request("", headers=_metadata._METADATA_HEADERS)
74+
assert _metadata.is_on_gce(request)
75+
76+
77+
@mock.patch("os.name", new="nt")
78+
def test_is_on_gce_windows_success():
79+
request = make_request("", headers={_metadata._METADATA_FLAVOR_HEADER: "meep"})
80+
assert not _metadata.is_on_gce(request)
81+
82+
83+
@mock.patch("os.name", new="posix")
84+
def test_is_on_gce_linux_success():
85+
request = make_request("", headers={_metadata._METADATA_FLAVOR_HEADER: "meep"})
86+
_metadata._GCE_PRODUCT_NAME_FILE = SMBIOS_PRODUCT_NAME_FILE
87+
assert _metadata.is_on_gce(request)
88+
89+
4890
def test_ping_success():
4991
request = make_request("", headers=_metadata._METADATA_HEADERS)
5092

tests/test__default.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,7 @@ def test__get_gae_credentials_no_apis():
781781

782782

783783
@mock.patch(
784-
"google.auth.compute_engine._metadata.ping", return_value=True, autospec=True
784+
"google.auth.compute_engine._metadata.is_on_gce", return_value=True, autospec=True
785785
)
786786
@mock.patch(
787787
"google.auth.compute_engine._metadata.get_project_id",
@@ -796,7 +796,7 @@ def test__get_gce_credentials(unused_get, unused_ping):
796796

797797

798798
@mock.patch(
799-
"google.auth.compute_engine._metadata.ping", return_value=False, autospec=True
799+
"google.auth.compute_engine._metadata.is_on_gce", return_value=False, autospec=True
800800
)
801801
def test__get_gce_credentials_no_ping(unused_ping):
802802
credentials, project_id = _default._get_gce_credentials()
@@ -806,7 +806,7 @@ def test__get_gce_credentials_no_ping(unused_ping):
806806

807807

808808
@mock.patch(
809-
"google.auth.compute_engine._metadata.ping", return_value=True, autospec=True
809+
"google.auth.compute_engine._metadata.is_on_gce", return_value=True, autospec=True
810810
)
811811
@mock.patch(
812812
"google.auth.compute_engine._metadata.get_project_id",
@@ -831,7 +831,7 @@ def test__get_gce_credentials_no_compute_engine():
831831

832832

833833
@mock.patch(
834-
"google.auth.compute_engine._metadata.ping", return_value=False, autospec=True
834+
"google.auth.compute_engine._metadata.is_on_gce", return_value=False, autospec=True
835835
)
836836
def test__get_gce_credentials_explicit_request(ping):
837837
_default._get_gce_credentials(mock.sentinel.request)
@@ -1246,7 +1246,7 @@ def test_quota_project_from_environment(get_adc_path):
12461246

12471247

12481248
@mock.patch(
1249-
"google.auth.compute_engine._metadata.ping", return_value=True, autospec=True
1249+
"google.auth.compute_engine._metadata.is_on_gce", return_value=True, autospec=True
12501250
)
12511251
@mock.patch(
12521252
"google.auth.compute_engine._metadata.get_project_id",

tests_async/test__default_async.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ def test__get_gae_credentials_no_apis():
366366

367367

368368
@mock.patch(
369-
"google.auth.compute_engine._metadata.ping", return_value=True, autospec=True
369+
"google.auth.compute_engine._metadata.is_on_gce", return_value=True, autospec=True
370370
)
371371
@mock.patch(
372372
"google.auth.compute_engine._metadata.get_project_id",
@@ -381,7 +381,7 @@ def test__get_gce_credentials(unused_get, unused_ping):
381381

382382

383383
@mock.patch(
384-
"google.auth.compute_engine._metadata.ping", return_value=False, autospec=True
384+
"google.auth.compute_engine._metadata.is_on_gce", return_value=False, autospec=True
385385
)
386386
def test__get_gce_credentials_no_ping(unused_ping):
387387
credentials, project_id = _default._get_gce_credentials()
@@ -391,7 +391,7 @@ def test__get_gce_credentials_no_ping(unused_ping):
391391

392392

393393
@mock.patch(
394-
"google.auth.compute_engine._metadata.ping", return_value=True, autospec=True
394+
"google.auth.compute_engine._metadata.is_on_gce", return_value=True, autospec=True
395395
)
396396
@mock.patch(
397397
"google.auth.compute_engine._metadata.get_project_id",
@@ -416,7 +416,7 @@ def test__get_gce_credentials_no_compute_engine():
416416

417417

418418
@mock.patch(
419-
"google.auth.compute_engine._metadata.ping", return_value=False, autospec=True
419+
"google.auth.compute_engine._metadata.is_on_gce", return_value=False, autospec=True
420420
)
421421
def test__get_gce_credentials_explicit_request(ping):
422422
_default._get_gce_credentials(mock.sentinel.request)

0 commit comments

Comments
 (0)