PostgreSQL via SSL com Golang
Criando o certificado
Primeiro vamos criar um certificado de testes, claro que quando for usar em produção você deve usar um certificado gerado por uma entidade certificadora reconhecida.
Com o comando abaixo vamos criar dois arquivos server.crt
e server.key
que vamos usar para configurar o PostgreSQL.
openssl req -new -x509 -days 365 -nodes -text -out server.crt \
-keyout server.key -subj "/CN=example.com"
Configurando PostgreSQL
Edite o arquivo postgresql.conf para ativar o uso da chave SSL.
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
Reinicie o serviço e teste a conexão com o seguinte comando
psql "sslmode=require"
Na string de conexão use slmode=verify-full
no lugar de sslmode=disable
. Como seu certificado não é assinado por uma entidade certificadora você vai precisar marcar ele como confiavel no seu sistema operacional.
Veja a documentação do PostgreSQL para ver mais exemplos de configuração.
Exemplo usando Go
No exemplo abaixo usei a função Connect do pacote sqlx que vai abrir o banco e em seguida tentar fazer ping no servidor, dessa forma ele vai imediatamente retornar um erro caso não consiga estabelecer uma conexão completa com o banco.
dbsource := "postgres://postgres:password@example.com/testdb?sslmode=verify-full"
conn, err := sqlx.Connect("postgres", dbsource)
if err != nil {
fmt.Printf("error open db: %v\n", err)
return
}
Bonus
As principais operações com PostgreSQL usando Go
Tabela simples para executar os exemplos:
CREATE TABLE public.clients
(
id integer NOT NULL DEFAULT nextval('clientes_id_seq'::regclass),
name character varying(200),
address text,
CONSTRAINT clientes_pkey PRIMARY KEY (id)
);
package main
import (
"fmt"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
)
func open(dbsource string) (db *sqlx.DB, err error) {
db, err = sqlx.Open("postgres", dbsource)
if err != nil {
err = fmt.Errorf("error open db: %v", err)
return
}
err = db.Ping()
if err != nil {
err = fmt.Errorf("error ping db: %v", err)
}
return
}
func main() {
/*************************
** Abre banco de dados **
*************************/
//dbsource := "postgres://postgres:password@example.com/testdb?sslmode=verify-full"
dbsource := "postgres://postgres@localhost/testdb?sslmode=disable"
db, err := open(dbsource)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(db.DriverName())
/************
** Insert **
************/
/*
Na maioria dos casos você pode usar tanto
Exec como Query, a diferença esta no retorno,
Query é mais adequado para quando você quer
ler linhas de retorno.
*/
// Insert simples
// -=-=-=-=-=-=-=
sql := `INSERT INTO "clients" ("name","address") VALUES ($1,$2)`
_, err = db.Exec(sql,
"Tyrell Corporation",
"TC Earth Headquarters")
if err != nil {
fmt.Println(err)
return
}
// named insert
// -=-=-=-=-=-=
type client struct {
Name string `json:"name" db:"name"`
Address string `json:"address" db:"address"`
}
namedSQL := `INSERT INTO "clients" ("name","address") VALUES (:name,:address)`
_, err = db.NamedExec(namedSQL,
client{
Name: "Cyberdyne Systems",
Address: "2144 Kramer St",
})
if err != nil {
fmt.Println(err)
return
}
// named insert e retorna ID
// -=-=-=-=-=-=-=-=-=-=-=-=-
id := 0
rows, err := db.NamedQuery(`INSERT INTO "clients" ("name","address") VALUES (:name,:address) RETURNING id`,
client{
Name: "Umbrella Corporation",
Address: "545 S Birdneck RD STE 202B Virginia Beach, VA 23451",
})
if err != nil {
fmt.Println(err)
return
}
for rows.Next() {
err = rows.Scan(&id)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("id", id)
}
err = rows.Close()
if err != nil {
fmt.Println(err)
return
}
// insert com transação
// -=-=-=-=-=-=-=-=-=-=
tx, err := db.Begin()
if err != nil {
fmt.Println(err)
return
}
_, err = tx.Exec(sql,
"OCP Omni Consumer Products",
"Delta City (formerly Detroit)")
if err != nil {
fmt.Println(err)
return
}
_, err = tx.Exec(sql,
"Weyland-Yutani Corporation",
"Weyland-Yutani Corporation HQ, Tokyo")
if err != nil {
fmt.Println(err)
return
}
_, err = tx.Exec(sql,
"GeneCo",
"401 N. Boonville Avenue Springfield")
if err != nil {
fmt.Println(err)
return
}
err = tx.Commit()
if err != nil {
fmt.Println(err)
return
}
// insert usando prepare
// -=-=-=-=-=-=-=-=-=-=-
stmt, err := db.Prepare(sql)
if err != nil {
fmt.Println(err)
return
}
_, err = stmt.Exec("Black Mesa", "Black Mesa, New Mexico, USA")
if err != nil {
fmt.Println(err)
return
}
err = stmt.Close()
if err != nil {
fmt.Println(err)
return
}
// MustExec (se der erro quebra tudo)
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
db.MustExec(sql,
"League of Industrial Nations",
"CON-AM 27, Io, Jupter")
db.MustExec(sql,
"Aperture Laboratories",
"Upper Michigan, USA")
/************
** Select **
************/
sql = `select "name", "address" from "clients" order by name`
// Select simples
// -=-=-=-=-=-=-=
r, err := db.Query(sql)
if err != nil {
fmt.Println(err)
return
}
// list := []client{}
for r.Next() {
c := client{} // nova instancia para conter o cliente
err = r.Scan(&c.Name, &c.Address) // popula nova instancia
if err != nil { // verifica se erro
fmt.Println(err)
return
}
fmt.Println("Nome....:", c.Name)
fmt.Println("Endereço:", c.Address)
fmt.Println("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
// list = append(list, c)
}
err = r.Close()
if err != nil {
fmt.Println(err)
return
}
// Select lendo item a item com StructScan
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
rows, err = db.Queryx(sql)
if err != nil {
fmt.Println(err)
return
}
// list := []client{}
for rows.Next() {
c := client{} // nova instancia para conter o cliente
err = rows.StructScan(&c) // popula nova instancia
if err != nil { // verifica se erro
fmt.Println(err)
return
}
fmt.Println("Nome....:", c.Name)
fmt.Println("Endereço:", c.Address)
fmt.Println("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
// list = append(list, c)
}
err = rows.Close()
if err != nil {
fmt.Println(err)
return
}
// Select lendo todos os itens de uma vez usando db.Select
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
list := []client{}
err = db.Select(&list, sql)
if err != nil {
fmt.Println(err)
return
}
for k, v := range list {
fmt.Println("Registro:", k+1) // não é o id :D
fmt.Println("Nome....:", v.Name)
fmt.Println("Endereço:", v.Address)
fmt.Println("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
}
// Select lendo apenas um item
// -=-=-=-=-=-=-=-=-=-=-=-=-=-
// limit 1
sql = `select "name", "address" from "clients" limit 1`
c := client{}
err = db.Get(&c, sql)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Nome....:", c.Name)
fmt.Println("Endereço:", c.Address)
fmt.Println("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
// count
sql = `select count(*) from "clients"`
count := 0
err = db.Get(&count, sql)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("count:", count)
fmt.Println("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
/***********
** Close **
***********/
err = db.Close()
if err != nil {
fmt.Println(err)
return
}
}