PostgreSQL 권한 구조

최근 PostgreSQL 권한 구조를 정리하면서 겪은 일들 학습한 것들에 대해서 정리하고자 한다. 여러가지를 테스트해보면서 느낀 것은 PostgreSQL 권한 과 관련된 컴포넌트가 몇 가지 존재하고 각 컴포넌트들에는 제약사항들이 존재한다. 그래서 이 컴포넌트들과 제약사항들을 먼저 이해하고 각자 상황에 맞는 PostgreSQL 권한 구조를 만드는 것이 가장 좋은 것 같다. 그렇다면 어떤 컴포넌트들과 제약사항이 있는지 살펴보자.

Role

가장 기본이 되는 컨셉 중 하나가 바로 Role이다. 조금 헷갈릴 수도 있는 부분 중에 하나가 PostgreSQL에서는 사용자와 그룹을 구분하지 않는다. 이 두가지 모두를 Role이라고 표현한다. 예를 들어 다음과 같이

CREATE ROLE foo;

foo 라는 Role 생성한다면 foo 는 사용자가 될 수도 있고 그룹이 될 수도 있다.

Membership

그룹이라는 개념이 필요한 이유는 관리의 편의성 때문인데 예를 들어 개인에게 어떤 권한을 주는 것보다 그룹에 어떤 권한을 부여하고 개인은 해당 그룹의 권한을 사용할 수 있도록 하는게 훨씬 편하다. 이것을 조금 다르게 보면 어떤 Role의 권한을 다른 Role이 상속받는다고 생각할 수도 있다.

PostgreSQL 권한 에서 Role을 이해할 때 개인, 그룹이라는 개념보다는 마치 Parent Class와 Child Class처럼 상속의 개념으로 바라보면 이해하기가 훨씬 편하다. PostgreSQL에서는 Parent를 Membership이라 표현하고 Child를 Member라고 표현한다. 특정 Role을 다른 Role의 권한을 주기 위해서는 다음과 같이 하면 된다.

GRANT viewer TO "[email protected]";

viewer 가 마치 그룹처럼 보이지만 [email protected] 와 동일하게 둘 다 Role이다. psql에서 \du 를 해보면 다음과 같이 표현된다.

       Role name      |       Attributes      |       Member of                        
----------------------+-----------------------+---------------------
 viewer               |                       | {}
 [email protected]     |                       | {viewer}

[email protected] Role이 viewer Role을 상속받고 있는 것이다.

그림 1. [email protected] Role이 viewer Role을 상속받고 있는 것이다.

Role은 디비 권한을 부여할 대상을 표현한 것이다. 그렇다면 Role에는 어떤 권한을 부여할 수 있을까?

Attribute

Role에는 Attribute와 Privilege를 부여할 수 있다. Attribute는 데이터베이스 시스템과 관련된 권한 혹은 속성을 나타낸 것이다. 예를 들면 해당 Role을 통해 DBMS에 로그인을 할 수 있는지, 비밀번호에 대한 정보라든지, 혹은 해당 계정이 데이터베이스를 생성할 수 있는지와 같은 권한을 말한다. 다음과 같이 Role에 Attribute를 부여할 수 있다.

CREATE ROLE foo CREATEROLE;
ALTER ROLE bar NOCREATEDB;

Role에는 하나 이상의 Attribute를 부여할 수 있다. 그림2의 경우는 foo 라는 Role에 CREATEDB, LOGIN 이라는 Attribute를 부여한 것이다. 그 밖의 여러가지 Attribute가 존재하며 이에 대한 설명은 공식 문서에서 찾아볼 수 있다.

PostgreSQL 권한 Role foo has CREATEDB, LOGIN attribute
그림 2. foo Role이 CREATEDB, LOGIN Attribute를 가지고 있다.

Privilege

Privilege는 특정 데이터베이스와 object에 대한 권한이다. object라고 하면 특정 데이터베이스의 테이블(Table), 함수(Function), 뷰(View) 와 같은 것들이 있다. Privilege를 이용하면 특정 Role에게 데이터베이스 혹은 object를 컨트롤할 수 있는 권한을 부여할 수 있다. 다음과 같이 Privilege를 부여할 수 있다.

GRANT SELECT ON TABLE accounts TO foo;
GRANT CREATE ON DATABASE service TO bar;

그림 3에서는 foo 라는 Role에 SELECT ALL TABLE 이라는 Privilege를 부여해준 것이다. 이 밖에도 다양하게 GRANT 를 활용할 수 있고 이는 공식 문서에서 찾아볼 수 있다.

PostgreSQL 권한 Role foo has SELECT ALL TABLE Privilege
그림 3. foo Role이 SELECT ALL TABLE Privilege를 가지고 있다.

Owner

하나 또 중요한 컨셉이 있는데 바로 owner라는 개념이다. PostgreSQL object에는 owner가 존재한다. owner는 object에 대한 모든 Privilege를 행사할 수 있다. owner는 해당 object를 만든 Role이 된다. owner가 아닌 다른 Role이 해당 object에 Privilege를 행사하기 위해서는 위에서 살펴본 GRANT 를 통해서 Privilege를 부여받아야한다.

또한 owner는 기본적으로 해당 object의 속성을 업데이트하거나 object를 삭제할 수 있다. 그리고 owner는 스스로 자신의 Privilege를 없애버릴 수 있다.

object의 owner를 바꿀 수 있다. 다만 owner를 바꾸기 위해서는 두 가지 조건을 만족해야 한다. 첫 번째로 바꾸려고하는 Role이 object의 owner이거나 혹은 object의 owner인 Role에 속해있어야 한다. 두 번째로는 바꾸려고 하는 Role에 바꾸는 Role이 속해 있어야 한다.

owner가 중요한 이유가 또 하나 있다. object에 대해 Privilege를 부여하거나 회수하기 위해서는 먼저 Privilege를 부여하는 Role은 object의 owner이거나 owner의 Member이어야 한다. 두 번째 조건은 Privilege를 부여하는 Role이 CREATEROLE Attribute를 가지고 있어야 한다. 이 두 가지를 모두 만족해야 다른 Role에게 object의 Privilege를 부여할 수 있다.

Example

여기까지해서 기본적인 컨셉과 제약사항들에 대해서 살펴보았다. 이제 조금 더 복합적으로 생각해보기 위해서 한 가지 간단한 시나리오를 가정하고 이럴 때는 어떻게 하면 좋을지에 대해서 살펴보자.

관리자

이제 만드려고하는 어플리케이션에 디비를 붙이기 위해서 PostgreSQL 인스턴스를 하나 띄웠다. 그러면 이제 당연하게 데이터베이스를 만들고 권한을 부여할 수 있는 관리자가 필요하다. 관리자는 로그인을 할 수 있어야하고 데이터베이스 생성할 수 있어야하며 다른 사용자들에게 권한을 부여할 수 있어야 한다. 이런 경우 다음과 같이 관리자에 해당하는 Role을 생성할 수 있을 것이다.

CREATE ROLE "[email protected]" CREATEDB CREATEROLE LOGIN PASSWORD 'xxx';
admin@example.io Role has CREATEROLE, CREATEDB, LOGIN Attributes
그림 4. 관리자가 CREATEROLE, CREATEDB, LOGIN Attribute를 가지고 있다.

어플리케이션, 마이그레이션 도구

어플리케이션은 디비와 연결하여 테이블에 데이터를 읽고 쓸 수 있어야 한다. 그리고 데이터를 업데이트하고 필요하다면 삭제도 할 수 있어야 한다. 이 때 어플리케이션이 사용하는 테이블에 대한 스키마는 별도의 마이그레이션 도구를 이용하여 만들어진다. 마이그레이션 도구는 테이블의 스키마를 변경할 수 있어야 하며 또한 새로운 테이블을 만들 수도 있을 것이다. 그리고 테이블 간에 JOIN을 하기 위해 foreign key를 생성할 수도 있어야 한다.

이제 위의 요구 사항을 PostgreSQL 관점에서 살펴보자. 마이그레이션 도구는 별다른 설정을 하지 않으면 생성된 테이블의 owner가 될 것이다. 어플리케이션은 생성된 테이블에 데이터를 읽고 쓸 수 있으려면 두 가지 방법이 있다. 첫 번째는 마이그레이션 도구 Role의 Member가 되는 방법이 있고 두 번째는 GRANT 를 통해서 테이블에 대한 Privilege를 얻을 수 있을 것이다. 여기서는 좀 더 구체적으로 권한을 통제하기 위해 두 번째 방법을 선택하기로 하자.

application, migration_tool privilege, attribute structure
그림 5. 어플리케이션, 마이그레이션 도구 Role 구조

migration_tool 의 경우 마이그레이션 도구가 직접 인스턴스에 붙을 수 있어야 하기 때문에 LOGIN 속성이 필요하다. 어플리케이션의 경우에는 applications 라는 Membership을 만들었는데 이는 만약 MSA 구조로 전체 서비스를 구성한다면 한 인스턴스에 여러 어플리케이션이 연결될 것이다. 이런 때를 대비해서 applications 라는 Membership에 여러 마이크로서비스가 Member로 포함될 수 있을 것이다. 지금은 service_x 라는 Role이 속해있다.

// 마이그레이션 도구
CREATE ROLE migration_tool LOGIN PASSWORD 'xxx';

// 어플리케이션
CREATE ROLE applications;
CREATE ROLE service_x LOGIN PASSWORD 'xxx';
GRANT applications TO service_x;

이제 applications Role 에 Privilege를 부여해주면 되는데 아래와 같이 부여한다면 문제가 생긴다.

GRANT SELECT, INSERT, UPDATE, DELETE
	ON ALL TABLES IN SCHEMA public
	TO applications; 

별로 문제가 없어보이지만 다음과 같은 상황에 문제가 생긴다. 위와 같이 Privilege를 부여한 시점 이후에 마이그레이션 도구가 새로운 테이블을 생성하게 된다면 해당 테이블에 대해서는 applications 이 Privilege를 가지지 못한다.

ALTER DEFAULT PRIVILEGES 를 이용하여 Role에 Privilege를 부여하면 이후부터 생기는 object에 대해서 해당 Role이 Privilege를 가지게 된다. 이제 위의 SQL은 아래와 같이 바뀌어야 한다.

ALTER DEFAULT PRIVILEGES
	IN SCHEMA public
	GRANT SELECT, INSERT, UPDATE, DELETE
	TO applications;

그런데 여전히 한 가지 문제가 남아있다. 이렇게 되면 관리자가 사용자들에게 마이그레이션 도구가 생성한 테이블에 대해 Privilege를 부여할 수 없게 된다. 이는 위의 owner에 대해 설명할 때 이야기 했다. 그렇기 때문에 관리자는 migration_tool 의 Member가 되어야 한다. 최종적으로 이를 정리하면 그림 6과 같다.

GRANT migration_tool TO "[email protected]";
admin, application, migration_tool Role structure
그림 6. 관리자, 어플리케이션, 마이그레이션 권한 구조

답글 남기기

이메일 주소는 공개되지 않습니다.