ลดความยุ่งยากในการใช้คลาวด์ พูดคุยกับเจ้าหน้าที่

Home เปิดให้เชื่อมต่อ PostgreSQL จากภายนอกด้วย pg_hba.conf

เปิดให้เชื่อมต่อ PostgreSQL จากภายนอกด้วย pg_hba.conf

ค่าเริ่มต้นของ PostgreSQL รับการเชื่อมต่อจากภายในเครื่องเท่านั้น เมื่อแอปพลิเคชันอยู่คนละเซิร์ฟเวอร์กับฐานข้อมูล หรือต้องให้ระบบรายงานจากสำนักงานดึงข้อมูล ต้องปรับค่าสองส่วน คือ listen_addresses ใน postgresql.conf เพื่อให้ PostgreSQL ฟังบน Network Interface ภายนอก และ pg_hba.conf เพื่อกำหนดว่าใครเชื่อมต่อจาก IP ใด เข้าฐานข้อมูลใด และยืนยันตัวตนด้วยวิธีใด

บทความนี้แสดงวิธีเปิดการเชื่อมต่อแบบจำกัดเฉพาะ IP ที่ไว้ใจได้ ใช้รหัสผ่านแบบ scram-sha-256 และบังคับเข้ารหัส SSL พร้อมตั้ง Firewall ให้สอดคล้องกัน

สิ่งที่ต้องเตรียม

  • PostgreSQL 14 ขึ้นไปที่ติดตั้งแล้ว ตาม ติดตั้ง PostgreSQL และสร้างผู้ใช้บน Ubuntu หรือบน AlmaLinux/Rocky Linux
  • สิทธิ์ sudo บนเซิร์ฟเวอร์ฐานข้อมูล
  • IP สาธารณะแบบคงที่ของเครื่องที่จะเชื่อมต่อเข้ามา ตัวอย่างในบทความใช้ 198.51.100.25 และเซิร์ฟเวอร์ฐานข้อมูลคือ 203.0.113.10
  • Role และฐานข้อมูลที่จะใช้ ตัวอย่างคือ appuser และ appdb

หมายเหตุ: ถ้าผู้ที่ต้องเข้าถึงคือผู้ดูแลระบบที่ต้องการเปิดดูข้อมูลเป็นครั้งคราว ไม่ใช่แอปพลิเคชัน ไม่จำเป็นต้องเปิดพอร์ต 5432 สู่ภายนอกเลย ใช้ SSH Tunnel ด้วย DBeaver แทนจะปลอดภัยกว่า

ขั้นตอนที่ 1: หาตำแหน่งไฟล์ตั้งค่า

ตำแหน่งไฟล์ต่างกันตามระบบปฏิบัติการ วิธีที่แน่นอนที่สุดคือถามจาก PostgreSQL เอง

sudo -u postgres psql -c "SHOW config_file;" -c "SHOW hba_file;"
ระบบตำแหน่งโดยทั่วไป
Ubuntu (แพ็กเกจของ Ubuntu หรือ PGDG)/etc/postgresql/16/main/postgresql.conf และ pg_hba.conf ในโฟลเดอร์เดียวกัน
AlmaLinux/Rocky (แพ็กเกจของ OS)/var/lib/pgsql/data/
AlmaLinux/Rocky (แพ็กเกจ PGDG)/var/lib/pgsql/16/data/

สำรองไฟล์ทั้งสองก่อนแก้ไข (แทนพาธด้วยค่าที่ได้จากคำสั่งข้างบน)

sudo cp /etc/postgresql/16/main/postgresql.conf /etc/postgresql/16/main/postgresql.conf.bak
sudo cp /etc/postgresql/16/main/pg_hba.conf /etc/postgresql/16/main/pg_hba.conf.bak

ขั้นตอนที่ 2: ตั้ง listen_addresses

เปิด postgresql.conf แล้วหาบรรทัด listen_addresses (ค่าเริ่มต้นถูก Comment ไว้และมีค่าเป็น localhost) แก้เป็น

listen_addresses = 'localhost,203.0.113.10'

การระบุ IP ของเซิร์ฟเวอร์เองจะฟังเฉพาะ Interface นั้น ถ้าเซิร์ฟเวอร์มีหลาย IP หรือ IP อาจเปลี่ยน ใช้ '*' เพื่อฟังทุก Interface ได้ แล้วให้ pg_hba.conf และ Firewall เป็นตัวจำกัดสิทธิ์ ค่านี้ต้อง รีสตาร์ต จึงมีผล การ Reload อย่างเดียวไม่พอ

sudo systemctl restart postgresql

บน AlmaLinux/Rocky ชื่อ Service อาจเป็น postgresql หรือ postgresql-16 ตามแหล่งที่ติดตั้ง ตรวจว่าฟังพอร์ตถูกต้องแล้ว

sudo ss -ltnp | grep 5432

ควรเห็นบรรทัดที่มี 203.0.113.10:5432 หรือ 0.0.0.0:5432 (ดูเพิ่มเติมที่ ตรวจสอบพอร์ตที่เปิดอยู่บน Linux ด้วย ss และ lsof)

ขั้นตอนที่ 3: เพิ่มกฎใน pg_hba.conf

แต่ละบรรทัดใน pg_hba.conf มี 5 คอลัมน์หลัก

คอลัมน์ตัวอย่างความหมาย
TYPEhostssllocal คือ Socket, host คือ TCP ทั้งแบบเข้ารหัสและไม่เข้ารหัส, hostssl คือ TCP ที่เข้ารหัส SSL เท่านั้น
DATABASEappdbชื่อฐานข้อมูล ใช้ all ได้แต่ไม่แนะนำ
USERappuserชื่อ Role
ADDRESS198.51.100.25/32IP หรือช่วง IP ในรูปแบบ CIDR, /32 คือ IP เดียว
METHODscram-sha-256วิธียืนยันตัวตน ไม่ควรใช้ trust หรือ password กับการเชื่อมต่อจากภายนอก

เพิ่มบรรทัดนี้ไว้ท้ายไฟล์

# แอปพลิเคชันจากเซิร์ฟเวอร์ 198.51.100.25
hostssl  appdb  appuser  198.51.100.25/32  scram-sha-256

PostgreSQL อ่านกฎจากบนลงล่างและใช้ บรรทัดแรกที่ตรงเงื่อนไข ทันที ถ้ามีกฎที่กว้างกว่าอยู่ด้านบน เช่น บรรทัดที่ปฏิเสธ (reject) ช่วง IP เดียวกัน กฎของคุณจะไม่ถูกใช้

ต่อมา Reload ค่า ซึ่งไม่ตัดการเชื่อมต่อที่มีอยู่

sudo systemctl reload postgresql
# หรือจากใน psql
sudo -u postgres psql -c "SELECT pg_reload_conf();"

ตรวจว่า PostgreSQL อ่านไฟล์ได้โดยไม่มีบรรทัดผิดรูปแบบ คอลัมน์ error ต้องว่างทุกแถว

sudo -u postgres psql -c "SELECT line_number, type, database, user_name, address, auth_method, error FROM pg_hba_file_rules;"

ขั้นตอนที่ 4: ตรวจสอบ SSL และรูปแบบรหัสผ่าน

กฎแบบ hostssl ต้องเปิด SSL ที่เซิร์ฟเวอร์ แพ็กเกจบน Ubuntu เปิดไว้ให้แล้วโดยใช้ Certificate แบบ Self-signed ส่วน AlmaLinux/Rocky ต้องเปิดเอง ตรวจสอบด้วย

sudo -u postgres psql -c "SHOW ssl;"

ถ้าได้ off ให้วางไฟล์ Certificate และ Private Key ในโฟลเดอร์ Data (ชื่อค่าเริ่มต้นคือ server.crt และ server.key ไฟล์ Key ต้องเป็นของผู้ใช้ postgres และสิทธิ์ 0600) แล้วตั้ง ssl = on ใน postgresql.conf และรีสตาร์ต

ต่อมาตรวจว่ารหัสผ่านของ Role ถูกเก็บแบบ SCRAM (ค่าเริ่มต้นตั้งแต่ PostgreSQL 14) ถ้าผลลัพธ์ขึ้นต้นด้วย md5 ให้ตั้งรหัสผ่านใหม่หนึ่งครั้งด้วย \password appuser ใน psql

sudo -u postgres psql -c "SELECT rolname, left(rolpassword, 14) FROM pg_authid WHERE rolname = 'appuser';"

ขั้นตอนที่ 5: เปิด Firewall เฉพาะ IP ที่อนุญาต

บน Ubuntu ที่ใช้ UFW (ดู ตั้งค่า Firewall ด้วย UFW บน Ubuntu)

sudo ufw allow from 198.51.100.25 to any port 5432 proto tcp
sudo ufw status numbered

บน AlmaLinux/Rocky ที่ใช้ firewalld (ดู ตั้งค่า Firewall ด้วย firewalld บน AlmaLinux และ Rocky Linux)

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.25/32" port port="5432" protocol="tcp" accept'
sudo firewall-cmd --reload

หากเซิร์ฟเวอร์มี Security Group หรือ Firewall ภายนอกจากฝั่งผู้ให้บริการ ให้เปิดพอร์ตนั้นเพิ่มด้วย โดยจำกัดต้นทางเป็น IP เดียวกัน ไม่ควรเปิดพอร์ต 5432 ให้ทุก IP (0.0.0.0/0) เพราะจะถูกสแกนและเดารหัสผ่านตลอดเวลา

ตรวจสอบผลลัพธ์

จากเครื่อง 198.51.100.25 ที่ติดตั้ง psql ไว้ ให้เชื่อมต่อโดยบังคับ SSL

psql "host=203.0.113.10 port=5432 dbname=appdb user=appuser sslmode=require"

เมื่อเข้าได้ ใช้ \conninfo จะเห็นข้อความระบุว่าเป็นการเชื่อมต่อ SSL พร้อมชื่อ Protocol เช่น TLSv1.3 ถ้าใช้ Certificate จาก CA จริงและต้องการตรวจชื่อเซิร์ฟเวอร์ด้วย ให้ใช้ sslmode=verify-full

ฝั่งเซิร์ฟเวอร์ ดูการเชื่อมต่อที่เข้ามาได้ด้วย

sudo -u postgres psql -c "SELECT a.usename, a.client_addr, s.ssl FROM pg_stat_activity a JOIN pg_stat_ssl s USING (pid) WHERE a.client_addr IS NOT NULL;"

ปัญหาที่พบบ่อย

FATAL: no pg_hba.conf entry for host "198.51.100.25", user "appuser", database "appdb", no encryption

PostgreSQL ได้รับการเชื่อมต่อแล้ว แต่ไม่มีกฎที่ตรง ข้อความ no encryption บอกว่า Client ไม่ได้ใช้ SSL ขณะที่กฎเป็น hostssl ให้เพิ่ม sslmode=require ฝั่ง Client หรือตรวจว่า IP ต้นทางที่แสดงใน Error ตรงกับที่ใส่ในกฎ (กรณีอยู่หลัง NAT IP อาจไม่ใช่ที่คาดไว้) และอย่าลืม Reload หลังแก้ไฟล์

Connection refused หรือ Connection timed out

Connection refused มักแปลว่า PostgreSQL ไม่ได้ฟังบน IP นั้น ให้ตรวจ listen_addresses และรีสตาร์ต ส่วน timed out มักเป็น Firewall บนเครื่องหรือ Firewall ภายนอกของผู้ให้บริการที่ยังไม่เปิด

FATAL: password authentication failed for user "appuser"

ตรวจว่ารหัสผ่านถูกต้อง และรหัสที่เก็บไว้เป็นแบบ SCRAM ตามขั้นตอนที่ 4 ถ้า Client เป็นไลบรารีรุ่นเก่ามากที่ไม่รองรับ SCRAM ให้อัปเดตไลบรารีแทนการลดระดับเป็น md5 รายละเอียดวิธียืนยันตัวตนทั้งหมดดูได้จากคู่มือทางการ The pg_hba.conf File

หากทำตามแล้วยังเชื่อมต่อไม่ได้ หรือต้องการตรวจสอบ Firewall ภายนอกของเซิร์ฟเวอร์ ติดต่อทีมงาน THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/

ผู้ให้บริการคลาวด์ไทย
เพื่อธุรกิจของคนไทย

"มุ่งมั่น" และ "มั่นคง"
พร้อมรับมือทุกการเติบโต
Trust Cloud
คลาว์ที่ปลอดภัย
คือรากฐานที่มั่นคง
cloud security