

# 使用 Active Directory 安全组为 Babelfish 设置 Kerberos 身份验证
<a name="babelfish-kerberos-securityad"></a>

从 Babelfish 版本 4.2.0 开始，可以使用 Active Directory 安全组为 Babelfish 设置 Kerberos 身份验证。以下是使用 Active Directory 设置 Kerberos 身份验证需要完成的先决条件：
+  必须按照[适用于 Babelfish 的 Kerberos 身份验证](babelfish-active-directory.md)中提到的所有步骤进行操作。
+ 确保数据库实例与 Active Directory 关联。为验证这一点，您可以在控制台中或者通过运行 [https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-instances.html](https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-instances.html) AWS CLI 命令来查看域成员资格的状态。

  数据库实例的状态应为已启用 kerberos。有关了解域成员资格的更多信息，请参阅[了解域成员资格](postgresql-kerberos-managing.md#postgresql-kerberos-managing.understanding)。
+ 使用以下查询验证 NetBIOS 域名和 DNS 域名之间的映射：

  ```
  SELECT netbios_domain_name, fq_domain_name FROM babelfish_domain_mapping;
  ```
+ 在继续操作之前，请验证使用单独登录名进行的 Kerberos 身份验证是否符合预期。使用以 Active Directory 用户身份进行 Kerberos 身份验证的连接应该会取得成功。如果您遇到任何问题，请参阅[经常发生的错误](babelfish-active-directory.md#babelfish-active-directory-errors)。

## 设置 pg\$1ad\$1mapping 扩展
<a name="babelfish-kerberos-securityad-setpgextn"></a>

 必须按照[设置 pg\$1ad\$1mapping 扩展](AD.Security.Groups.md#AD.Security.Groups.Setup)中提到的所有步骤进行操作。要验证扩展是否已安装，请从 TDS 端点运行以下查询：

```
1> SELECT extname, extversion FROM pg_extension where extname like 'pg_ad_mapping';
2> GO
extname       extversion
------------- ----------
pg_ad_mapping 0.1

(1 rows affected)
```

## 管理组登录名
<a name="babelfish-kerberos-securityad-managing"></a>

 按照[管理登录信息](babelfish-active-directory.md#babelfish-active-directory-login-managing)中提到的步骤创建组登录名。我们建议登录名与 Active Directory（AD）安全组名称相同以便于维护，但这不是强制性要求。例如：

```
CREATE LOGIN [corp\accounts-group] FROM WINDOWS [WITH DEFAULT_DATABASE=database]
```

# 将 T-SQL 组登录名与 AD 安全组进行映射
<a name="babelfish-kerberos-securityad-maptsql"></a>

 您需要为每个要求访问数据库服务器的 AD 安全组显式预置 T-SQL Windows 组登录名。属于至少一个预置 AD 安全组的 AD 用户将获得访问数据库服务器的权限。

**注意**  
此 T-SQL 登录名无法再使用基于密码的身份验证进行身份验证。

 例如，accounts-group 是 AD 中的一个安全组，如果您希望在 Babelfish 中预置该安全组，则必须使用 [corp\$1accounts-group] 格式。
+ AD 安全组：accounts-group
+ TSQL 登录名：[corp\$1accounts-group]
+ 给定 TSQL 登录名的等效 PG 角色：accounts-group@CORP.EXAMPLE.COM

 管理员现在可以通过以下 psql 命令，从 PostgreSQL 端点创建 AD 安全组和 T-SQL 登录名之间的映射。有关函数用法的更多信息，请参阅[使用 `pg_ad_mapping` 扩展中的函数](AD.Security.Groups.md#AD.Security.Groups.functions)。

**注意**  
添加映射时，应以 login\$1name@FQDN 格式指定 T-SQL 登录名。通过 TDS 端点进行连接时，将忽略权重。有关使用权重的更多信息，请参阅[通过 PostgreSQL 端口上的 PostgreSQL 端点连接到 Babelfish](babelfish-kerberos-securityad-connect-pgendpoint.md)。

```
postgres=>select pgadmap_set_mapping('accounts-group', 'accounts-group@CORP.EXAMPLE.COM', <SID>, <Weight>);
```

有关检索 AD 安全组的 SID 的信息，请参阅[在 PowerShell 中检索 Active Directory 组 SID](AD.Security.Groups.md#AD.Security.Groups.retrieving)。

下表显示了从 AD 安全组到 T-SQL 登录名的示例映射：


| AD 安全组 | TSQL 登录名 | 给定 TSQL 登录名的等效 PG 角色 | 权重 | 
| --- | --- | --- | --- | 
| accounts-group | [corp\$1accounts-group] | accounts-group@CORP.EXAMPLE.COM | 7 | 
| sales-group | [corp\$1sales-group] | sales-group@CORP.EXAMPLE.COM | 10 | 
| dev-group | [corp\$1dev-group] | dev-group@CORP.EXAMPLE.COM | 7 | 

```
postgres=> select admap.ad_sid, admap.ad_grp, lgn.orig_loginname, lgn.rolname, admap.weight from pgadmap_read_mapping() as admap, sys.babelfish_authid_login_ext as lgn where admap.pg_role = lgn.rolname;
    ad_sid    |     ad_grp     |    orig_loginname   |             rolname             | weight
--------------+----------------+---------------------+---------------------------------+--------
 S-1-5-67-890 | accounts-group | corp\accounts-group  | accounts-group@CORP.EXAMPLE.COM |     7
 S-1-2-34-560 | sales-group    | corp\sales-group     | sales-group@CORP.EXAMPLE.COM    |     10
 S-1-8-43-612 | dev-group      | corp\dev-group       | dev-group@CORP.EXAMPLE.COM      |     7
 (7 rows)
```

# 通过 TDS 端点连接到 Babelfish
<a name="babelfish-kerberos-securityad-connect"></a>

 在以下示例中，user1 是 accounts-group 和 sales-group 的成员，user2 是 accounts-group 和 dev-group 的成员。


| 用户名 | AD 安全组成员资格 | 
| --- | --- | 
| user1 | accounts-group、sales-group | 
| user2 | accounts-group、dev-group | 

 使用 sqlcmd 实用程序连接到 Babelfish 数据库服务器。可以按照以下示例验证用户（在本例中为 user1）是否已使用 Kerberos 进行身份验证：

```
1> select principal, gss_authenticated from pg_stat_gssapi where pid = pg_backend_pid();
2>  GO
principal               gss_authenticated
----------------------  -----------------
user1@CORP.EXAMPLE.COM  1 

((1 rows affected))
1> select suser_name();
2>  GO
suser_name
----------
corp\user1 

(1 rows affected)
```

 在此示例中，user1 将继承 accounts-group 和 sales-group 的权限。可以使用 `sys.login_token` 系统视图来验证组成员资格。

```
1> SELECT name, type FROM sys.login_token;
2>  GO
name                type
------------------- ----
corp\accounts-group WINDOWS GROUP
corp\sales-group    WINDOWS GROUP

(2 rows affected)
```

## 审计和日志记录
<a name="babelfish-kerberos-securityad-audit"></a>

 要确定 AD 安全主体身份，请使用以下命令：

```
1> select suser_name();
2> GO
suser_name
----------
corp\user1

(1 rows affected)
```

目前，AD 用户身份在日志中不可见。可以开启 `log_connections` 参数来记录数据库会话建立情况。有关更多信息，请参阅 [log\$1connections](https://docs.aws.amazon.com/prescriptive-guidance/latest/tuning-postgresql-parameters/log-connections.html)。输出包括作为主体的 AD 用户身份，如以下示例所示。然后，与此输出关联的后端 PID 有助于将操作归因回实际 AD 用户。

```
bbf_group_ad_login@babelfish_db:[615]:LOG: connection authorized: user=bbf_group_ad_login database=babelfish_db application_name=sqlcmd GSS (authenticated=yes, encrypted=yes, principal=user1@CORP.EXAMPLE.COM)
```

# 利用 AD 安全组成员资格的权限
<a name="babelfish-kerberos-securityad-privileges"></a>

## 继承服务器级权限
<a name="babelfish-kerberos-securityad-inheritpriv-server"></a>

 属于给定 AD 安全组成员的 AD 用户将继承向映射的 Windows 组登录名授予的服务器级权限。例如，考虑 `accounts-group` AD 安全组，该安全组被授予 Babelfish 上 `sysadmin` 服务器角色的成员资格。可以使用以下命令继承服务器级权限：

```
1> ALTER SERVER ROLE sysadmin ADD MEMBER [corp\accounts-group];
```

 因此，属于 `accounts-group` AD 安全组成员的任何 Active Directory 用户都将继承与 `sysadmin` 角色关联的服务器级权限。这意味着，像 `corp\user1` 这样的用户（属于 `accounts-group` 的成员）现在可以在 Babelfish 中执行服务器级操作。

**注意**  
 要执行服务器级 DDL，单独 AD 用户的 Windows 登录名必须存在。有关更多信息，请参阅 [限制](babelfish-kerberos-securityad-limitations.md)。

## 继承数据库级权限
<a name="babelfish-kerberos-securityad-inheritpriv-database"></a>

 要授予数据库级权限，必须创建数据库用户并使用 Windows 组登录名进行映射。属于给定 AD 安全组成员的 AD 用户将继承授予该数据库用户的数据库级权限。在以下示例中，可以看到 Windows 组 [corp\$1accounts-group] 的数据库级权限是如何分配的。

```
1> CREATE DATABASE db1; 
2> GO
1> USE db1;
2> GO
Changed database context to 'db1'.
1> CREATE TABLE dbo.t1(a int);
2> GO
```

 为 Windows 组登录名 [corp\$1accounts-group] 创建数据库用户 [corp\$1sales-group]。要执行此步骤，请使用作为 sysadmin 成员的登录名通过 TDS 端点进行连接。

```
1> CREATE USER [corp\accounts-group] FOR LOGIN [corp\accounts-group];
2> GO
```

 现在，以 AD 用户 user1 的身份进行连接，来检查表 t1 的访问权限。由于我们尚未授予数据库级权限，因此这会导致权限被拒绝错误。

```
1> SELECT * FROM dbo.t1;
2> GO
Msg 33557097, Level 16, State 1, Server db-inst, Line 1
permission denied for table t1
```

向数据库用户 [corp\$1accounts-group] 授予对表 t1 的 SELECT 权限。要执行此步骤，请使用作为 sysadmin 成员的登录名通过 TDS 端点进行连接。

```
1> GRANT SELECT ON dbo.t1 TO [corp\accounts-group];
2> GO
```

 以 AD 用户 user1 的身份进行连接，来验证访问权限。

```
1> SELECT * FROM dbo.t1;
2> GO
a
-----------

(0 rows affected)
```

# 基于默认或显式架构处理 DDL 语句行为
<a name="babelfish-kerberos-securityad-ddl"></a>

 使用经 AD 身份验证的会话时，当前会话的默认架构由以下条件决定：
+ 如果存在单独的数据库用户，则该用户的默认架构将被视为当前会话的默认架构。
+ 如果存在组数据库用户的默认架构，则该组数据库用户的默认架构将被视为当前会话的默认架构，其主体 ID 为最小。

## 了解 CREATE DDL 语句行为
<a name="babelfish-kerberos-securityad-ddlcreate"></a>

 如果未在 CREATE DDL 语句中指定显式架构，则将在当前会话的默认架构中创建对象。如果无法确定架构是默认架构还是显式架构，则 DDL 语句将引发以下错误：

```
"Babelfish Unsupported Command : Schema required for CREATE DDLs when connecting with Active Directory Group authentication. Assign default schema to group user or specify schema in command."
```

**Example ：Windows 组用户的默认架构不存在**  
Windows 组用户 [corp\$1accounts-group] 具有 NULL 默认架构，AD 用户 user1 正在尝试在没有显式指定架构的情况下执行 DDL。由于 user1 不存在单独的 Windows 登录名和用户，因此它将仅获得 Windows 组用户 [corp\$1accounts-group] 的数据库级权限。  

```
1> create TABLE t2(a int);
2> GO

Msg 33557097, Level 16, State 1, Server db-inst, Line 1
Babelfish Unsupported Command : Schema required for CREATE DDLs when connecting with Active Directory Group authentication. Assign default schema to group user or specify schema in command.
```
AD 用户 user1 不存在单独的 Windows 登录名和用户

**Example ：Windows 组用户存在默认架构**  
使用 sysadmin 通过默认架构为 [corp\$1accounts-group] 登录名创建 Windows 组用户。  

```
1> CREATE USER [corp\accounts-group] FOR LOGIN [corp\accounts-group] WITH DEFAULT_SCHEMA = sch_acc;
2> GO
1> CREATE SCHEMA sch_acc AUTHORIZATION [gad\accounts-group];
2> GO
1> SELECT name, principal_id, default_schema_name FROM sys.database_principals WHERE name = 'corp\accounts-group';
2> GO
                
name               principal_id default_schema_name
------------------ ------------ -------------------
corp\accounts-group 24162        sch_acc

(1 rows affected)
```
 尝试使用 AD 用户 user1 在不显式指定架构的情况下创建对象。表 t2 将在 [corp\$1accounts-group] Windows 组用户的默认架构中创建。此对象的所有者将与架构 sch\$1acc 的所有者相同。  

```
1> CREATE TABLE t_group(a int);
2> GO
1> SELECT name, schema_name(schema_id) FROM sys.objects WHERE name like 't_group';
2> GO

name    schema_name
------- -----------
t_group sch_acc

(1 rows affected)
```
AD 用户 user1 不存在单独的 Windows 登录名和用户

**Example ：AD 用户也存在单独的数据库用户**  
 如果 AD 用户也存在单独的数据库用户，则将始终在与单独的数据库用户关联的架构中创建对象。如果数据库用户不存在该架构，将使用 dbo 架构。为 AD 用户 user1 创建单独的 Windows 登录名和数据库用户。使用 sysadmin 登录名通过 TDS 端点进行连接   

```
1> CREATE LOGIN [corp\user1] FROM WINDOWS;
2> GO
1> CREATE USER [corp\user1] FOR LOGIN [corp\user1] WITH DEFAULT_SCHEMA = sch1;
2> GO
1> CREATE SCHEMA sch1 AUTHORIZATION [corp\user1];
2> GO
1> SELECT name, default_schema_name FROM sys.database_principals WHERE name = 'corp\user1';
2> GO

name      default_schema_name
--------- -------------------
corp\user1 sch1

(1 rows affected)
```
 使用 AD 用户 user1 进行连接，然后尝试在不显式指定架构的情况下创建对象。将在架构 sch1 中创建表 t2。另请注意，此对象的所有者将与架构 sch1 的所有者相同。  

```
1> CREATE TABLE t2(a int);
2> GO
1> SELECT name, schema_name(schema_id) FROM sys.objects WHERE name like 't2';
2> GO
            
name schema_name
---- -----------
t2   sch1

(1 rows affected)
```

# 限制
<a name="babelfish-kerberos-securityad-limitations"></a>
+ 转储/还原实用程序不支持转储 pg\$1ad\$1mapping 扩展映射。还原后，您将需要重新创建这些映射。
+ 具有 `pg_ad_mapping` 的 Babelfish 和 Aurora PostgreSQL 实例不支持蓝绿部署。
+ 不支持隐式架构创建。不支持需要隐式架构创建的 DDL 语句。
+ 当单独的 Windows 登录名不存在而只有组 Windows 登录名存在时，在经组 AD 身份验证的会话中不支持服务器级 DDL ALTER AUTHORIZATION ON DATABASE、CREATE DATABASE、CREATE LOGIN、ALTER LOGIN、ALTER SERVER ROLE、ALTER DATABASE。要解决此限制，建议在经密码验证身份的会话中执行这些操作或创建单独的 Windows 登录名。
+ 不支持隐式用户创建。理想的 T-SQL 行为 [Babelfish 尚不支持]；在某些情况下，例如 DDL 和访问控制语句（如 GRANT/REVOKE），此时在命令中指定了 AD 用户的名称，但该用户在数据库中不存在，则会隐式创建名为 AD 用户的数据库用户。
+ 对于 PL/pgSQL 过程或函数中的 DDL（它们在经组 AD 身份验证的会话中从 PSQL 端点创建并从 TDS 端点执行）：
  + 将支持 ALTER/DROP 语句。
  + 如果未显式提供架构且当前会话的默认架构为 null，则 CREATE TABLE、CREATE VIEW、CREATE INDEX、CREATE FUNCTION/PROC、CREATE TYPE、CREATE SEQUENCE、CREATE TRIGGER、SELECT INTO、CREATE FULLTEXT INDEX、CREATE UNIQUE INDEX 将引发错误。
  + 对于 PG（不在 T-SQL 中）特定对象 CREATE 订阅、CREATE 表空间、CREATE 策略、CREATE 转换，将不支持 CREATE DATABASE、CREATE EXTENSION 和所有其它 CREATE 语句。
+ 经组 AD 身份验证的会话不支持来自 PostgreSQL 端点的 DDL。作为一种解决方法，您始终可以使用主用户或任何使用基于密码的身份验证机制的其它用户进行连接。
+ 像 SUSER\$1SID()、IS\$1SRVROLEMEMBER()、IS\$1MEMBER()、sys.dm\$1exec\$1sessions 这样的系统对象具有以下限制。
  + 提供 AD 用户或 AD 安全组时，SUSER\$1SID() 不会返回 SID。
  + 如果当前 AD 用户从任何 Windows 组登录名的服务器角色成员资格继承服务器角色成员资格，则 IS\$1SRVROLEMEMBER() 将不考虑角色成员资格。
  + 对于任何与 Windows 组相关的查询，IS\$1MEMBER() 都将返回 false。
  + sys.dm\$1exec\$1sessions 不会显示预期值 login\$1name、nt\$1user\$1name 列。

# 通过 PostgreSQL 端口上的 PostgreSQL 端点连接到 Babelfish
<a name="babelfish-kerberos-securityad-connect-pgendpoint"></a>

也可以利用从 TDS 端口创建的组登录名，通过 PostgreSQL 端口进行连接。要通过 PostgreSQL 端口进行连接，您需要从 PostgreSQL 客户端应用程序中按照格式 `<ad_username@FQDN>` 指定 AD 用户的名称。不能使用 `<DNS domain name\ad_username>` 格式。

默认情况下，PostgreSQL 对用户名使用区分大小写的比较。要让 Aurora PostgreSQL 将 Kerberos 用户名解释为不区分大小写，必须在自定义 Babelfish 集群参数组中将 krb\$1caseins\$1users 参数设置为 true。默认情况下，此参数设置为 false。有关更多信息，请参阅 [将 Aurora PostgreSQL 数据库集群配置为使用不区分大小写的用户名](postgresql-kerberos-setting-up.md#postgresql-kerberos-setting-up.create-logins.set-case-insentive)。

## 当 AD 用户属于多个组时，T-SQL 和 PostgreSQL 端点之间的行为差异
<a name="babelfish-kerberos-securityad-diff-tsql-pg"></a>

考虑 AD 用户 user1 是两个 AD 安全组 [corp\$1accounts-group] 和 [corp\$1sales-group] 的一部分，数据库管理员已按以下方式设置了用户映射。

```
postgres=> select * from pgadmap_read_mapping();
            
ad_sid       | pg_role                         | weight | ad_grp 
-------------+---------------------------------+--------+---------------
S-1-5-67-980 | accounts-group@CORP.EXAMPLE.COM | 7      | accounts-group
S-1-2-34-560 | sales-group@CORP.EXAMPLE.COM    | 10     | sales-group
(2 rows)
```

如果用户从 T-SQL 端点进行连接，则在授权期间，该用户将继承所有关联 T-SQL 登录名的权限。在此示例中，user1 将继承来自这两个 T-SQL 组登录名的权限并集，但将忽略权重。这符合标准的 T-SQL 行为。

但是，如果同一个用户从 PostgreSQL 端点进行连接，则它只能从一个权重最高的关联 T-SQL 登录名继承权限。如果为两个 T-SQL 组登录名分配的权重相同，则 AD 用户将继承与最新添加的映射相对应的 T-SQL 登录名的权限。对于 PostgreSQL，建议指定权重，以反映各个数据库角色的相对权限/特权来避免模糊不清。在下面的示例中，user1 通过 PSQL 端点进行了连接，并且仅继承了 sales-groups 权限。

```
babelfish_db=> select session_user, current_user;

   session_user               |   current_user
------------------------------+---------------------------
 sales-group@CORP.EXAMPLE.COM | sales-group@CORP.EXAMPLE.COM
(1 row)


babelfish_db=> select principal, gss_authenticated from pg_stat_gssapi where pid = pg_backend_pid();

     principal          | gss_authenticated
------------------------+-------------------
 user1@CORP.EXAMPLE.COM | t
(1 row)
```