권한

권한 (Privileges)

데이터베이스 객체를 누가 만들었든, 그 객체에는 소유자(owner) 가 있어요. 보통 객체를 만든 롤(role)이 소유자가 되죠. 그런데 문제는 기본 상태에서는 소유자(또는 superuser)만 그 객체를 마음대로 쓸 수 있다는 거예요. 다른 롤이 객체를 쓰게 하려면 권한(privilege)을 부여해야 해요. 이 포스트에서는 PostgreSQL의 권한 종류와, GRANT·REVOKE로 권한을 어떻게 주고 뺏는지, 그리고 ACL이 실제로 어떻게 보이는지 살펴볼게요.

출처: 공식문서

권한의 기본 개념

객체가 생성되면 소유자가 할당돼요. 소유자는 보통 생성문을 실행한 롤이에요. 대부분의 객체에서 초기 상태는 소유자(또는 superuser)만 무엇이든 할 수 있는 상태예요. 다른 롤이 쓰게 하려면 권한을 부여해야 해요.

권한에는 여러 종류가 있어요: SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER, CREATE, CONNECT, TEMPORARY, EXECUTE, USAGE, SET, ALTER SYSTEM, MAINTAIN. 어떤 권한이 적용되는지는 객체 유형(테이블, 함수 등)에 따라 달라져요.

객체를 수정하거나 파괴할 권리는 소유자라는 사실 자체에 내재돼 있어요. 이 권리는 그 자체로 부여하거나 뺏을 수 없어요. (다만 다른 권한들과 마찬가지로, 소유 롤의 구성원들은 이 권리를 상속받을 수 있어요.)

객체는 그에 맞는 종류의 ALTER 명령으로 새 소유자에게 할당할 수 있어요.

ALTER TABLE table_name OWNER TO new_owner;

superuser는 항상 이걸 할 수 있어요. 일반 롤은 객체의 현재 소유자이면서(또는 소유 롤의 권한을 상속받으면서) 새 소유 롤로 SET ROLE 할 수 있을 때만 가능해요. 옛 소유자의 모든 객체 권한은 소유권과 함께 새 소유자에게 이전돼요.

GRANT와 REVOKE

권한을 부여하려면 GRANT 명령을 써요. 예를 들어 joe가 기존 롤이고 accounts가 기존 테이블이라면, 테이블 갱신 권한은 이렇게 부여해요.

GRANT UPDATE ON accounts TO joe;

특정 권한 대신 ALL을 쓰면 그 객체 유형과 관련된 모든 권한을 부여해요.

특별한 '롤' 이름 PUBLIC을 쓰면 시스템의 모든 롤에 권한을 부여할 수 있어요. 또 '그룹' 롤을 만들어 데이터베이스 사용자가 많을 때 권한 관리를 돕게 할 수도 있어요.

이전에 부여한 권한을 뺏으려면 이름답게 REVOKE 명령을 써요.

REVOKE ALL ON accounts FROM PUBLIC;

보통 객체의 소유자(또는 superuser)만 객체에 권한을 부여하거나 뺏을 수 있어요. 다만 권한을 "grant option과 함께" 부여할 수 있는데, 그러면 받는 사람이 그 권한을 다시 다른 사람에게 부여할 권리를 갖게 돼요. grant option이 나중에 뺏기면, 그 수신자로부터 (직접이든 연쇄 부여를 통해서든) 권한을 받았던 모든 사람이 권한을 잃어요.

객체 소유자는 자신의 일반 권한을 뺏기로 선택할 수 있어요. 예를 들어 자신을 포함해 모두에게 테이블을 읽기 전용으로 만들 수 있죠. 하지만 소유자는 항상 모든 grant option을 가진 것으로 취급되므로, 언제든 자신의 권한을 다시 부여할 수 있어요.

권한 종류들

  • SELECT: 테이블, 뷰, 구체화 뷰 등 테이블형 객체의 아무 컬럼(또는 특정 컬럼)에서 SELECT를 허용해요. COPY TO 사용도 허용하고요. UPDATE, DELETE, MERGE에서 기존 컬럼 값을 참조할 때도 필요해요. 시퀀스에 대해서는 currval 함수 사용을, 대형 객체에 대해서는 읽기를 허용해요.
  • INSERT: 테이블·뷰 등에 새 행 삽입을 허용해요. 특정 컬럼에만 부여할 수 있는데, 그러면 INSERT 명령에서 그 컬럼들만 할당할 수 있어요(나머지 컬럼은 기본값을 받아요). COPY FROM 사용도 허용해요.
  • UPDATE: 테이블·뷰 등의 아무 컬럼(또는 특정 컬럼)을 갱신하게 해줘요. (실제로는 사소하지 않은 UPDATE 명령은 SELECT 권한도 필요해요. 어떤 행을 갱신할지 정하거나 새 값을 계산하려고 컬럼을 참조해야 하거든요.) SELECT ... FOR UPDATESELECT ... FOR SHARE도 SELECT 권한 외에 적어도 한 컬럼의 이 권한이 필요해요. 시퀀스에 대해서는 nextvalsetval 함수 사용을 허용하고, 대형 객체에 대해서는 쓰기나 잘라내기를 허용해요.
  • DELETE: 테이블·뷰 등에서 행 삭제를 허용해요. (실제로 사소하지 않은 DELETE도 어떤 행을 지울지 정하려고 컬럼을 참조해야 하므로 SELECT 권한이 필요해요.)
  • TRUNCATE: 테이블의 TRUNCATE를 허용해요.
  • REFERENCES: 테이블(또는 특정 컬럼)을 참조하는 외래 키 제약 생성을 허용해요. 이 권한을 부여할 때는 각별히 주의해야 해요. 외래 키를 만드는 사용자가 그 외래 키의 강제 실행이 캐스트 함수 같은 임의의 함수를 호출하도록 만들 수 있고, 그 함수들은 테이블 소유자의 권한으로 호출되거든요.
  • TRIGGER: 테이블·뷰 등에 트리거 생성을 허용해요. 역시 주의가 필요해요. 테이블·뷰에 추가된 트리거는 그것을 수정하는 사용자의 권한으로 실행되기 때문이에요.
  • CREATE: 데이터베이스에는 해당 DB 내에 새 스키마·출판물 생성과 신뢰할 수 있는(trusted) 확장 설치를 허용해요. 스키마에는 그 스키마 내에 새 객체 생성을 허용해요. 기존 객체 이름을 바꾸려면 그 객체를 소유하고 스키마에 대한 이 권한이 있어야 해요. 테이블스페이스에는 테이블·인덱스·임시 파일 생성과, 그 테이블스페이스를 기본으로 하는 데이터베이스 생성을 허용해요. 이 권한을 뺏어도 기존 객체의 존재나 위치는 바뀌지 않아요.
  • CONNECT: 수여자가 데이터베이스에 접속하게 해줘요. (pg_hba.conf 제한에 더해) 접속 시작 시 검사돼요.
  • TEMPORARY: 데이터베이스를 쓰는 동안 임시 테이블을 만들게 해줘요.
  • EXECUTE: 함수·프로시저 호출을 허용하고, 그 함수를 기반으로 구현된 연산자 사용도 포함해요. 함수·프로시저에 적용되는 유일한 권한이에요.
  • USAGE: 절차적 언어에 대해서는 그 언어로 함수를 만들 수 있게 해줘요(절차적 언어에 적용되는 유일한 권한). 스키마에 대해서는 스키마 내 객체에 접근하게 해줘요(객체 자신의 권한 요건도 충족된다는 전제하에요). 기본적으로 수여자가 스키마 내 객체를 '찾아볼 수' 있게 하는 거예요. 이 권한이 없어도 시스템 카탈로그 조회로 객체 이름은 보일 수 있어요. 또 뺏은 뒤에도 기존 세션이 이전에 조회한 문장을 가질 수 있으므로, 객체 접근을 막는 완전히 안전한 방법은 아니에요. 시퀀스에는 currvalnextval 함수 사용을 허용해요. 타입·도메인에는 타입·도메인을 테이블·함수 등 스키마 객체 생성에 사용하게 해줘요. (이 권한이 타입의 모든 '사용'을 통제하는 건 아니에요. 쿼리에서 타입 값이 나타나는 건 통제하지 못해요. 타입에 의존하는 객체가 생성되는 것만 막아요. 주 목적은 타입에 의존 관계를 만들 수 있는 사용자를 통제해서, 나중에 소유자가 타입을 바꾸는 걸 막는 거예요.) 외부 데이터 래퍼(FDW)에는 그 래퍼로 새 서버를 만들게 해주고, 외부 서버에는 그 서버로 외부 테이블을 만들게 해줘요. 수여자는 그 서버와 관련된 자신의 사용자 매핑을 만들·바꿀·지울 수도 있어요.
  • SET: 현재 세션에서 서버 설정 매개변수를 새 값으로 설정하게 해줘요. (어느 매개변수에든 부여할 수 있지만, 보통 superuser 권한이 필요한 매개변수에서만 의미가 있어요.)
  • ALTER SYSTEM: ALTER SYSTEM 명령으로 서버 설정 매개변수를 새 값으로 설정하게 해줘요.
  • MAINTAIN: 관계(relation)에 VACUUM, ANALYZE, CLUSTER, REFRESH MATERIALIZED VIEW, REINDEX, LOCK TABLE, 데이터베이스 객체 통계 조작 함수를 허용해요.

다른 명령들이 요구하는 권한은 각 명령의 레퍼런스 문서에 나열돼 있어요.

기본 PUBLIC 권한

PostgreSQL은 일부 객체 유형에 대해 생성 시 PUBLIC에 기본 권한을 부여해요. 테이블, 테이블 컬럼, 시퀀스, 외부 데이터 래퍼, 외부 서버, 대형 객체, 스키마, 테이블스페이스, 설정 매개변수에는 기본적으로 PUBLIC에 권한이 부여되지 않아요. 다른 객체 유형의 기본 PUBLIC 권한은 다음과 같아요.

  • 데이터베이스: CONNECTTEMPORARY(임시 테이블 생성)
  • 함수·프로시저: EXECUTE
  • 언어·데이터 타입(도메인 포함): USAGE

물론 소유자는 기본 권한이든 명시적 권한이든 REVOKE할 수 있어요. (최대 보안을 위해 객체를 만드는 같은 트랜잭션에서 REVOKE를 실행하세요. 그러면 다른 사용자가 객체를 쓸 수 있는 창이 없어져요.) 또 ALTER DEFAULT PRIVILEGES 명령으로 기본 권한 설정을 재정의할 수 있어요.

ACL로 권한 읽기

ACL 값에는 권한 유형의 한 글자 약어가 쓰여요. 이 문자들은 아래 나열된 psql 명령의 출력이나 시스템 카탈로그의 ACL 컬럼에서 보게 돼요.

권한 약어 적용 객체 유형
SELECT r ("read") LARGE OBJECT, SEQUENCE, TABLE(테이블형 객체), 테이블 컬럼
INSERT a ("append") TABLE, 테이블 컬럼
UPDATE w ("write") LARGE OBJECT, SEQUENCE, TABLE, 테이블 컬럼
DELETE d TABLE
TRUNCATE D TABLE
REFERENCES x TABLE, 테이블 컬럼
TRIGGER t TABLE
CREATE C DATABASE, SCHEMA, TABLESPACE
CONNECT c DATABASE
TEMPORARY T DATABASE
EXECUTE X FUNCTION, PROCEDURE
USAGE U DOMAIN, FOREIGN DATA WRAPPER, FOREIGN SERVER, LANGUAGE, SCHEMA, SEQUENCE, TYPE
SET s PARAMETER
ALTER SYSTEM A PARAMETER
MAINTAIN m TABLE

객체 유형별 권한 요약 (모든 권한 / 기본 PUBLIC 권한 / 검사용 psql 명령):

객체 유형 모든 권한 기본 PUBLIC psql 명령
DATABASE CTc Tc \l
DOMAIN U U \dD+
FUNCTION/PROCEDURE X X \df+
FOREIGN DATA WRAPPER U 없음 \dew+
FOREIGN SERVER U 없음 \des+
LANGUAGE U U \dL+
LARGE OBJECT rw 없음 \dl+
PARAMETER sA 없음 \dconfig+
SCHEMA UC 없음 \dn+
SEQUENCE rwU 없음 \dp
TABLE (테이블형 객체) arwdDxtm 없음 \dp
테이블 컬럼 arwx 없음 \dp
TABLESPACE C 없음 \db+
TYPE U U \dT+

특정 객체에 부여된 권한은 aclitem 항목 목록으로 표시되는데, 각 항목은 다음 형식이에요.

grantee=privilege-abbreviation[*].../grantor

각 aclitem은 특정 grantor가 부여한, 한 grantee의 모든 권한을 나열해요. 특정 권한은 표의 한 글자 약어로 표시되고, 권한이 grant option과 함께 부여됐다면 *가 붙어요. 예를 들어 calvin=r*w/hobbes는 롤 calvin이 grant option(*)이 있는 SELECT(r) 권한과 부여 불가능한 UPDATE(w) 권한을 가지며, 둘 다 롤 hobbes가 부여했음을 의미해요. calvin이 같은 객체에 대해 다른 grantor가 부여한 권한도 있다면 그것은 별도의 aclitem 항목으로 나타나요. aclitem에서 비어 있는 grantee 필드는 PUBLIC을 뜻해요.

예를 들어 사용자 miriam이 테이블 mytable을 만들고 이렇게 권한을 부여했다고 해볼게요.

GRANT SELECT ON mytable TO PUBLIC;
GRANT SELECT, UPDATE, INSERT ON mytable TO admin;
GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw;

그러면 psql의 \dp 명령이 이렇게 보여요.

=> \dp mytable
                                  Access privileges
 Schema |  Name   | Type  |   Access privileges    |   Column privileges   | Policies
--------+---------+-------+------------------------+-----------------------+----------
 public | mytable | table | miriam=arwdDxtm/miriam+| col1:                +|
        |         |       | =r/miriam             +|   miriam_rw=rw/miriam |
        |         |       | admin=arw/miriam       |                       |
(1 row)

객체의 'Access privileges' 컬럼이 비어 있으면 기본 권한을 갖는다는 뜻이에요(관련 시스템 카탈로그의 권한 항목이 null이라는 뜻). 기본 권한은 항상 소유자의 모든 권한을 포함하고, 위에서 설명한 대로 객체 유형에 따라 PUBLIC에 대한 일부 권한을 포함할 수 있어요. 객체에 대한 첫 GRANTREVOKE는 기본 권한을 실체화한 다음(miriam=arwdDxt/miriam 같은) 지정된 요청대로 수정해요. 비슷하게 'Column privileges'에는 기본 권한이 아닌 컬럼의 항목만 표시돼요. (여기서 '기본 권한'은 항상 객체 유형의 내장 기본 권한을 의미해요. ALTER DEFAULT PRIVILEGES의 영향을 받은 객체는 그 효과를 포함한 명시적 권한 항목으로 항상 표시돼요.)

소유자의 암시적 grant option은 접근 권한 표시에 표시되지 않는다는 점에 주의하세요. *는 grant option이 누군가에게 명시적으로 부여됐을 때만 나타나요.

'Access privileges' 컬럼은 객체의 권한 항목이 null이 아니지만 비어 있을 때 (none)을 보여줘요. 이는 소유자에게조차 어떤 권한도 부여되지 않았다는 뜻 — 드문 상황이에요. (이 경우에도 소유자는 암시적 grant option을 가지므로 자신의 권한을 다시 부여할 수 있지만, 지금은 아무것도 없어요.)

더 알아보기 (Learn more)