Skip to content

Commit bcff64e

Browse files
committed
fix: sqlite c dependency
1 parent c089238 commit bcff64e

File tree

12 files changed

+2458
-1
lines changed

12 files changed

+2458
-1
lines changed

arch.dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
eleven git clone ${BUILD_SRC} v${APP_VERSION}; \
2727
sed -i 's/"development"/"v'${APP_VERSION}'"/' /go/netbird/version/version.go; \
2828
sed -i 's|"gorm.io/driver/sqlite"|"github.com/glebarez/sqlite"|' /go/netbird/management/server/geolocation/database.go; \
29-
sed -i 's|"gorm.io/driver/sqlite"|"github.com/glebarez/sqlite"|' /go/netbird/management/server/geolocation/store.go;
29+
sed -i 's|"gorm.io/driver/sqlite"|"github.com/glebarez/sqlite"|' /go/netbird/management/server/geolocation/store.go; \
30+
sed -i 's|"github.com/dexidp/dex/storage/sql"|"github.com/11notes/docker-netbird/build/go/dexidp/dex/storage/sql"|' /go/netbird/idp/dex/config.go; \
31+
sed -i 's|"github.com/dexidp/dex/storage/sql"|"github.com/11notes/docker-netbird/build/go/dexidp/dex/storage/sql"|' /go/netbird/idp/dex/provider.go;
3032

3133
RUN set -ex; \
3234
for BUILD in ${BUILD_ROOT}; do \
Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
package sql
2+
3+
import (
4+
"crypto/tls"
5+
"crypto/x509"
6+
"database/sql"
7+
"fmt"
8+
"log/slog"
9+
"net"
10+
"os"
11+
"regexp"
12+
"strconv"
13+
"strings"
14+
"time"
15+
16+
"github.com/go-sql-driver/mysql"
17+
"github.com/lib/pq"
18+
19+
"github.com/dexidp/dex/storage"
20+
)
21+
22+
const (
23+
// postgres error codes
24+
pgErrUniqueViolation = "23505" // unique_violation
25+
)
26+
27+
const (
28+
// MySQL error codes
29+
mysqlErrDupEntry = 1062
30+
mysqlErrDupEntryWithKeyName = 1586
31+
mysqlErrUnknownSysVar = 1193
32+
)
33+
34+
const (
35+
// postgres SSL modes
36+
pgSSLDisable = "disable"
37+
pgSSLRequire = "require"
38+
pgSSLVerifyCA = "verify-ca"
39+
pgSSLVerifyFull = "verify-full"
40+
)
41+
42+
const (
43+
// MySQL SSL modes
44+
mysqlSSLTrue = "true"
45+
mysqlSSLFalse = "false"
46+
mysqlSSLSkipVerify = "skip-verify"
47+
mysqlSSLCustom = "custom"
48+
)
49+
50+
// NetworkDB contains options common to SQL databases accessed over network.
51+
type NetworkDB struct {
52+
Database string
53+
User string
54+
Password string
55+
Host string
56+
Port uint16
57+
58+
ConnectionTimeout int // Seconds
59+
60+
// database/sql tunables, see
61+
// https://golang.org/pkg/database/sql/#DB.SetConnMaxLifetime and below
62+
// Note: defaults will be set if these are 0
63+
MaxOpenConns int // default: 5
64+
MaxIdleConns int // default: 5
65+
ConnMaxLifetime int // Seconds, default: not set
66+
}
67+
68+
// SSL represents SSL options for network databases.
69+
type SSL struct {
70+
Mode string
71+
CAFile string
72+
// Files for client auth.
73+
KeyFile string
74+
CertFile string
75+
}
76+
77+
// Postgres options for creating an SQL db.
78+
type Postgres struct {
79+
NetworkDB
80+
81+
SSL SSL `json:"ssl" yaml:"ssl"`
82+
}
83+
84+
// Open creates a new storage implementation backed by Postgres.
85+
func (p *Postgres) Open(logger *slog.Logger) (storage.Storage, error) {
86+
conn, err := p.open(logger)
87+
if err != nil {
88+
return nil, err
89+
}
90+
return conn, nil
91+
}
92+
93+
var strEsc = regexp.MustCompile(`([\\'])`)
94+
95+
func dataSourceStr(str string) string {
96+
return "'" + strEsc.ReplaceAllString(str, `\$1`) + "'"
97+
}
98+
99+
// createDataSourceName takes the configuration provided via the Postgres
100+
// struct to create a data-source name that Go's database/sql package can
101+
// make use of.
102+
func (p *Postgres) createDataSourceName() string {
103+
parameters := []string{}
104+
105+
addParam := func(key, val string) {
106+
parameters = append(parameters, fmt.Sprintf("%s=%s", key, val))
107+
}
108+
109+
addParam("connect_timeout", strconv.Itoa(p.ConnectionTimeout))
110+
111+
// detect host:port for backwards-compatibility
112+
host, port, err := net.SplitHostPort(p.Host)
113+
if err != nil {
114+
// not host:port, probably unix socket or bare address
115+
116+
host = p.Host
117+
118+
if p.Port != 0 {
119+
port = strconv.Itoa(int(p.Port))
120+
}
121+
}
122+
123+
if host != "" {
124+
addParam("host", dataSourceStr(host))
125+
}
126+
127+
if port != "" {
128+
addParam("port", port)
129+
}
130+
131+
if p.User != "" {
132+
addParam("user", dataSourceStr(p.User))
133+
}
134+
135+
if p.Password != "" {
136+
addParam("password", dataSourceStr(p.Password))
137+
}
138+
139+
if p.Database != "" {
140+
addParam("dbname", dataSourceStr(p.Database))
141+
}
142+
143+
if p.SSL.Mode == "" {
144+
// Assume the strictest mode if unspecified.
145+
addParam("sslmode", dataSourceStr(pgSSLVerifyFull))
146+
} else {
147+
addParam("sslmode", dataSourceStr(p.SSL.Mode))
148+
}
149+
150+
if p.SSL.CAFile != "" {
151+
addParam("sslrootcert", dataSourceStr(p.SSL.CAFile))
152+
}
153+
154+
if p.SSL.CertFile != "" {
155+
addParam("sslcert", dataSourceStr(p.SSL.CertFile))
156+
}
157+
158+
if p.SSL.KeyFile != "" {
159+
addParam("sslkey", dataSourceStr(p.SSL.KeyFile))
160+
}
161+
162+
return strings.Join(parameters, " ")
163+
}
164+
165+
func (p *Postgres) open(logger *slog.Logger) (*conn, error) {
166+
dataSourceName := p.createDataSourceName()
167+
168+
db, err := sql.Open("postgres", dataSourceName)
169+
if err != nil {
170+
return nil, err
171+
}
172+
173+
// set database/sql tunables if configured
174+
if p.ConnMaxLifetime != 0 {
175+
db.SetConnMaxLifetime(time.Duration(p.ConnMaxLifetime) * time.Second)
176+
}
177+
178+
if p.MaxIdleConns == 0 {
179+
db.SetMaxIdleConns(5)
180+
} else {
181+
db.SetMaxIdleConns(p.MaxIdleConns)
182+
}
183+
184+
if p.MaxOpenConns == 0 {
185+
db.SetMaxOpenConns(5)
186+
} else {
187+
db.SetMaxOpenConns(p.MaxOpenConns)
188+
}
189+
190+
errCheck := func(err error) bool {
191+
sqlErr, ok := err.(*pq.Error)
192+
if !ok {
193+
return false
194+
}
195+
return sqlErr.Code == pgErrUniqueViolation
196+
}
197+
198+
c := &conn{db, &flavorPostgres, logger, errCheck}
199+
if _, err := c.migrate(); err != nil {
200+
return nil, fmt.Errorf("failed to perform migrations: %v", err)
201+
}
202+
return c, nil
203+
}
204+
205+
// MySQL options for creating a MySQL db.
206+
type MySQL struct {
207+
NetworkDB
208+
209+
SSL SSL `json:"ssl" yaml:"ssl"`
210+
211+
// TODO(pborzenkov): used by tests to reduce lock wait timeout. Should
212+
// we make it exported and allow users to provide arbitrary params?
213+
params map[string]string
214+
}
215+
216+
// Open creates a new storage implementation backed by MySQL.
217+
func (s *MySQL) Open(logger *slog.Logger) (storage.Storage, error) {
218+
conn, err := s.open(logger)
219+
if err != nil {
220+
return nil, err
221+
}
222+
return conn, nil
223+
}
224+
225+
func (s *MySQL) open(logger *slog.Logger) (*conn, error) {
226+
cfg := mysql.Config{
227+
User: s.User,
228+
Passwd: s.Password,
229+
DBName: s.Database,
230+
AllowNativePasswords: true,
231+
232+
Timeout: time.Second * time.Duration(s.ConnectionTimeout),
233+
234+
ParseTime: true,
235+
Params: map[string]string{
236+
"transaction_isolation": "'SERIALIZABLE'",
237+
},
238+
}
239+
if s.Host != "" {
240+
if s.Host[0] != '/' {
241+
cfg.Net = "tcp"
242+
cfg.Addr = s.Host
243+
244+
if s.Port != 0 {
245+
cfg.Addr = net.JoinHostPort(s.Host, strconv.Itoa(int(s.Port)))
246+
}
247+
} else {
248+
cfg.Net = "unix"
249+
cfg.Addr = s.Host
250+
}
251+
}
252+
253+
switch {
254+
case s.SSL.CAFile != "" || s.SSL.CertFile != "" || s.SSL.KeyFile != "":
255+
if err := s.makeTLSConfig(); err != nil {
256+
return nil, fmt.Errorf("failed to make TLS config: %v", err)
257+
}
258+
cfg.TLSConfig = mysqlSSLCustom
259+
case s.SSL.Mode == "":
260+
cfg.TLSConfig = mysqlSSLTrue
261+
default:
262+
cfg.TLSConfig = s.SSL.Mode
263+
}
264+
265+
for k, v := range s.params {
266+
cfg.Params[k] = v
267+
}
268+
269+
db, err := sql.Open("mysql", cfg.FormatDSN())
270+
if err != nil {
271+
return nil, err
272+
}
273+
274+
if s.MaxIdleConns == 0 {
275+
/*Override default behavior to fix https://github.com/dexidp/dex/issues/1608*/
276+
db.SetMaxIdleConns(0)
277+
} else {
278+
db.SetMaxIdleConns(s.MaxIdleConns)
279+
}
280+
281+
err = db.Ping()
282+
if err != nil {
283+
if mysqlErr, ok := err.(*mysql.MySQLError); ok && mysqlErr.Number == mysqlErrUnknownSysVar {
284+
logger.Info("reconnecting with MySQL pre-5.7.20 compatibility mode")
285+
286+
// MySQL 5.7.20 introduced transaction_isolation and deprecated tx_isolation.
287+
// MySQL 8.0 doesn't have tx_isolation at all.
288+
// https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_transaction_isolation
289+
delete(cfg.Params, "transaction_isolation")
290+
cfg.Params["tx_isolation"] = "'SERIALIZABLE'"
291+
292+
db, err = sql.Open("mysql", cfg.FormatDSN())
293+
if err != nil {
294+
return nil, err
295+
}
296+
} else {
297+
return nil, err
298+
}
299+
}
300+
301+
errCheck := func(err error) bool {
302+
sqlErr, ok := err.(*mysql.MySQLError)
303+
if !ok {
304+
return false
305+
}
306+
return sqlErr.Number == mysqlErrDupEntry ||
307+
sqlErr.Number == mysqlErrDupEntryWithKeyName
308+
}
309+
310+
c := &conn{db, &flavorMySQL, logger, errCheck}
311+
if _, err := c.migrate(); err != nil {
312+
return nil, fmt.Errorf("failed to perform migrations: %v", err)
313+
}
314+
return c, nil
315+
}
316+
317+
func (s *MySQL) makeTLSConfig() error {
318+
cfg := &tls.Config{}
319+
if s.SSL.CAFile != "" {
320+
rootCertPool := x509.NewCertPool()
321+
pem, err := os.ReadFile(s.SSL.CAFile)
322+
if err != nil {
323+
return err
324+
}
325+
if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
326+
return fmt.Errorf("failed to append PEM")
327+
}
328+
cfg.RootCAs = rootCertPool
329+
}
330+
if s.SSL.CertFile != "" && s.SSL.KeyFile != "" {
331+
clientCert := make([]tls.Certificate, 0, 1)
332+
certs, err := tls.LoadX509KeyPair(s.SSL.CertFile, s.SSL.KeyFile)
333+
if err != nil {
334+
return err
335+
}
336+
clientCert = append(clientCert, certs)
337+
cfg.Certificates = clientCert
338+
}
339+
340+
mysql.RegisterTLSConfig(mysqlSSLCustom, cfg)
341+
return nil
342+
}

0 commit comments

Comments
 (0)