Repository: googleapis/google-auth-library-python

Issue:
impersonated_credentials.Credentials.sign_bytes leaks socket
_#### Environment details

  - OS: Ubuntu 20.04
  - Python version: 3.8
  - pip version: 22.2.2
  - `google-auth` version: 2.11.0

#### Steps to reproduce

  1. Create a service account save the email in a environment variable `SA_EMAIL`.
  2. Enable service account impersonation for your user (principal YOUR_EMAIL) with the role `iam.serviceAccountTokenCreator`. i.e:
```bash
gcloud iam service-accounts add-iam-policy-binding \
  --role=roles/iam.serviceAccountTokenCreator \
  --member=serviceAccount:${YOUR_EMAIL} ${SA_EMAIL}
```
  3. Install google-auth and requests into your Python environment
     `pip install google-auth requests`.
  4. Paste the following code into a test Python file (e.g test.py):
```python
import sys

import google.auth  # type: ignore
import google.auth.impersonated_credentials  # type: ignore

import unittest

class TestSignBlob(unittest.TestCase):

  def test_sign_blob(self):
    credentials, _ = google.auth.default()

    service_account_email = 'agentic-local-sa@agenticcorp.iam.gserviceaccount.com'

    signing_credentials = google.auth.impersonated_credentials.Credentials(
      source_credentials=credentials,
      target_principal=service_account_email,
      target_scopes=('https://www.googleapis.com/auth/devstorage.read_only',),
      lifetime=300)
    self.assertNotEqual(signing_credentials.sign_bytes(b'test'), b'')

if __name__ == '__main__':
  unittest.main()
```

  5. Run the test
```shell
python test.py
```

  6. Observe that a socket has been leaked on test tear down:
```
sign_leak.py:20: ResourceWarning: unclosed <ssl.SSLSocket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('172.23.11.59', 53774), raddr=('172.217.164.106', 443)>
  self.assertNotEqual(signing_credentials.sign_bytes(b'test'), b'')
ResourceWarning: Enable tracemalloc to get the object allocation traceback
```

It looks like the bug is here https://github.com/googleapis/google-auth-library-python/blob/bb5c9791c64e2472a90ba7191f79f4c5fedb2538/google/auth/impersonated_credentials.py#L289

The requests session object is created but it's never closed. It _should_ eventually be closed by the GC but really since it's no longer required it should be closed in this method.



Discussion:

Resolving patch:
diff --git a/google/auth/impersonated_credentials.py b/google/auth/impersonated_credentials.py
--- a/google/auth/impersonated_credentials.py
+++ b/google/auth/impersonated_credentials.py
@@ -288,9 +288,12 @@ def sign_bytes(self, message):
 
         authed_session = AuthorizedSession(self._source_credentials)
 
-        response = authed_session.post(
-            url=iam_sign_endpoint, headers=headers, json=body
-        )
+        try:
+            response = authed_session.post(
+                url=iam_sign_endpoint, headers=headers, json=body
+            )
+        finally:
+            authed_session.close()
 
         if response.status_code != http_client.OK:
             raise exceptions.TransportError(


Test patch:
diff --git a/system_tests/secrets.tar.enc b/system_tests/secrets.tar.enc
Binary files a/system_tests/secrets.tar.enc and b/system_tests/secrets.tar.enc differ