Golang에서 데이터베이스 자격 증명 순환 처리
소개
많은 분들이 데이터베이스 자격 증명 순환에 대해 들어 보셨을 것입니다. 데이터베이스 보안을 위한 일반적인 솔루션입니다. Hashicorp Vault, AWS Secret Manager 등과 같은 많은 공급자가 이 솔루션을 구현했습니다. 데이터베이스 자격 증명을 자동으로 교체하면 아무도 데이터베이스에 연결하는 방법을 알 수 없습니다. 실제로 안전하지만 또 다른 문제가 발생합니다. 데이터베이스 자격 증명을 교체하면 애플리케이션이 일정 시간이 지나면 데이터베이스에 연결하지 못할 수 있습니다. 특히 데이터베이스 최대 연결 수명을 선언할 때. 이 기사에서는 golang에서 프로그래밍 방식으로 데이터베이스 자격 증명을 회전하는 방법을 배웁니다.
커스텀 드라이버
이 로테이터를 일반 솔루션으로 구현하려면 기본 데이터베이스 드라이버(예: postgres, sqlite3, mysql 등)를 보유하는
customDriver struct
및 데이터베이스 자격 증명을 가져오는 데 사용할 Fetcher interface
를 생성해야 합니다.type Fetcher interface {
Fetch() (string, error)
}
type FetcherFunc func() (string, error)
func (f FetcherFunc) Fetch() (string, error) {
return f()
}
// customDriver implements the `sql.Driver` interface.
type customDriver struct {
// base is the base database driver
base driver.Driver
// fetcherFn is the function that will be used to fetch the database credential
fetcherFn FetcherFunc
}
func (d *customDriver) Open(_ string) (driver.Conn, error) {
// fetch the database credential
dsn, err := d.fetcherFn()
if err != nil {
return nil, err
}
// open the database connection using the fetched credential and base driver
return d.base.Open(dsn)
}
드라이버 등록 및 연결 열기
이제 드라이버를 사용하려면 드라이버를 등록하고 연결을 열어야 합니다.
func OpenWithRotator(name string, base driver.Driver, fetcher Fetcher) (*sql.DB, error) {
sql.Register(name, &customDriver{
base: base,
fetcherFn: fetcher.Fetch,
})
// you don't need to fill the dsn, it will be fetched from the fetcher.
return sql.Open(name, "")
}
Fetcher 인터페이스 구현
간단한 함수를 사용하여
Fetcher interface
를 구현해 봅시다. 데이터베이스 수명이 다할 때마다 다른 데이터베이스에 연결하려고 한다고 가정해 보겠습니다.var counter int
func simpleFetcher() (string, error) {
log.Println("fetcher called")
// add your custom logic
// e.g. fetching from vault / config / etc.
counter++
return fmt.Sprintf("file:foobar-%d.sqlite", counter), nil
}
로테이터로 연결 열기
열고 연결의 최대 수명을 2초로 설정합니다.
simpleFetcher
는 2초마다 호출됩니다.If your database credential is rotated faster than your connection lifetime, Golang still can use the old connection.
// you can adjust the `&sqlite3.SQLiteDriver{}` accordingly (e.g. &pq.Driver{}, etc.)
db, err := OpenWithRotator("foobar", &sqlite3.SQLiteDriver{}, FetcherFunc(simpleFetcher))
if err != nil {
log.Fatal(err)
}
defer db.Close()
db.SetConnMaxLifetime(2 * time.Second)
연결 테스트
연결이 작동하는지 간단히 테스트하려면 매초
Ping
메서드를 사용할 수 있습니다.for range time.Tick(time.Second) {
if err := db.Ping(); err != nil {
log.Fatal(err)
}
}
이제 프로그램을 실행하면 다음과 같은 출력이 표시됩니다.
$ go run .
2022/08/29 16:56:16 fetcher called
2022/08/29 16:56:19 fetcher called
2022/08/29 16:56:22 fetcher called
2022/08/29 16:56:25 fetcher called
2022/08/29 16:56:28 fetcher called
2022/08/29 16:56:31 fetcher called
다음은 sqlite3 데이터베이스 파일입니다.
$ ls | grep foobar-
foobar-1.sqlite
foobar-2.sqlite
foobar-3.sqlite
foobar-4.sqlite
foobar-5.sqlite
foobar-6.sqlite
전체 코드는 다음과 같습니다.
package main
import (
"database/sql"
"database/sql/driver"
"fmt"
"log"
"time"
"github.com/mattn/go-sqlite3"
)
type Fetcher interface {
Fetch() (string, error)
}
type FetcherFunc func() (string, error)
func (f FetcherFunc) Fetch() (string, error) {
return f()
}
// customDriver implements the `sql.Driver` interface.
type customDriver struct {
// base is the base database driver
base driver.Driver
// fetcherFn is the function that will be used to fetch the database credential
fetcherFn FetcherFunc
}
func (d *customDriver) Open(_ string) (driver.Conn, error) {
// fetch the database credential
dsn, err := d.fetcherFn()
if err != nil {
return nil, err
}
// open the database connection using the fetched credential and base driver
return d.base.Open(dsn)
}
func OpenWithRotator(name string, base driver.Driver, fetcher Fetcher) (*sql.DB, error) {
sql.Register(name, &customDriver{
base: base,
fetcherFn: fetcher.Fetch,
})
// you don't need to fill the dsn, it will be fetched from the fetcher.
return sql.Open(name, "")
}
var counter int
func simpleFetcher() (string, error) {
log.Println("fetcher called")
// add your custom logic
// e.g. fetching from vault / config / etc.
counter++
return fmt.Sprintf("file:foobar-%d.sqlite", counter), nil
}
func main() {
// you can adjust the `&sqlite3.SQLiteDriver{}` accordingly (e.g. &pq.Driver{}, etc.)
db, err := OpenWithRotator("foobar", &sqlite3.SQLiteDriver{}, FetcherFunc(simpleFetcher))
if err != nil {
log.Fatal(err)
}
defer db.Close()
db.SetConnMaxLifetime(2 * time.Second)
for range time.Tick(time.Second) {
if err := db.Ping(); err != nil {
log.Fatal(err)
}
}
}
결론
이것은 데이터베이스 로테이터의 간단한 구현입니다. 사용 사례에 따라 페처를 구현하는 것이 좋습니다.
읽어 주셔서 감사합니다!
Reference
이 문제에 관하여(Golang에서 데이터베이스 자격 증명 순환 처리), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/clavinjune/handle-database-credential-rotation-in-golang-cji텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)