如何使用 Amazon RDS Oracle 钱包中绑定的 CA 证书连接到 SSL/TLS 终端节点?

8 分钟阅读
0

我有一个运行 Oracle 的 Amazon Relational Database Service(Amazon RDS)数据库实例。我想使用 Oracle 钱包从我的数据库实例安全地与外部 SSL/TLS 终端节点进行通信。

简短描述

您可以使用 UTL_HTTP 等实用程序从 Amazon RDS Oracle 数据库实例连接到远程 Web 服务器终端节点。要安全地执行此操作,您可以使用 Oracle 钱包。Oracle 钱包是一种容器,用于存储访问 Web 服务的 SSL/TLS 终端节点所需的根证书和中间证书。

解决方法

使用 Oracle 钱包从实例连接到外部 SSL/TLS 终端节点

1.    在 Web 浏览器中,打开要访问的 URL(Web 服务的 SSL/TLS 终端节点)。

2.    在浏览器的地址栏单击挂锁符号,查看证书详细信息。您还可以在本地工作站的命令行上运行与以下类似的命令,从而查看证书详细信息。

$ openssl s_client -connect status.aws.amazon.com:443
CONNECTED(00000004)
depth=2 C = US, O = Amazon, CN = Amazon Root CA 1
verify return:1
depth=1 C = US, O = Amazon, OU = Server CA 1B, CN = Amazon
verify return:1
depth=0 CN = status.aws.amazon.com
verify return:1
---
Certificate chain
 0 s:CN = status.aws.amazon.com
   i:C = US, O = Amazon, OU = Server CA 1B, CN = Amazon
 1 s:C = US, O = Amazon, OU = Server CA 1B, CN = Amazon
   i:C = US, O = Amazon, CN = Amazon Root CA 1
 2 s:C = US, O = Amazon, CN = Amazon Root CA 1
   i:C = US, ST = Arizona, L = Scottsdale, O = "Starfield Technologies, Inc.", CN = Starfield Services Root Certificate Authority - G2
 3 s:C = US, ST = Arizona, L = Scottsdale, O = "Starfield Technologies, Inc.", CN = Starfield Services Root Certificate Authority - G2
   i:C = US, O = "Starfield Technologies, Inc.", OU = Starfield Class 2 Certification Authority
---

注意:请务必将 status.aws.amazon.com 替换您选择的 URL。

3.    从证书提供商下载所列的相关根证书和中间证书。从堆栈跟踪中可以看出,您需要证书 Amazon Root CA 1Starfield Services Root Certificate Authority - G2。您可以从 Amazon trust services 存储库下载证书。如果证书以 pem 格式提供,则无需进一步操作。

4.    如果证书无法以 pem 格式下载,请下载 DER/CRT 格式的证书。然后在本地工作站的命令行上运行与以下类似的命令,从而将下载的证书转换为 pem 格式:

$ openssl x509 -inform der -in AmazonRootCA1.cer -outform pem -out AmazonRootCA1.pem
$ openssl x509 -inform der -in SFSRootCAG2.cer -outform pem -out SFSRootCAG2.pem

5.    使用 orapki 实用程序创建钱包。安装相关的 Oracle 客户端软件后,orapki 实用程序将会可用。钱包必须在源数据库环境中创建。Oracle JDeveloper Studio 版本也包含 orapki 实用程序。创建钱包时可以选择不同的参数。例如,您可以使用参数 auto_login_only 创建不需要密码即可打开的自动登录钱包(cwallet.sso)。您可以在不使用密码的情况下修改或删除钱包。文件系统权限为自动登录钱包提供了必要的安全性。有关创建钱包时的其他可用选项的更多信息,请参阅 Oracle 文档使用 orapki 实用程序管理 Oracle 钱包。浏览至要在其中创建钱包的目录。然后在本地工作站的命令行上运行与以下类似的命令。

>cd /app/client/wallet
>orapki wallet create -wallet . -auto_login_only

6.    在本地工作站的命令行上运行与以下类似的命令,将这两个证书添加到钱包中:

>orapki wallet add -wallet . -trusted_cert -cert AmazonRootCA1.pem -auto_login_only
>orapki wallet add -wallet . -trusted_cert -cert SFSRootCAG2.pem -auto_login_only

7.    在本地工作站的命令行上运行下面的命令,从而列出钱包的内容。验证内容以确保已添加了证书。

>ls -ltrh cwallet.sso
-rw------- 1 user1 Domain Users 2.4K Apr 29 2020 cwallet.sso

>orapki wallet display -wallet .
Oracle PKI Tool Release 18.0.0.0.0 - Production
Version 18.1.0.0.0
Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.

Requested Certificates:
User Certificates:
Trusted Certificates:
Subject:        CN=Amazon Root CA 1,O=Amazon,C=US
Subject:        CN=Starfield Services Root Certificate Authority - G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US

8.    以 SQL*Plus 主用户身份连接到 Amazon RDS Oracle 数据库实例。通过运行与以下类似的命令,向将使用 UTL_HTTP 程序包的数据库用户授予所需的权限:

SQL> define user='app_user';
SQL>  BEGIN rdsadmin.rdsadmin_util.grant_sys_object('DBA_DIRECTORIES', UPPER('&user')); END;
  2  /
old   1:  BEGIN rdsadmin.rdsadmin_util.grant_sys_object('DBA_DIRECTORIES', UPPER('&user')); END;
new   1:  BEGIN rdsadmin.rdsadmin_util.grant_sys_object('DBA_DIRECTORIES', UPPER('app_user')); END;

PL/SQL procedure successfully completed.

SQL> BEGIN rdsadmin.rdsadmin_util.grant_sys_object('UTL_HTTP', UPPER('&user')); END;
  2  /
old   1: BEGIN rdsadmin.rdsadmin_util.grant_sys_object('UTL_HTTP', UPPER('&user')); END;
new   1: BEGIN rdsadmin.rdsadmin_util.grant_sys_object('UTL_HTTP', UPPER('app_user')); END;

PL/SQL procedure successfully completed.

SQL> BEGIN rdsadmin.rdsadmin_util.grant_sys_object('UTL_FILE', UPPER('&user')); END;
  2  /
old   1: BEGIN rdsadmin.rdsadmin_util.grant_sys_object('UTL_FILE', UPPER('&user')); END;
new   1: BEGIN rdsadmin.rdsadmin_util.grant_sys_object('UTL_FILE', UPPER('app_user')); END;

PL/SQL procedure successfully completed.

9.    在连接到 RDS 数据库实例的 SQL*Plus 会话中,运行与以下类似的命令,从而为钱包创建目录:
注意:最佳实践是将每个钱包存储在自己的目录中。

SQL> exec rdsadmin.rdsadmin_util.create_directory('WALLET');

10.    创建一个新的 Amazon Simple Storage Service(Amazon S3)存储桶,或使用一个现有的存储桶上传钱包。如果您安装并配置了 AWS 命令行界面(AWS CLI),请在创建 Oracle 钱包的客户端计算机上运行下面的命令。您还可以从 Amazon S3 控制台上传钱包。

>aws s3 cp cwallet.sso s3://wallet4321/

注意:如果在运行 AWS CLI 命令时遇到错误,请确保您使用的是最新版本的 AWS CLI

11.    将钱包从 S3 存储桶下载到 RDS 数据库实例。无论是否使用 Amazon S3 集成,您都可以执行此操作。

使用 Amazon S3 集成将钱包从 S3 存储桶下载到 RDS 实例

要使用 Amazon S3 集成从 S3 存储桶下载钱包,请参阅 Amazon S3 集成

1.    运行下面的命令将钱包文件从 S3 存储桶下载到 RDS 实例目录:

SQL> SELECT rdsadmin.rdsadmin_s3_tasks.download_from_s3(
          p_bucket_name    =>  'wallet4321',
          p_s3_prefix => 'cwallet.sso',
          p_directory_name =>  'WALLET') 
          AS TASK_ID FROM DUAL;   
OUTPUT
1588278782462-32

2.    使用上一步中的任务 ID 显示任务的输出文件来查看结果,从而确认下载是否成功。

SQL> SELECT text FROM table(rdsadmin.rds_file_util.read_text_file('BDUMP','dbtask-1588278782462-32.log'));

OUTPUT
2020-04-30 20:33:03.452 UTC [INFO ] This task is about to list the Amazon S3 objects for AWS Region us-east-1, bucket name wallet4321, and prefix cwallet.sso.
2020-04-30 20:33:03.526 UTC [INFO ] The task successfully listed the Amazon S3 objects for AWS Region us-east-1, bucket name wallet4321, and prefix cwallet.sso.
2020-04-30 20:33:03.544 UTC [INFO ] This task is about to download the Amazon S3 object or objects in /rdsdbdata/userdirs/01 from bucket name wallet4321 and key cwallet.sso.
2020-04-30 20:33:03.734 UTC [INFO ] The task successfully downloaded the Amazon S3 object or objects from bucket name wallet4321 with key cwallet.sso to the location /rdsdbdata/userdirs/01.
2020-04-30 20:33:03.734 UTC [INFO ] The task finished successfully.

3.    验证钱包是否已下载到数据库实例:

SQL> select * from table (rdsadmin.rds_file_util.listdir(p_directory => 'WALLET'));
FILENAME  TYPE         FILESIZE   MTIME
------------------------------------------------------------
01/              directory        4096      29-APR-20

cwallet.sso file                  2405     29-APR-20

4.    在连接到 RDS 数据库实例的 SQL*Plus 会话中,运行与以下类似的命令,从而为 utl_http 事务处理设置钱包路径:

SQL>  DECLARE
l_wallet_path all_directories.directory_path%type;
BEGIN
select directory_path into l_wallet_path from all_directories
where upper(directory_name)='WALLET';
utl_http.set_wallet('file:/' || l_wallet_path);
END;
/
PL/SQL procedure successfully completed.

5.    运行与以下类似的命令,以验证 Web 服务主机 DNS 名称解析:

SQL> SELECT UTL_INADDR.GET_HOST_ADDRESS(host => 'status.aws.amazon.com') FROM DUAL

6.    运行与以下类似的命令,以使用 Oracle 钱包成功浏览远程 Web 服务 URL:

SQL> SELECT utl_http.request('https://status.aws.amazon.com/robots.txt') AS ROBOTS_TXT FROM DUAL;

ROBOTS_TXT
--------------------------------------------------------------------------------
User-agent: *
Allow: /

有关将 utl_http 与 Oracle 实例结合使用的更多信息,请参阅在 Oracle 数据库实例上配置出站网络访问

在 RDS 选项组中不使用 Amazon S3 集成将钱包从 S3 存储桶下载到 RDS 实例

1.    在连接到 RDS 数据库实例的 SQL*Plus 会话中,运行与以下类似的命令,以允许 Oracle ACL 上的出站流量(使用您定义的用户):

SQL> define user='app_user';
SQL> BEGIN DBMS_NETWORK_ACL_ADMIN.CREATE_ACL
     ( acl => 's3.xml', description => 'AWS S3 ACL', principal => UPPER('&user'), is_grant => TRUE, privilege => 'connect');
     COMMIT;
     END;
     /
old   3: ( acl => 's3.xml', description => 'AWS S3 ACL', principal => UPPER('&user'), is_grant => TRUE, privilege => 'connect');
new   3: ( acl => 's3.xml', description => 'AWS S3 ACL', principal => UPPER('app_user'), is_grant => TRUE, privilege => 'connect');

PL/SQL procedure successfully completed.

SQL> BEGIN DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL ( acl => 's3.xml', host => '*.amazonaws.com');
     COMMIT;
     END;
     /
PL/SQL procedure successfully completed.

2.    在连接到 RDS 数据库实例的 SQL*Plus 会话中,创建以下过程:

SQL> CREATE OR REPLACE PROCEDURE s3_download_presigned_url (
        p_s3_url IN VARCHAR2,
        p_local_filename IN VARCHAR2,
        p_local_directory IN VARCHAR2,
        p_wallet_directory IN VARCHAR2 DEFAULT NULL
    ) AS
-- Local variables
    l_req utl_http.req;
    l_wallet_path VARCHAR2(4000);
    l_fh utl_file.file_type;
    l_resp utl_http.resp;
    l_data raw(32767);
    l_file_size NUMBER;
    l_file_exists BOOLEAN;
    l_block_size BINARY_INTEGER;
    l_http_status NUMBER;
-- User-defined exceptions
    e_https_requires_wallet EXCEPTION;
    e_wallet_dir_invalid EXCEPTION;
    e_http_exception EXCEPTION;
BEGIN
    -- Validate input
    IF (regexp_like(p_s3_url, '^https:', 'i') AND
        p_wallet_directory IS NULL) THEN
        raise e_https_requires_wallet;
    END IF;
    -- Use wallet, if specified
    IF (p_wallet_directory IS NOT NULL) THEN
        BEGIN
                   SELECT directory_path INTO l_wallet_path 
                   FROM dba_directories 
                   WHERE upper(directory_name)= upper(p_wallet_directory);
                   utl_http.set_wallet('file:' || l_wallet_path);
        EXCEPTION
            WHEN NO_DATA_FOUND
                THEN raise e_wallet_dir_invalid;
        END;
    END IF;

    -- Do HTTP request
    BEGIN
        l_req := utl_http.begin_request(p_s3_url, 'GET', 'HTTP/1.1');
                l_fh := utl_file.fopen(p_local_directory, p_local_filename, 'wb', 32767);
        l_resp := utl_http.get_response(l_req);
        -- If we get HTTP error code, write that instead
        l_http_status := l_resp.status_code;
        IF (l_http_status != 200) THEN
            dbms_output.put_line('WARNING: HTTP response '
                || l_http_status
                || ' - ' || l_resp.reason_phrase
                || '. Details in ' || p_local_filename
            );
        END IF;

        -- Loop over response and write to file
        BEGIN
            LOOP
                utl_http.read_raw(l_resp, l_data, 32766);
                utl_file.put_raw(l_fh, l_data, true);
            END LOOP;
        EXCEPTION
            WHEN utl_http.end_of_body THEN

                utl_http.end_response(l_resp);
        END;

        -- Get file attributes to see what we did
        utl_file.fgetattr(
            location => p_local_directory,
            filename => p_local_filename,
            fexists => l_file_exists,
            file_length => l_file_size,
            block_size => l_block_size
        );

        utl_file.fclose(l_fh);
        dbms_output.put_line('wrote ' || l_file_size || ' bytes');
        EXCEPTION
            WHEN OTHERS THEN
                utl_http.end_response(l_resp);
                utl_file.fclose(l_fh);
                dbms_output.put_line(dbms_utility.format_error_stack());
                dbms_output.put_line(dbms_utility.format_error_backtrace());
                raise;
    END;
EXCEPTION

    WHEN e_https_requires_wallet THEN
        dbms_output.put_line('ERROR: HTTPS requires a valid wallet location');
    WHEN e_wallet_dir_invalid THEN
        dbms_output.put_line('ERROR: wallet directory not found');
    WHEN others THEN
        raise;
END s3_download_presigned_url;

3.    通过运行与以下类似的命令生成一个 S3 预签名 URL:
注意:要运行此命令,您需要在客户端计算机上安装和配置 AWS CLI。默认情况下,预签名 URL 的有效期为 1 小时。有关更多信息,请参阅有关 presign 的 AWS CLI 参考

>aws s3 presign s3://wallet4321/cwallet.sso

https://wallet4321.s3.amazonaws.com/cwallet.sso?AWSAccessKeyId=AKIAJWTPIJJA4FAQURFA&Signature=u8ysKQyp4O6ws3Qy5qZak8PfmLE%3D&Expires=1588199836

4.    在连接到 RDS 数据库实例的 SQL*Plus 会话中,运行 s3_download_presigned_url 过程,以将钱包从 S3 存储桶下载到 RDS 数据库实例。请务必按以下所示更新过程的输入参数:
注意:您可以使用 http 或 https 将钱包从 S3 存储桶下载到 RDS 数据库实例。

  • p_s3_url 加生成的 S3 预签名 URL
  • p_local_filename 加钱包文件的名称
  • p_local_directory 加在 RDS Oracle 实例上创建的用于存储钱包的目录的名称
  • p_wallet_directory 加 S3_SSL_WALLET。RDS Oracle 实例使用此目录来存储包含 S3 Web 服务证书的钱包。

要通过 HTTP 来使用 S3 Web 服务,您可以使用以下示例代码:

注意:请将之前生成的 S3 预签名 URL 中的“http”替换为“https”。

SQL> SET SERVEROUTPUT ON;
SQL> set define #;
SQL> BEGIN s3_download_presigned_url( 
 p_s3_url=> 'http://wallet4321.s3.amazonaws.com/cwallet.sso?AWSAccessKeyId=AKIAJWTPIJJA4FAQURFA&Signature=u8ysKQyp4O6ws3Qy5qZak8PfmLE%3D&Expires=1588199836',
 p_local_filename => 'cwallet.sso',
 p_local_directory => 'WALLET' 
);
END;
/

要通过 HTTPS 来使用 S3 Web 服务,您可以使用以下示例代码:

注意:将 S3 Web 服务钱包存储在 RDS 实例目录 S3_SSL_WALLET 中是使用 HTTPS 的一个先决条件。

SQL> exec rdsadmin.rdsadmin_util.create_directory('S3_SSL_WALLET');
SQL> SET SERVEROUTPUT ON;
SQL> set define #;
SQL> BEGIN s3_download_presigned_url(
      p_s3_url=> 'https://wallet4321.s3.amazonaws.com/cwallet.sso?AWSAccessKeyId=AKIAJWTPIJJA4FAQURFA&Signature=u8ysKQyp4O6ws3Qy5qZak8PfmLE%3D&Expires=1588199836',
      p_local_filename => 'cwallet.sso',
      p_local_directory => 'WALLET',
      p_wallet_directory => 'S3_SSL_WALLET'
     );
     END;
    /

5.    验证钱包是否已下载到数据库实例:

SQL> select * from table (rdsadmin.rds_file_util.listdir(p_directory => 'WALLET'));
FILENAME         TYPE         FILESIZE   MTIME
------------------------------------------------------------
01/              directory     4096      29-APR-20

cwallet.sso      file          2405      29-APR-20

6.    在连接到 RDS 数据库实例的 SQL*Plus 会话中,运行与以下类似的命令,从而为 utl_http 事务处理设置钱包路径:

SQL>  DECLARE
l_wallet_path all_directories.directory_path%type;
BEGIN
select directory_path into l_wallet_path from all_directories
where upper(directory_name)='WALLET';
utl_http.set_wallet('file:/' || l_wallet_path);
END;
/
PL/SQL procedure successfully completed.

7.    运行与以下类似的命令,以验证 Web 服务主机 DNS 名称解析:

SQL> SELECT UTL_INADDR.GET_HOST_ADDRESS(host => 'status.aws.amazon.com') FROM DUAL

8.    运行与以下类似的命令,以使用 Oracle 钱包成功浏览远程 Web 服务 URL:

SQL> SELECT utl_http.request('https://status.aws.amazon.com/robots.txt') AS ROBOTS_TXT FROM DUAL;
ROBOTS_TXT
--------------------------------------------------------------------------------
User-agent: *
Allow: /

常见错误

ORA-28759: failure to open file(未能打开文件)

如果您引用的钱包不在您指定的位置,则可能会收到此错误。您可以通过列出目录来确认钱包文件的位置:

SQL> select directory_name, directory_path from dba_directories where directory_name = 'WALLET';

DIRECTORY_NAME               DIRECTORY_PATH
--------------------------------------------------------------------------------
WALLET                       /rdsdbdata/userdirs/01

ORA-28768: bad magic number(错误魔数)

如果 S3 存储桶及其内容使用 AWS Key Management Service(AWS KMS)进行加密,则可能会收到此错误。要解决此错误,请删除存储桶对象的加密。

ORA-12535: TNS: operation timed out(TNS 操作超时)

在以下情况下,您可能会收到此错误:

  • 数据库实例位于私有子网中。
  • 路由表没有通往互联网的路由(无 NAT 网关或 NAT 实例)。

相关信息

Amazon RDS 上的 Oracle

在 Amazon RDS for Oracle 上配置 Oracle 钱包并访问基于 SSL/TLS 的终端节点

Solving utl_file conundrum in Amazon RDS for Oracle

相关视频

AWS 官方
AWS 官方已更新 3 年前