Skip to content

Commit 438b7a4

Browse files
authored
Merge pull request #89 from oracle/release_2018-11-29
Releasing version 2.1.2
2 parents 70928bd + f3fd7ca commit 438b7a4

16 files changed

+355
-27
lines changed

CHANGELOG.rst

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on `Keep a Changelog <http://keepachangelog.com/>`_.
66

7+
====================
8+
2.1.2 - 2018-11-29
9+
====================
10+
11+
Added
12+
-----
13+
* Support for getting bucket statistics in the Object Storage service
14+
* Support for using FIPS compliant libcrypto library
15+
16+
Fixed
17+
-----
18+
* Block Storage service for copying volume backups across regions is now enabled
19+
20+
721
====================
822
2.1.1 - 2018-11-15
923
====================
@@ -49,10 +63,6 @@ Changed
4963
-------
5064
* database_edition field in Backup and model changed from a free format string to a validated string. It will only accept one of the following: “STANDARD_EDITION”, “ENTERPRISE_EDITION”, “ENTERPRISE_EDITION_HIGH_PERFORMANCE”, “ENTERPRISE_EDITION_EXTREME_PERFORMANCE”
5165

52-
Known issue
53-
-----------
54-
* Block Storage service for copying volume backups across regions is not enabled
55-
5666
Breaking
5767
--------
5868
* db_data_size_in_mbs field in Backup and BackupSummary models renamed to database_size_in_g_bs. The type changed from int to float.

docs/fips-libraries.rst

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
.. _fips-libraries:
2+
3+
.. raw:: html
4+
5+
<script type='text/javascript'>
6+
var oldDocsHost = 'oracle-bare-metal-cloud-services-python-sdk';
7+
if (window.location.href.indexOf(oldDocsHost) != -1) {
8+
window.location.href = 'https://oracle-bare-metal-cloud-services-python-sdk.readthedocs.io/en/latest/deprecation-notice.html';
9+
}
10+
</script>
11+
12+
Using FIPS-validated Libraries
13+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14+
15+
The SDK can be configured to use FIPS-validated libcrypto library. You can set it programmatically on a per session basis or persistently across the environment. Both approaches require the path to the libcrypto library on your system.
16+
17+
Enabling FIPS Mode Programmatically
18+
------------------------------------
19+
20+
To configure the SDK to use a FIPS-validated libcrypto library, execute the following:
21+
22+
.. code-block:: python
23+
24+
oci.fips.enable_fips_mode('</path/to/libcrypto.x.x.x>')
25+
26+
Setting the Environment Variables
27+
---------------------------------
28+
29+
If you do not want to run ``enable_fips_mode()`` for every session, you can set an environment variable so that the SDK uses the library every time.
30+
31+
Set the following environment variable to the path to the libcrypto library:
32+
33+
- FIPS_LIBCRYPTO_PATH
34+
35+
Verifying the Configuration
36+
---------------------------
37+
38+
To verify that the SDK is using the libcrypto library that you specified, execute the following:
39+
40+
.. code-block:: python
41+
42+
oci.fips.is_fips_mode()
43+
44+
This should return True, indicating that the SDK is using the library specified by the environment variable.

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ To get started, head over to the :ref:`installation instructions <install>` or s
4444

4545
installation
4646
configuration
47+
fips-libraries
4748
forward-compatibility
4849
backward-compatibility
4950
quickstart

docs/installation.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,15 @@ This command instructs the `requests <https://pypi.python.org/pypi/requests>`_
119119
library used by the Python SDK to use the version of OpenSSL that is bundled with the `cryptography <https://pypi.python.org/pypi/cryptography>`_
120120
library used by the SDK.
121121

122-
**Note:**
123122
If you don't want to use ``requests[security]`` you can update OpenSSL as you normally would. For example, on OS X, use Homebrew to update OpenSSL using the following commands::
124123

125124
brew update
126125
brew install openssl
127126
brew install python
128127

128+
.. note::
129+
If you need to configure your environment for FIPS-compliance, see :doc:`fips-libraries`
130+
129131
=================
130132
Troubleshooting
131133
=================

src/oci/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22
# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
33

44
from . import audit, container_engine, core, database, dns, email, file_storage, identity, key_management, load_balancer, object_storage, resource_search
5-
from . import auth, config, constants, decorators, exceptions, regions, pagination, retry
5+
from . import auth, config, constants, decorators, exceptions, regions, pagination, retry, fips
66
from .base_client import BaseClient
77
from .request import Request
88
from .response import Response
99
from .signer import Signer
1010
from .version import __version__ # noqa
1111
from .waiter import wait_until
1212

13+
fips.enable_fips_mode()
1314

1415
__all__ = [
15-
"BaseClient", "Error", "Request", "Response", "Signer", "config", "constants", "decorators", "exceptions", "regions", "wait_until", "pagination", "auth", "retry",
16+
"BaseClient", "Error", "Request", "Response", "Signer", "config", "constants", "decorators", "exceptions", "regions", "wait_until", "pagination", "auth", "retry", "fips",
1617
"audit", "container_engine", "core", "database", "dns", "email", "file_storage", "identity", "key_management", "load_balancer", "object_storage", "resource_search"
1718
]

src/oci/auth/auth_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ def get_tenancy_id_from_certificate(cert):
1212
val = name_attribute.value
1313
if val.startswith('opc-tenant:'):
1414
return val[len('opc-tenant:'):]
15+
if val.startswith('opc-identity:'):
16+
return val[len('opc-identity:'):]
1517

1618
raise RuntimeError('The certificate does not contain a tenancy OCID')
1719

src/oci/auth/certificate_retriever.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def __init__(self, **kwargs):
103103
self.passphrase = self.passphrase.encode('ascii')
104104

105105
self._refresh_lock = threading.Lock()
106+
self.requests_session = requests.Session()
106107

107108
self.refresh()
108109

@@ -166,7 +167,7 @@ def _refresh_inner(self):
166167
import oci.signer
167168

168169
downloaded_certificate = six.BytesIO()
169-
response = requests.get(self.cert_url, stream=True)
170+
response = self.requests_session.get(self.cert_url, stream=True, timeout=(10, 60))
170171

171172
response.raise_for_status()
172173

@@ -182,7 +183,7 @@ def _refresh_inner(self):
182183

183184
if self.private_key_url:
184185
downloaded_private_key_raw = six.BytesIO()
185-
response = requests.get(self.private_key_url, stream=True)
186+
response = self.requests_session.get(self.private_key_url, stream=True, timeout=(10, 60))
186187

187188
response.raise_for_status()
188189

@@ -242,7 +243,7 @@ class PEMStringCertificateRetriever(AbstractCertificateRetriever):
242243
def __init__(self, **kwargs):
243244
import oci.signer
244245

245-
super(UrlBasedCertificateRetriever, self).__init__()
246+
super(PEMStringCertificateRetriever, self).__init__()
246247

247248
if 'certificate_pem' not in kwargs:
248249
raise TypeError('certificate_pem must be supplied as a keyword argument')
@@ -317,7 +318,7 @@ def __init__(self, **kwargs):
317318

318319
parent_class_kwargs = {
319320
'certificate_pem': self._load_data_from_file(kwargs['certificate_file_path']),
320-
'private_key_pem_file_path': private_key_pem,
321+
'private_key_pem': private_key_pem,
321322
'passphrase': kwargs.get('passphrase')
322323
}
323324

src/oci/auth/federation_client.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ def __init__(self, **kwargs):
9797
else:
9898
self.retry_strategy = oci.retry.DEFAULT_RETRY_STRATEGY
9999

100+
self.requests_session = requests.Session()
101+
100102
def refresh_security_token(self):
101103
return self._refresh_security_token_inner()
102104

@@ -142,9 +144,9 @@ def _get_security_token_from_auth_service(self):
142144
signer = AuthTokenRequestSigner(self.tenancy_id, fingerprint, self.leaf_certificate_retriever)
143145

144146
if self.cert_bundle_verify:
145-
response = requests.post(self.federation_endpoint, json=request_payload, auth=signer, verify=self.cert_bundle_verify)
147+
response = self.requests_session.post(self.federation_endpoint, json=request_payload, auth=signer, verify=self.cert_bundle_verify, timeout=(10, 60))
146148
else:
147-
response = requests.post(self.federation_endpoint, json=request_payload, auth=signer)
149+
response = self.requests_session.post(self.federation_endpoint, json=request_payload, auth=signer, timeout=(10, 60))
148150

149151
parsed_response = None
150152
try:

src/oci/auth/signers/instance_principals_security_token_signer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def initialize_and_return_region(self):
9090
if hasattr(self, 'region'):
9191
return self.region
9292

93-
response = requests.get(self.GET_REGION_URL)
93+
response = requests.get(self.GET_REGION_URL, timeout=(10, 60))
9494
region_raw = response.text.strip().lower()
9595

9696
# The region can be something like "phx" but internally we expect "us-phoenix-1", "us-ashburn-1" etc.

src/oci/fips.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# coding: utf-8
2+
# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
3+
4+
import sys
5+
import ctypes
6+
import logging
7+
import os
8+
9+
10+
class DevNull:
11+
"""
12+
Simple class to supress errors which may occur when importing hashlib
13+
in FIPS mode.
14+
"""
15+
def write(self, msg):
16+
pass
17+
18+
19+
def override_libcrypto(fips_libcrypto_path):
20+
"""
21+
Override libcrypto and add FIPS_mode function to ssl if it is not there
22+
"""
23+
24+
_bs_crypto = ctypes.CDLL(fips_libcrypto_path)
25+
_bs_crypto.FIPS_mode_set(ctypes.c_int(1))
26+
import ssl # noqa: E402
27+
if not hasattr(ssl, 'FIPS_mode'):
28+
ssl.FIPS_mode = _bs_crypto.FIPS_mode
29+
30+
31+
def md5(intitial_message=''):
32+
"""
33+
Placeholder md5 function for hashlib so it won't segfault when called after
34+
enabling FIPS mode.
35+
"""
36+
raise ValueError("md5 disabled for fips")
37+
return None
38+
39+
40+
def patch_hashlib_md5():
41+
"""
42+
hashlib.md5 is imported by urllib3, which is required by requests,
43+
which is used by oci (python sdk). This will cause errors so we need to
44+
patch hashlib.
45+
"""
46+
47+
stderr = sys.stderr
48+
try:
49+
sys.stderr = DevNull()
50+
import hashlib # noqa: E402
51+
except (RuntimeError, ValueError):
52+
pass
53+
sys.stderr = stderr
54+
hashlib.md5 = md5
55+
56+
57+
def is_fips_mode():
58+
"""
59+
Verify that ssl.FIPS_mode() returns 1 and that using md5 raises an
60+
exception
61+
"""
62+
63+
import hashlib
64+
import ssl
65+
66+
if not hasattr(ssl, 'FIPS_mode'):
67+
return False
68+
elif ssl.FIPS_mode() != 1:
69+
return False
70+
71+
try:
72+
digest = hashlib.md5(b"Hello World\n").hexdigest() # noqa: F841
73+
return False
74+
except ValueError:
75+
# Expect to get this exception so do nothing
76+
pass
77+
78+
return True
79+
80+
81+
def enable_fips_mode(fips_libcrypto_path=None):
82+
"""
83+
Enable FIPS mode by overriding libcrypto and patching hashlib
84+
"""
85+
86+
logger = logging.getLogger("{}.{}".format(__name__, id(enable_fips_mode)))
87+
logger.addHandler(logging.NullHandler())
88+
89+
if not fips_libcrypto_path:
90+
if 'FIPS_LIBCRYPTO_PATH' in os.environ:
91+
fips_libcrypto_path = os.environ['FIPS_LIBCRYPTO_PATH']
92+
93+
if fips_libcrypto_path:
94+
override_libcrypto(fips_libcrypto_path)
95+
96+
import hashlib
97+
try:
98+
digest = hashlib.md5(b"Hello World\n").hexdigest() # noqa: F841
99+
100+
# If the previous line did not raise an exception md5 needs to be
101+
# patched
102+
patch_hashlib_md5()
103+
104+
except ValueError:
105+
# Expect to get this exception so do nothing
106+
pass
107+
108+
logger.info("Using '{}' for libcypto".format(fips_libcrypto_path))
109+
if is_fips_mode():
110+
logger.info("FIPS mode is active")
111+
else:
112+
logger.error("Failed to enter FIPS mode")

0 commit comments

Comments
 (0)