IBM Cloud Object Storage (ICOS) のプライベートネットワーク接続する方法

#IBM Cloud Object Storage(ICOS)の認証方法について
###A. IAM トークン認証方式
 - インターネット経由でトークンを発行してプライベートネットワーク経由でアクセスする
 - IAM API key を使ってIAM endpointで1時間有効なトークンを発行し、そのトークンを使ってアクセスする
 - IAM エンドポイントはインターネット経由のみでアクセス可能

###B. HMAC 認証方式
 - access key と secret key からハッシュ関数を使って作成される署名を用いて認証する方式
1. HMAC認証のCredentialを発行する
2. IBM Boto3 を導入するまたは手組みでヘッダーを実装する

詳しくはこちらを参照して下さい。
[IBM Cloud Object Storage (ICOS) の認証方式について調べてみた](https://qiita.com/khayama/items/abe223073f286d9ac16a)

#B-1. HMAC認証のCredentialを発行する
[HMAC 資格情報の使用](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-hmac)

![スクリーンショット 2021-10-19 14.17.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130492/74ad2416-35e4-098f-4c9a-d1ba7f88b90a.png)

![スクリーンショット 2021-10-19 14.17.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130492/5c62fe34-6b3f-e1b6-04dc-403ada7d823a.png)

#B-2. IBM Boto3 (ibm-cos-sdk)を導入する
IBM Boto3とはS3のBoto3のラッパーで、認証などが追加されております。
詳しくは後述しますが、認証周りはごちゃごちゃしております。
試行錯誤のうえ、たどり着いた設定方法をご紹介します。
[IBM Boto3 reference](https://ibm.github.io/ibm-cos-sdk-python/reference/core/ibm_boto3.html)
[ibm-cos-sdk-python](https://github.com/IBM/ibm-cos-sdk-python)

 - ICOS_ENDPOINTをprivateに設定するとHMAC認証となります
 - AWS S3のライブラリーであるBoto3のラッパーであるため変数名はAWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYとなります


```bash:bash
$ pip install ibm-cos-sdk
$ vi .env

コピーしたaccess_key_idとsecret_access_keyをペーストする
ICOS_ENDPOINT = https://s3.private.jp-tok.cloud-object-storage.appdomain.cloud
AWS_ACCESS_KEY_ID="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

#B-2. 補足説明 IBM Boto3 (ibm-cos-sdk)を導入する
[GitのReadme](https://github.com/IBM/ibm-cos-sdk-python)に記載されている環境構築手順は、
 - ホームディレクトリにcos_credentialsという名前でcredentialファイルを作成する
※この方法だとユーザー毎にcredentialファイルを作成する必要があるため気をつけて下さい

```bash:bash
$ mkdir  ~/.bluemix
$ cd  ~/.bluemix/cos_credentials
$ vi  cos_credentials
```

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130492/3903bd36-4891-a2cc-fc26-42a650abac3f.png)

補足説明として、ラッパーの認証でやっていることは、状況に応じたチェックを実施したいようですが、環境によって上手く動かない可能性があります。
私はrootやcronで実行する際にcos_credentialsファイルのパスが参照できなくて試行錯誤することになりました。
とにかく、最終的にセットしたい値であるAWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYを設定するのが最も確実だと思います。
ちなみにラッパーでやっていること、
 - COS_CREDENTIALS_FILEをチェックする
 - AWS_SHARED_CREDENTIALS_FILEをチェックする
https://github.com/IBM/ibm-cos-sdk-python-core/blob/18e8a854a2a4fdca7fbb50d5b80fb666734def1f/ibm_botocore/configprovider.py
 - cos_hmac_keysをチェックする
https://github.com/IBM/ibm-cos-sdk-python-core/blob/18e8a854a2a4fdca7fbb50d5b80fb666734def1f/ibm_botocore/credentials.py

#B-2. 手組みでヘッダーを実装する
IBM Boto3を使わなくてもヘッダーを手組みで実装すればHMAC認証できます
リンク先にはPython、Java、NodeJSのサンプルコードがあります
[authorization ヘッダーの生成](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-hmac-signature#hmac-auth-header-generate-python)

curlの場合はこちらを参考にして下さい
[IBM Cloud Object Storage(ICOS)にcurl+HMACでアクセスする](https://qiita.com/testnin2/items/b7cc4b5f7217395c5f36)

```python:sample.py
import os
import datetime
import hashlib
import hmac
import requests

# please don't store credentials directly in code
access_key = os.environ.get('COS_HMAC_ACCESS_KEY_ID')
secret_key = os.environ.get('COS_HMAC_SECRET_ACCESS_KEY')

# request elements
http_method = 'GET'
host = 's3.us.cloud-object-storage.appdomain.cloud'
region = 'us-standard'
endpoint = 'http://s3.us.cloud-object-storage.appdomain.cloud'
bucket = '' # add a '/' before the bucket name to list buckets
object_key = ''
request_parameters = ''


# hashing and signing methods
def hash(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()

# region is a wildcard value that takes the place of the AWS region value
# as COS doen't use the same conventions for regions, this parameter can accept any string
def createSignatureKey(key, datestamp, region, service):

    keyDate = hash(('AWS4' + key).encode('utf-8'), datestamp)
    keyString = hash(keyDate, region)
    keyService = hash(keyString, service)
    keySigning = hash(keyService, 'aws4_request')
    return keySigning


# assemble the standardized request
time = datetime.datetime.utcnow()
timestamp = time.strftime('%Y%m%dT%H%M%SZ')
datestamp = time.strftime('%Y%m%d')

standardized_resource = bucket + '/' + object_key
standardized_querystring = request_parameters
standardized_headers = 'host:' + host + '\n' + 'x-amz-date:' + timestamp + '\n'
signed_headers = 'host;x-amz-date'
payload_hash = hashlib.sha256(''.encode('utf-8')).hexdigest()

standardized_request = (http_method + '\n' +
                        standardized_resource + '\n' +
                        standardized_querystring + '\n' +
                        standardized_headers + '\n' +
                        signed_headers + '\n' +
                        payload_hash).encode('utf-8')


# assemble string-to-sign
hashing_algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + 's3' + '/' + 'aws4_request'
sts = (hashing_algorithm + '\n' +
       timestamp + '\n' +
       credential_scope + '\n' +
       hashlib.sha256(standardized_request).hexdigest())


# generate the signature
signature_key = createSignatureKey(secret_key, datestamp, region, 's3')
signature = hmac.new(signature_key,
                     (sts).encode('utf-8'),
                     hashlib.sha256).hexdigest()


# assemble all elements into the 'authorization' header
v4auth_header = (hashing_algorithm + ' ' +
                 'Credential=' + access_key + '/' + credential_scope + ', ' +
                 'SignedHeaders=' + signed_headers + ', ' +
                 'Signature=' + signature)


# create and send the request
headers = {'x-amz-date': timestamp, 'Authorization': v4auth_header}
# the 'requests' package autmatically adds the required 'host' header
request_url = endpoint + standardized_resource + standardized_querystring

print('\nSending `%s` request to IBM COS -----------------------' % http_method)
print('Request URL = ' + request_url)
request = requests.get(request_url, headers=headers)

print('\nResponse from IBM COS ----------------------------------')
print('Response code: %d\n' % request.status_code)
print(request.text)
```