보안 객체로 데이터 접근 제어하기

보안 객체로 데이터 접근 제어하기

공유된 데이터베이스의 민감한 데이터가 소비자 계정의 사용자에게 노출되지 않도록 하기 위해, Snowflake는 테이블을 직접 공유하는 대신 보안 뷰(secure view)나 보안 UDF를 공유할 것을 강력히 권장해요.

출처: Snowflake User Guide - Use secure objects to control data access

본문

또한 최적의 성능, 특히 매우 큰 테이블의 데이터를 공유할 때는 보안 객체의 기반이 되는 기본 테이블에 클러스터링 키(clustering key)를 정의하는 것을 권장해요. 이 주제는 공유된 보안 객체의 기본 테이블에서 클러스터링 키를 사용하는 방법을 설명하고, 보안 뷰를 소비자 계정과 공유하는 단계별 지침을 제공해요. 데이터 프로바이더와 소비자용 샘플 스크립트도 함께 다뤄요.

참고: 보안 객체를 공유하는 절차는 테이블을 공유하는 것과 본질적으로 같으며, 다음 객체들이 추가돼요.

  • 기본 테이블을 포함하는 "프라이빗" 스키마와 보안 객체를 포함하는 "퍼블릭" 스키마 — 퍼블릭 스키마와 보안 객체만 공유돼요.
  • 기본 테이블의 데이터를 여러 소비자 계정과 공유하고 특정 행을 특정 계정과 공유하려는 경우에만 필요한 "매핑 테이블"(mapping table, 프라이빗 스키마 안에도 있음)

공유된 데이터에 클러스터링 키 사용하기

매우 큰(예: 멀티 테라바이트) 테이블에서 클러스터링 키는 상당한 쿼리 성능 이점을 제공해요. 공유된 보안 뷰나 보안 UDF에 사용되는 기본 테이블에 클러스터링 키를 하나 이상 정의하면 소비자 계정의 사용자가 이 객체를 사용할 때 부정적인 영향을 받지 않게 해줘요. 클러스터링 키로 사용할 컬럼을 선택할 때는 몇 가지 중요한 고려 사항이 있어요.

샘플 설정과 작업

이 샘플 지침은 데이터 프로바이더 계정에 mydb라는 데이터베이스가 있고 이 데이터베이스에 private와 public 두 스키마가 있다고 가정해요. 데이터베이스와 스키마가 존재하지 않으면 진행 전에 만들어야 해요.

1단계: 프라이빗 스키마에 데이터와 매핑 테이블 만들기

mydb.private 스키마에 다음 두 테이블을 만들고 데이터로 채워요.

  • sensitive_data — 공유할 데이터를 포함하며, 계정별 데이터 접근을 제어하는 access_id 컬럼이 있어요.
  • sharing_access — access_id 컬럼을 사용해 공유된 데이터와 그 데이터에 접근할 수 있는 계정을 매핑해요.

2단계: 퍼블릭 스키마에 보안 뷰 만들기

mydb.public 스키마에 다음 보안 뷰를 만들어요.

  • paid_sensitive_data — 계정에 따라 데이터를 표시해요. 기본 테이블(sensitive_data)의 access_id 컬럼은 뷰에 포함할 필요가 없어요.

3단계: 테이블과 보안 뷰 검증하기

데이터가 계정별로 제대로 필터링되는지 테이블과 보안 뷰를 검증해요. 다른 계정과 공유될 보안 뷰의 검증을 가능하게 하기 위해 Snowflake는 SIMULATED_DATA_SHARING_CONSUMER 세션 파라미터를 제공해요. 이 세션 파라미터를 시뮬레이션하려는 소비자 계정 이름으로 설정해요. 그러면 뷰를 쿼리해 소비자 계정의 사용자가 보게 될 결과를 확인할 수 있어요.

4단계: 셰어 만들기

  1. 셰어를 만들어요. 셰어를 만들려면 ACCOUNTADMIN 역할 또는 전역 CREATE SHARE 권한이 부여된 역할을 사용해야 해요. 이 역할은 셰어에 객체를 부여하기 위해 다음 중 하나도 가져야 해요.
    • 공유된 데이터베이스에 대한 OWNERSHIP 권한이 있는 역할
    • 데이터베이스에 대한 USAGE 권한이 WITH GRANT OPTION으로 있는 역할. 예:
      GRANT USAGE ON <database-name> TO ROLE <role-name> WITH GRANT OPTION;
      
  2. 데이터베이스(mydb), 스키마(public), 보안 뷰(paid_sensitive_data)를 셰어에 추가해요. 이 객체들에 대한 권한을 데이터베이스 역할을 통해 셰어에 추가하거나, 객체에 대한 권한을 셰어에 직접 부여할 수 있어요.
  3. 셰어의 내용을 확인해요. 기본적으로 SHOW GRANTS 명령으로 셰어 안 객체가 필요한 권한을 갖고 있는지 확인해야 해요. 보안 뷰 paid_sensitive_data는 명령 출력에서 테이블로 표시된다는 점을 기억해 두세요.
  4. 셰어에 계정을 하나 이상 추가해요.

샘플 스크립트

다음 스크립트는 앞서 설명한 모든 작업을 수행하는 예시예요.

  1. private 스키마에 두 테이블을 만들고, 첫 번째 테이블에 세 회사(Apple, Microsoft, IBM)의 주식 데이터를 채운 뒤, 두 번째 테이블에 주식 데이터를 개별 계정에 매핑하는 데이터를 채워요.
use role sysadmin;

create or replace table mydb.private.sensitive_data (
  name string,
  date date,
  time time(9),
  bid_price float,
  ask_price float,
  bid_size int,
  ask_size int,
  access_id string /* granularity for access */
) cluster by (date);

insert into mydb.private.sensitive_data
values ('AAPL', dateadd(day, -1, current_date()), '10:00:00', 116.5, 116.6, 10, 10, 'STOCK_GROUP_1'),
       ('AAPL', dateadd(month, -2, current_date()), '10:00:00', 116.5, 116.6, 10, 10, 'STOCK_GROUP_1'),
       ('MSFT', dateadd(day, -1, current_date()), '10:00:00', 58.0, 58.9, 20, 25, 'STOCK_GROUP_1'),
       ('MSFT', dateadd(month, -2, current_date()), '10:00:00', 58.0, 58.9, 20, 25, 'STOCK_GROUP_1'),
       ('IBM', dateadd(day, -1, current_date()), '11:00:00', 175.2, 175.4, 30, 15, 'STOCK_GROUP_2'),
       ('IBM', dateadd(month, -2, current_date()), '11:00:00', 175.2, 175.4, 30, 15, 'STOCK_GROUP_2');

create or replace table mydb.private.sharing_access (
  access_id string,
  snowflake_account string
);

/* 첫 번째 insert에서 CURRENT_ACCOUNT()는 당신 계정에 AAPL과 MSFT 데이터 접근을 줍니다. */
insert into mydb.private.sharing_access values ('STOCK_GROUP_1', CURRENT_ACCOUNT());

/* 두 번째 insert에서 <consumer_account>를 계정 이름으로 바꿉니다. 이 계정은 IBM 데이터에만 접근합니다. */
/* 계정 이름은 대소문자를 구분하며 대문자로 작은따옴표 안에 넣어야 합니다. 예: */
/*     insert into mydb.private.sharing_access values('STOCK_GROUP_2', 'ACCT1') */
/* IBM 데이터를 여러 계정과 공유하려면 각 계정에 대해 두 번째 insert를 반복합니다. */
insert into mydb.private.sharing_access values ('STOCK_GROUP_2', '<consumer_account>');
  1. public 스키마에 보안 뷰를 만들어요. 이 뷰는 두 번째 테이블의 매핑 정보를 사용해 첫 번째 테이블의 주식 데이터를 계정별로 필터링해요.
create or replace secure view mydb.public.paid_sensitive_data as
  select name, date, time, bid_price, ask_price, bid_size, ask_size
  from mydb.private.sensitive_data sd
  join mydb.private.sharing_access sa on sd.access_id = sa.access_id
    and sa.snowflake_account = current_account();

grant select on mydb.public.paid_sensitive_data to public;

/* 먼저 프로바이더 계정으로 데이터를 쿼리해 테이블과 보안 뷰를 테스트합니다. */
select count(*) from mydb.private.sensitive_data;
select * from mydb.private.sensitive_data;
select count(*) from mydb.public.paid_sensitive_data;
select * from mydb.public.paid_sensitive_data;
select * from mydb.public.paid_sensitive_data where name = 'AAPL';

/* 다음으로 시뮬레이션된 소비자 계정으로 데이터를 쿼리해 보안 뷰를 테스트합니다. */
/* 시뮬레이션할 계정은 SIMULATED_DATA_SHARING_CONSUMER 세션 파라미터로 지정합니다. */
/* ALTER 명령에서 <consumer_account>를 매핑 테이블에 지정한 계정 중 하나로 바꿉니다. */
/* 계정 이름은 대소문자를 구분하지 않으며 작은따옴표로 감쌀 필요가 없습니다. 예: */
/*     alter session set simulated_data_sharing_consumer=acct1; */
alter session set simulated_data_sharing_consumer=<account_name>;

select * from mydb.public.paid_sensitive_data;
  1. ACCOUNTADMIN 역할로 셰어를 만들어요.
use role accountadmin;

create or replace share mydb_shared
  comment = 'Example of using Secure Data Sharing with secure views';

show shares;
  1. 객체를 셰어에 추가해요. 이 객체들에 대한 권한을 데이터베이스 역할을 통해 셰어에 추가하거나(옵션 1), 객체에 대한 권한을 셰어에 직접 부여할 수 있어요(옵션 2).
/* 옵션 1: 데이터베이스 역할을 만들고, 객체 권한을 데이터베이스 역할에 부여한 뒤, 그 역할을 셰어에 부여 */
create database role mydb.dr1;
grant usage on database mydb to database role mydb.dr1;
grant usage on schema mydb.public to database role mydb.dr1;
grant select on mydb.public.paid_sensitive_data to database role mydb.dr1;
grant usage on database mydb to share mydb_shared;
grant database role mydb.dr1 to share mydb_shared;

/* 옵션 2: 셰어에 포함할 데이터베이스 객체에 대한 권한을 부여 */
grant usage on database mydb to share mydb_shared;
grant usage on schema mydb.public to share mydb_shared;
grant select on mydb.public.paid_sensitive_data to share mydb_shared;

/* 셰어의 내용을 확인 */
show grants to share mydb_shared;
  1. 셰어에 계정을 추가해요.
/* ALTER 문에서 <consumer_accounts>를 앞서 STOCK_GROUP2에 지정한 소비자 계정으로 바꿉니다. */
/* 각 계정 이름은 쉼표로 구분합니다. 예: */
/*     alter share mydb_shared set accounts = acct1, acct2; */
alter share mydb_shared set accounts = <consumer_accounts>;

소비자용 샘플 스크립트

소비자는 이 스크립트로 (위 스크립트에서 만든 셰어에서) 데이터베이스를 만들고 결과 데이터베이스의 보안 뷰를 쿼리할 수 있어요.

  1. 셰어에서 데이터베이스를 만들어 공유된 데이터베이스를 계정으로 가져와요.
/* 다음 명령에서 셰어 이름은 <provider_account>를 셰어를 제공한 계정 이름으로 바꿔 정규화해야 합니다. */
/* 예: desc prvdr1.mydb_shared; */
use role accountadmin;

show shares;

desc share <provider_account>.mydb_shared;

create database mydb_shared1 from share <provider_account>.mydb_shared;
  1. 데이터베이스에 대한 권한을 계정 안의 다른 역할(예: CUSTOM_ROLE1)에 부여해요. GRANT 문은 데이터 소비자가 데이터베이스 역할(옵션 1)로 셰어에 객체를 추가했는지, 객체에 대한 권한을 셰어에 직접 부여했는지(옵션 2)에 따라 달라져요.
/* 옵션 1 */
grant database role mydb_shared1.db1 to role custom_role1;

/* 옵션 2 */
grant imported privileges on database mydb_shared1 to custom_role1;
  1. CUSTOM_ROLE1 역할로 만든 데이터베이스의 뷰를 쿼리해요. 쿼리를 수행하려면 세션에서 활성 웨어하우스가 사용 중이어야 해요. USE WAREHOUSE 명령에서 <warehouse_name>을 계정 안의 웨어하우스 이름 중 하나로 바꿔요. CUSTOM_ROLE1 역할은 그 웨어하우스에 대한 USAGE 권한이 있어야 해요.
use role custom_role1;

show views;

use warehouse <warehouse_name>;

select * from paid_sensitive_data;

더 알아보기