- 來源 | 願碼(ChainDesk.CN)內容編輯
- 願碼Slogan | 鏈接每一個程序員的故事
- 網站 | http://chaindesk.cn
- 願碼願景 | 打造全學科IT系統免費課程,助力小白用戶、初級工程師0成本免費系統學習、低成本進階,幫助BAT一線資深工程師成長並利用自身優點創造睡後收入。
- 官方公衆號 | 願碼 | 願碼服務號 | 區塊鏈部落
- 免費加入願碼全思惟工程師社羣 | 任一公衆號回覆「願碼」兩個字獲取入羣二維碼
本文閱讀時長:17min程序員
在處理安全性時,記住這些級別以便以有組織的方式處理與安全相關的問題。sql
· 綁定地址:listen_addresses在文件中 postgresql.conf數據庫
· 基於主機的訪問控制:pg_hba.conf文件安全
· 實例級權限:用戶,角色,數據庫建立,登陸和複製服務器
· 數據庫級權限:鏈接,建立模式等網絡
· 架構級權限:使用架構並在架構內建立對象架構
· 表級權限:選擇,插入,更新等socket
· 列級權限:容許或限制對列的訪問post
· 行級安全性:限制對行的訪問學習
爲了讀取值, PostgreSQL必須確保咱們在每一個級別都有足夠的權限。整個權限鏈必須是正確的。
在本文中,您將學習處理SSL,列級安全性和配置默認權限等的過程。
配置PostgreSQL服務器時,須要作的第一件事就是定義遠程訪問。默認狀況下,PostgreSQL不接受遠程鏈接。重要的是因爲PostgreSQL不能監聽端口,因此致使它接收不到鏈接。若是咱們嘗試鏈接,錯誤消息實際上未來自操做系統。
假設有一個使用默認配置的數據庫服務器192.168.0.123,將發生如下狀況:
iMac:~ hs$ telnet 192.168.0.123 5432 Trying 192.168.0.123... telnet: connect to address 192.168.0.123: Connection refused telnet: Unable to connect to remote host
Telnet嘗試在端口上建立鏈接,5432並當即被遠程框拒絕。從外面看,看起來PostgreSQL根本就沒有運行。
成功鏈接的關鍵能夠在postgresql.conf文件中找到:
# - Connection Settings -
# shop_addresses ='localhost'
# 要監聽的IP地址;
# 逗號分隔的地址列表;
# defaults爲'localhost'; 使用'*'表示全部
# (更改須要重啓)
該listen_addresses設置將告訴PostgreSQL要監聽的地址。從技術上講,這些地址是綁定地址。這究竟意味着什麼?假設咱們的機器中有四個IP地址。咱們這些IP地址只監聽其中的三個。PostgreSQL只考慮這三個IP的請求,而不考慮第四個。
若是咱們放入 *,PostgreSQL會監聽分配給您機器的每一個IP。
可是,有更多與鏈接管理相關的設置對於理解很是重要。它們以下:
#port = 5432 # (change requires restart) max_connections = 100 # (change requires restart) # Note: Increasing max_connections costs ~400 bytes of # shared memory per # connection slot, plus lock space # (see max_locks_per_transaction). #superuser_reserved_connections = 3 # (change requires restart) #unix_socket_directories = '/tmp' # comma-separated list of directories # (change requires restart) #unix_socket_group = '' # (change requires restart) #unix_socket_permissions = 0777 # begin with 0 to use octal notation # (change requires restart)
首先,PostgreSQL偵聽單個TCP端口,默認值爲5432。請記住,PostgreSQL只會偵聽單個端口。每當有請求進入時,postmaster將建立一個新進程來處理鏈接。默認狀況下,最多容許100個普通鏈接。最重要的是,爲超級用戶保留了三個額外的鏈接。這意味着咱們能夠擁有97個鏈接加上三個超級用戶或100個超級用戶鏈接。
PostgreSQL容許咱們加密服務器和客戶端之間的傳輸。加密很是有用,特別是若是咱們進行遠距離通訊。SSL提供了一種簡單而安全的方式來確保沒有人可以收聽您的通訊。
在本節中,咱們將學習如何設置SSL。
首先要作的是在服務器啓動時將ssl參數設置on爲postgresql.conf文件。在下一步中,咱們能夠將SSL證書放入$PGDATA目錄中。若是咱們不但願證書位於其餘目錄中,請更改如下參數:
#ssl_cert_file = 'server.crt' # (change requires restart) #ssl_key_file = 'server.key' # (change requires restart) #ssl_ca_file = '' # (change requires restart) #ssl_crl_file = '' # (change requires restart)
若是咱們要使用自簽名證書,請執行如下步驟:
openssl req -new -text -out server.req
回答OpenSSL提出的問題。確保咱們輸入本地主機名做爲通用名稱。咱們能夠將密碼留空。此調用將生成一個受密碼保護的密鑰; 它不會接受長度少於四個字符的密碼短語。
要刪除密碼(若是要自動啓動服務器,則必須如此),請運行如下命令:
openssl rsa -in privkey.pem -out server.key rm privkey.pem
輸入舊密碼以解鎖現有密鑰。如今,執行此操做將證書轉換爲自簽名證書,並將密鑰和證書複製到服務器將查找的位置:
openssl req -x509 -in server.req -text -key server.key -out server.crt
執行此操做後,請確保文件具備正確的權限集:
chmod og-rwx server.key
一旦將適當的規則放入pg_hba.conf文件中,咱們就可使用SSL鏈接到您的服務器。要驗證咱們確實使用SSL,請考慮簽出該pg_stat_ssl功能。它將告訴咱們每一個鏈接以及它是否使用SSL,它將提供有關加密的一些重要信息:
test=# \d pg_stat_sslView "pg_catalog.pg_stat_ssl" Column | Type | Modifiers -------------+----------+----------- pid | integer | ssl | boolean | version | text | cipher | text | bits | integer | compression | boolean | clientdn | text |
若是ssl進程的字段包含true; PostgreSQL作了咱們指望它作的事情:
postgres=# select * from pg_stat_ssl; -[ RECORD 1 ] ---------------------------- pid | 20075 ssl | t version | TLSv1.2 cipher | ECDHE-RSA-AES256-GCM-SHA384 bits | 256 compression | f clientdn |
處理實例級安全性
到目前爲止,咱們已經配置了綁定地址,咱們告訴PostgreSQL使用哪一種IP範圍進行身份驗證。到目前爲止,配置純粹與網絡相關。
在下一步中,咱們能夠將注意力轉移到實例級別的權限。最重要的是要知道PostgreSQL中的用戶存在於實例級別。若是咱們建立一個用戶,它不只在一個數據庫中可見; 它能夠被全部數據庫看到。用戶可能只具備訪問單個數據庫的權限,但基本上用戶是在實例級別建立的。
對於那些剛接觸PostgreSQL的人來講,還有一件事要記住:用戶和角色是一回事。CREATE ROLE和CREATE USER子句有不一樣的默認值(字面上,惟一的區別是LOGIN默認狀況下角色沒有獲得屬性),但在一天結束時,用戶和角色是相同的。所以,CREATE ROLE和CREATE USER子句支持徹底相同的語法:
test=# \h CREATE USER Command: CREATE USER Description: define a new database role Syntax: CREATE USER name [ [ WITH ] option [ ... ] ] where option can be: SUPERUSER | NOSUPERUSER | CREATEDB | NOCREATEDB | CREATEROLE | NOCREATEROLE | INHERIT | NOINHERIT | LOGIN | NOLOGIN | REPLICATION | NOREPLICATION | BYPASSRLS | NOBYPASSRLS | CONNECTION LIMIT connlimit | [ ENCRYPTED ] PASSWORD 'password' | VALID UNTIL 'timestamp' | IN ROLE role_name [, ...] | IN GROUP role_name [, ...] | ROLE role_name [, ...] | ADMIN role_name [, ...] | USER role_name [, ...] | SYSID uid
讓咱們逐個討論這些語法元素。咱們首先看到的是用戶能夠是超級用戶或普通用戶。若是某人被標記爲a SUPERUSER ,則再也不有普通用戶必須面對的任何限制。A SUPERUSER 能夠根據須要刪除對象(數據庫等)。
下一個重要的事情是它在實例級別上獲取建立新數據庫的權限。
規則是這樣的:建立者老是自動成爲對象的全部者(除非另有說明,不然可使用該CREATE DATABASE子句)。美麗的是,對象全部者也能夠再次丟棄一個對象。
下一個重要的是INHERIT/ NOINHERITclause。若是INHERIT設置了子句(這是默認值),則用戶能夠繼承其餘用戶的權限。使用繼承權限容許咱們使用角色,這是抽象權限的好方法。例如,咱們能夠建立角色bookkeeper並使許多其餘角色繼承bookkeeper。咱們的想法是bookkeeper,即便咱們有不少人從事會計工做,咱們也只須要告訴PostgreSQL一次容許作什麼。
該LOGIN/ NOLOGIN子句定義一個角色是否能夠登陸到該實例。
在理論介紹以後,是時候實際建立用戶並看看在實際示例中如何使用事物:
test=# CREATE ROLE bookkeeper NOLOGIN; CREATE ROLE test=# CREATE ROLE joe LOGIN; CREATE ROLE test=# GRANT bookkeeper TO joe; GRANT ROLE
這裏作的第一件事bookkeeper是建立一個名爲的角色。
請注意,咱們不但願人們以身份登陸bookkeeper,所以角色標記爲NOLOGIN。
另請注意,NOLOGIN若是使用該CREATE ROLE子句,則爲默認值。若是您更喜歡該CREATE USER子句,則默認設置爲LOGIN。
而後,joe建立角色並標記爲LOGIN。最後,將bookkeeper角色分配給joe角色,以便他能夠執行bookkeeper實際容許的全部操做。
一旦用戶到位,咱們能夠測試到目前爲止咱們擁有的內容:
[hs@zenbook ~]$ psql test -U bookkeeper psql: FATAL: role "bookkeeper" is not permitted to log in
正如所料,該bookkeeper角色不容許登陸系統。若是joe角色嘗試登陸會發生什麼?
[hs@zenbook ~]$ psql test -U joe ... test=>
這實際上將按預期工做。但請注意,命令提示符已更改。這只是PostgreSQL向您顯示您未以超級用戶身份登陸的一種方式。
建立用戶後,可能須要對其進行修改。咱們可能想要改變的一件事是密碼。在PostgreSQL中,容許用戶更改本身的密碼。下面是它的工做原理:
test=> ALTER ROLE joe PASSWORD 'abc'; ALTER ROLE test=> SELECT current_user; current_user -------------- joe (1 row)
該ALTER ROLE條款(或ALTER USER)將容許咱們改變它能夠建立用戶時設置大多數設置。可是,管理用戶還有不少。在許多狀況下,咱們但願爲用戶分配特殊參數。該ALTER USER條款爲咱們提供了這樣作的方法:
ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] SET configuration_parameter { TO | = } { value | DEFAULT } ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] SET configuration_parameter FROM CURRENT ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] RESET configuration_parameter ALTER ROLE { role_specification | ALL } [ IN DATABASE database_name ] RESET ALL
語法很是簡單,很是簡單。爲了描述爲何這很是有用,我添加了一個真實的例子。讓咱們假設Joe碰巧住在毛里求斯島上。當他登陸時,即便他的數據庫服務器位於歐洲,他也但願本身在他本身的時區:
test=> ALTER ROLE joe SET TimeZone = 'UTC-4'; ALTER ROLE test=> SELECT now(); now ------------------------------- 2017-01-09 20:36:48.571584+01 (1 row) test=> q [hs@zenbook ~]$ psql test -U joe ... test=> SELECT now(); now ------------------------------- 2017-01-09 23:36:53.357845+04 (1 row)
該ALTER ROLE子句將修改用戶。一旦joe從新鏈接,就會爲他設置時區。
時區不會當即更改。您應該從新鏈接或使用SET ... TO DEFAULT子句。
這裏重要的是這對於某些內存參數也是可能的,例如work_mem等等。
在實例級別配置用戶以後,能夠深刻挖掘並查看在數據庫級別能夠執行的操做。出現的第一個主要問題是:咱們明確容許Joe登陸數據庫實例,可是誰或什麼容許Joe實際鏈接到其中一個數據庫?也許咱們不但願Joe訪問系統中的全部數據庫。限制對某些數據庫的訪問正是咱們在此級別上能夠實現的目標。
對於數據庫,可使用GRANT子句設置如下權限:
GRANT { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] } ON DATABASE database_name [, ...] TO role_specification [, ...] [ WITH GRANT OPTION ]
數據庫級別有兩個主要權限值得密切關注:
· CREATE:這容許某人在數據庫中建立模式。請注意,CREATE子句不容許建立表; 它是關於模式的。在PostgreSQL中,表位於模式中,所以您必須首先進入模式級別才能建立表。
· CONNECT:這容許有人鏈接到數據庫。
如今的問題是:沒有人明確CONNECT爲joe角色分配權限,那麼這些權限實際上來自何處?答案是:有一個叫作的東西public,相似於Unix世界。若是世界被容許作某事,那麼joe,誰是通常公衆的一部分。
最重要的是,public它不是一個角色,它能夠被刪除和重命名。咱們能夠簡單地將其視爲系統中每一個人的等價物。
所以,爲了確保不是每一個人均可以隨時鏈接到任何數據庫,CONNECT可能必須從公衆中撤銷。爲此,咱們能夠以超級用戶身份進行鏈接並解決問題:
[hs@zenbook ~]$ psql test -U postgres ... test=# REVOKE ALL ON DATABASE test FROM public; REVOKE test=# \q [hs@zenbook ~]$ psql test -U joe psql: FATAL: permission denied for database "test" DETAIL: User does not have CONNECT privilege.
咱們能夠看到,該joe角色再也不容許鏈接。此時,只有超級用戶才能進行測試。
一般,postgres 即便在建立其餘數據庫以前,最好仍是從數據庫中撤消權限。這個概念背後的想法是,這些權限將再也不存在於全部新建立的數據庫中。若是某人須要訪問某個數據庫,則必須明確授予權限。權利再也不自動存在。
若是咱們想容許joe角色鏈接到測試數據庫,請以超級用戶身份嘗試如下行:
[hs@zenbook ~]$ psql test -U postgres ... test=# GRANT CONNECT ON DATABASE test TO bookkeeper; GRANT test=# \q [hs@zenbook ~]$ psql test -U joe ... test=>
基本上,這裏有兩種選擇:
· 咱們能夠joe直接容許角色,以便只有joe角色才能鏈接。
· 或者,咱們能夠爲該bookkeeper角色授予權限。請記住,joe角色將繼承角色的全部權限bookkeeper,所以,若是咱們但願全部會計師都可以鏈接到數據庫,則爲角色分配權限bookkeeper彷佛是一個有吸引力的想法。
若是咱們爲該bookkeeper角色授予權限,則它沒有風險,由於該角色不容許首先登陸到該實例,所以它純粹做爲權限來源。
在某些狀況下,並非每一個人均可以看到全部數據。想象一下銀行,有些人可能會看到有關銀行賬戶的所有信息,而其餘人可能僅限於數據的一部分。在現實世界的狀況下,可能不容許某人閱讀餘額欄,或者有人可能看不到人民貸款的利率。
另外一個例子是,人們能夠看到人們的我的資料,但不能看到他們的照片或其餘私人信息。如今的問題是:如何使用列級安全性?
爲了證實這一點,咱們將向屬於該joe角色的現有表添加一列:
test=> ALTER TABLE t_useful ADD COLUMN name text; ALTER TABLE
該表如今由兩列組成。該示例的目標是確保用戶只能看到其中一列:
test=> \d t_useful Table "public.t_useful" Column | Type | Modifiers --------+---------+----------- id | integer | name | text |
做爲超級用戶,讓咱們建立一個用戶並讓它訪問包含咱們表的模式:
test=# CREATE ROLE paul LOGIN; CREATE ROLE test=# GRANT CONNECT ON DATABASE test TO paul; GRANT test=# GRANT USAGE ON SCHEMA public TO paul; GRANT
CONNECT被撤銷了public。所以,明確授予絕對是必要的,以確保咱們甚至能夠到達桌面。
該SELECT權限能夠賦予paul角色:
test=# GRANT SELECT (id) ON t_useful TO paul; GRANT
基本上,這已經足夠了。已經能夠以用戶身份鏈接到數據庫paul並閱讀該列:
[hs@zenbook ~]$ psql test -U paul ... test=> SELECT id FROM t_useful; id ---- (0 rows)
若是咱們使用列級權限,請記住一件重要的事情,咱們應該中止使用SELECT *,由於它再也不起做用了:
test=> SELECT * FROM t_useful; ERROR: permission denied for relation t_useful
到目前爲止,已經配置了不少東西。若是將新表添加到系統會發生什麼?逐個處理這些表並設置適當的權限可能會很是痛苦和風險。若是這些事情會自動發生,那不是很好嗎?這正是該ALTER DEFAULT PRIVILEGES條款的做用。這個想法是爲用戶提供一個選項,讓PostgreSQL在對象出現後當即自動設置所需的權限。有人不能再忘記設置這些權利了。
如下清單顯示了語法規範的第一部分:
postgres=# \h ALTER DEFAULT PRIVILEGES Command: ALTER DEFAULT PRIVILEGES Description: define default access privileges Syntax: ALTER DEFAULT PRIVILEGES [ FOR { ROLE | USER } target_role [, ...] ] [ IN SCHEMA schema_name [, ...] ] abbreviated_grant_or_revoke where abbreviated_grant_or_revoke is one of: GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER } [, ...] | ALL [ PRIVILEGES ] } ON TABLES TO { [ GROUP ] role_name | PUBLIC } [, ...] [ WITH GRANT OPTION ] ...
基本上,語法與GRANT子句相似,所以使用起來簡單直觀。爲了向咱們展現它的工做原理,我編寫了一個簡單的例子。咱們的想法是,若是joe角色建立了一個表,該paul角色將自動使用它:
test=# ALTER DEFAULT PRIVILEGES FOR ROLE joe IN SCHEMA public GRANT ALL ON TABLES TO paul; ALTER DEFAULT PRIVILEGES
讓咱們joe如今做爲角色鏈接並建立一個表:
[hs@zenbook ~]$ psql test -U joe ... test=> CREATE TABLE t_user (id serial, name text, passwd text); CREATE TABLE
做爲paul角色鏈接將證實該表已分配給適當的權限集:
[hs@zenbook ~]$ psql test -U paul ... test=> SELECT * FROM t_user; id | name | passwd ----+------+-------- (0 row