diff --git a/cmd/cgat/main.go b/cmd/cgat/main.go index 546d5ca13905edb36a1e2aef6c2166d5edbe8f38..e5b383b97317d85522c4e412616421236166cef3 100644 --- a/cmd/cgat/main.go +++ b/cmd/cgat/main.go @@ -8,6 +8,7 @@ import ( "tuxpa.in/a/zlog/log" + "pggat/lib/gat/modes/cloud_sql_discovery" "pggat/lib/gat/modes/digitalocean_discovery" "pggat/lib/gat/modes/pgbouncer" "pggat/lib/gat/modes/zalando" @@ -50,6 +51,18 @@ func main() { return } + if os.Getenv("PGGAT_GC_PROJECT") != "" { + conf, err := cloud_sql_discovery.Load() + if err != nil { + panic(err) + } + err = conf.ListenAndServe() + if err != nil { + panic(err) + } + return + } + if os.Getenv("PGGAT_DO_API_KEY") != "" { log.Printf("running in digitalocean discovery mode") diff --git a/go.mod b/go.mod index 17e750bd2d8c8c9cb6f5bb43bdf91170267f317f..e8bb04878239cc92e262f79e9ba90bcf9c719baa 100644 --- a/go.mod +++ b/go.mod @@ -8,12 +8,14 @@ require ( github.com/google/uuid v1.3.0 github.com/xdg-go/scram v1.1.2 github.com/zalando/postgres-operator v1.10.1 + google.golang.org/api v0.30.0 k8s.io/apimachinery v0.27.4 k8s.io/client-go v0.27.4 tuxpa.in/a/zlog v1.61.0 ) require ( + cloud.google.com/go v0.65.0 // indirect github.com/cristalhq/aconfig v0.18.3 // indirect github.com/cristalhq/aconfig/aconfigdotenv v0.17.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -23,11 +25,13 @@ require ( github.com/go-openapi/jsonreference v0.20.1 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.1.0 // indirect + github.com/googleapis/gax-go/v2 v2.0.5 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/imdario/mergo v0.3.6 // indirect @@ -47,6 +51,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect + go.opencensus.io v0.22.4 // indirect golang.org/x/crypto v0.8.0 // indirect golang.org/x/net v0.9.0 // indirect golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect @@ -55,6 +60,8 @@ require ( golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect + google.golang.org/grpc v1.47.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 2cab442610268da01c534c02fc398442c067962a..d512692cb614c240fd1aa43967ed43d83a745b66 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,7 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= @@ -35,12 +36,19 @@ gfx.cafe/util/go/gun v0.0.0-20230721185457-c559e86c829c h1:4XxKaHfYPam36FibTiy1T gfx.cafe/util/go/gun v0.0.0-20230721185457-c559e86c829c/go.mod h1:zxq7FdmfdrI4oGeze0MPJt9WqdkFj3BDDSAWRuB63JQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -60,7 +68,10 @@ github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -81,6 +92,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -101,6 +114,7 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= @@ -118,6 +132,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -136,10 +151,13 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= @@ -190,6 +208,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= @@ -229,7 +248,9 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -297,6 +318,7 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= @@ -345,6 +367,9 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -364,6 +389,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= @@ -436,6 +462,7 @@ google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -468,6 +495,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -475,6 +503,8 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -487,6 +517,11 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -499,6 +534,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -510,6 +547,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/lib/auth/credentials.go b/lib/auth/credentials.go index b8b2299429e286ee2e2929c69d96f6cc8de02888..5bb6b7fa89950b9ba71a5e104956a4e007a3e659 100644 --- a/lib/auth/credentials.go +++ b/lib/auth/credentials.go @@ -1,20 +1,30 @@ package auth type Credentials interface { - GetUsername() string + Credentials() } -type Cleartext interface { +type CleartextClient interface { Credentials EncodeCleartext() string +} + +type CleartextServer interface { + Credentials + VerifyCleartext(value string) error } -type MD5 interface { +type MD5Client interface { Credentials EncodeMD5(salt [4]byte) string +} + +type MD5Server interface { + Credentials + VerifyMD5(salt [4]byte, value string) error } @@ -32,11 +42,16 @@ type SASLVerifier interface { Write(bytes []byte) ([]byte, error) } -type SASL interface { +type SASLClient interface { + Credentials + + EncodeSASL(mechanisms []SASLMechanism) (SASLMechanism, SASLEncoder, error) +} + +type SASLServer interface { Credentials SupportedSASLMechanisms() []SASLMechanism - EncodeSASL(mechanisms []SASLMechanism) (SASLMechanism, SASLEncoder, error) VerifySASL(mechanism SASLMechanism) (SASLVerifier, error) } diff --git a/lib/auth/credentials/cleartext.go b/lib/auth/credentials/cleartext.go index 45821e07f726f6e2c4042040b0843e5feed5e356..4a00ee13b925111c0019f74db19277ad18756cb6 100644 --- a/lib/auth/credentials/cleartext.go +++ b/lib/auth/credentials/cleartext.go @@ -16,9 +16,7 @@ type Cleartext struct { Password string } -func (T Cleartext) GetUsername() string { - return T.Username -} +func (Cleartext) Credentials() {} func (T Cleartext) EncodeCleartext() string { return T.Password @@ -67,31 +65,6 @@ func (T Cleartext) SupportedSASLMechanisms() []auth.SASLMechanism { } } -type CleartextScramEncoder struct { - conversation *scram.ClientConversation -} - -func MakeCleartextScramEncoder(username, password string, hashGenerator scram.HashGeneratorFcn) (CleartextScramEncoder, error) { - client, err := hashGenerator.NewClient(username, password, "") - if err != nil { - return CleartextScramEncoder{}, err - } - - return CleartextScramEncoder{ - conversation: client.NewConversation(), - }, nil -} - -func (T CleartextScramEncoder) Write(bytes []byte) ([]byte, error) { - msg, err := T.conversation.Step(string(bytes)) - if err != nil { - return nil, err - } - return []byte(msg), nil -} - -var _ auth.SASLEncoder = CleartextScramEncoder{} - func (T Cleartext) EncodeSASL(mechanisms []auth.SASLMechanism) (auth.SASLMechanism, auth.SASLEncoder, error) { for _, mechanism := range mechanisms { switch mechanism { @@ -107,57 +80,6 @@ func (T Cleartext) EncodeSASL(mechanisms []auth.SASLMechanism) (auth.SASLMechani return "", nil, auth.ErrSASLMechanismNotSupported } -type CleartextScramVerifier struct { - conversation *scram.ServerConversation -} - -func MakeCleartextScramVerifier(username, password string, hashGenerator scram.HashGeneratorFcn) (CleartextScramVerifier, error) { - client, err := hashGenerator.NewClient(username, password, "") - if err != nil { - return CleartextScramVerifier{}, err - } - - kf := scram.KeyFactors{ - Iters: 4096, - } - stored := client.GetStoredCredentials(kf) - - server, err := hashGenerator.NewServer( - func(string) (scram.StoredCredentials, error) { - return stored, nil - }, - ) - if err != nil { - return CleartextScramVerifier{}, err - } - - return CleartextScramVerifier{ - conversation: server.NewConversation(), - }, nil -} - -func (T CleartextScramVerifier) Write(bytes []byte) ([]byte, error) { - msg, err := T.conversation.Step(string(bytes)) - if err != nil { - return nil, err - } - - if T.conversation.Done() { - // check if conversation params are valid - if !T.conversation.Valid() { - return nil, auth.ErrFailed - } - - // done - return []byte(msg), auth.ErrSASLComplete - } - - // there is more - return []byte(msg), nil -} - -var _ auth.SASLVerifier = CleartextScramVerifier{} - func (T Cleartext) VerifySASL(mechanism auth.SASLMechanism) (auth.SASLVerifier, error) { switch mechanism { case auth.ScramSHA256: @@ -168,6 +90,9 @@ func (T Cleartext) VerifySASL(mechanism auth.SASLMechanism) (auth.SASLVerifier, } var _ auth.Credentials = Cleartext{} -var _ auth.Cleartext = Cleartext{} -var _ auth.MD5 = Cleartext{} -var _ auth.SASL = Cleartext{} +var _ auth.CleartextClient = Cleartext{} +var _ auth.CleartextServer = Cleartext{} +var _ auth.MD5Client = Cleartext{} +var _ auth.MD5Server = Cleartext{} +var _ auth.SASLClient = Cleartext{} +var _ auth.SASLServer = Cleartext{} diff --git a/lib/auth/credentials/errors.go b/lib/auth/credentials/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..e41c6f2379aa814c2020d6406d25e7fe96d7b9ca --- /dev/null +++ b/lib/auth/credentials/errors.go @@ -0,0 +1,7 @@ +package credentials + +import "errors" + +var ( + ErrInvalidSecretFormat = errors.New("invalid secret format") +) diff --git a/lib/auth/credentials/md5.go b/lib/auth/credentials/md5.go index c96c48487a40b355e03dcba04ef97d4c0b66f7cc..bb891bf54505018e5b8c72ee37326b03e46ba034 100644 --- a/lib/auth/credentials/md5.go +++ b/lib/auth/credentials/md5.go @@ -10,14 +10,27 @@ import ( ) type MD5 struct { - Username string - Hash []byte + Hash []byte } -func (T MD5) GetUsername() string { - return T.Username +func MD5FromString(value string) (MD5, error) { + if !strings.HasPrefix(value, "md5") { + return MD5{}, ErrInvalidSecretFormat + } + + var res MD5 + var err error + hexString := strings.TrimPrefix(value, "md5") + res.Hash, err = hex.DecodeString(hexString) + if err != nil { + return MD5{}, err + } + + return res, nil } +func (MD5) Credentials() {} + func (T MD5) EncodeMD5(salt [4]byte) string { hexEncoded := make([]byte, hex.EncodedLen(len(T.Hash))) hex.Encode(hexEncoded, T.Hash) @@ -44,4 +57,6 @@ func (T MD5) VerifyMD5(salt [4]byte, value string) error { return nil } -var _ auth.MD5 = MD5{} +var _ auth.Credentials = MD5{} +var _ auth.MD5Client = MD5{} +var _ auth.MD5Server = MD5{} diff --git a/lib/auth/credentials/scram.go b/lib/auth/credentials/scram.go new file mode 100644 index 0000000000000000000000000000000000000000..431474419a8ecd306143f1da38d6611ceb628670 --- /dev/null +++ b/lib/auth/credentials/scram.go @@ -0,0 +1,89 @@ +package credentials + +import ( + "github.com/xdg-go/scram" + + "pggat/lib/auth" +) + +type ConversationScramEncoder struct { + conversation *scram.ClientConversation +} + +func MakeCleartextScramEncoder(username, password string, hashGenerator scram.HashGeneratorFcn) (auth.SASLEncoder, error) { + client, err := hashGenerator.NewClient(username, password, "") + if err != nil { + return nil, err + } + + return ConversationScramEncoder{ + conversation: client.NewConversation(), + }, nil +} + +func (T ConversationScramEncoder) Write(bytes []byte) ([]byte, error) { + msg, err := T.conversation.Step(string(bytes)) + if err != nil { + return nil, err + } + return []byte(msg), nil +} + +var _ auth.SASLEncoder = ConversationScramEncoder{} + +type ConversationScramVerifier struct { + conversation *scram.ServerConversation +} + +func MakeCleartextScramVerifier(username, password string, hashGenerator scram.HashGeneratorFcn) (auth.SASLVerifier, error) { + client, err := hashGenerator.NewClient(username, password, "") + if err != nil { + return nil, err + } + + kf := scram.KeyFactors{ + Iters: 4096, + } + stored := client.GetStoredCredentials(kf) + + return MakeStoredCredentialsScramVerifier(stored, hashGenerator) +} + +func MakeStoredCredentialsScramVerifier(credentials scram.StoredCredentials, hashGenerator scram.HashGeneratorFcn) (auth.SASLVerifier, error) { + server, err := hashGenerator.NewServer( + func(string) (scram.StoredCredentials, error) { + return credentials, nil + }, + ) + if err != nil { + return nil, err + } + + return ConversationScramVerifier{ + conversation: server.NewConversation(), + }, nil +} + +func (T ConversationScramVerifier) Write(bytes []byte) ([]byte, error) { + msg, err := T.conversation.Step(string(bytes)) + if err != nil { + return nil, err + } + + if T.conversation.Done() { + // check if conversation params are valid + if !T.conversation.Valid() { + return nil, auth.ErrFailed + } + + T.conversation.AuthzID() + + // done + return []byte(msg), auth.ErrSASLComplete + } + + // there is more + return []byte(msg), nil +} + +var _ auth.SASLVerifier = ConversationScramVerifier{} diff --git a/lib/auth/credentials/scram_sha_256.go b/lib/auth/credentials/scram_sha_256.go new file mode 100644 index 0000000000000000000000000000000000000000..cbc382c0874c45cb60a66d553b6feb98d2edf935 --- /dev/null +++ b/lib/auth/credentials/scram_sha_256.go @@ -0,0 +1,77 @@ +package credentials + +import ( + "encoding/base64" + "strconv" + "strings" + + "github.com/xdg-go/scram" + + "pggat/lib/auth" +) + +type ScramSHA256 struct { + StoredCredentials scram.StoredCredentials +} + +func ScramSHA256FromString(value string) (ScramSHA256, error) { + alg, iterKeys, ok := strings.Cut(value, "$") + if !ok || alg != "SCRAM-SHA-256" { + return ScramSHA256{}, ErrInvalidSecretFormat + } + iterSalt, keys, ok := strings.Cut(iterKeys, "$") + if !ok { + return ScramSHA256{}, ErrInvalidSecretFormat + } + iter, salt, ok := strings.Cut(iterSalt, ":") + if !ok { + return ScramSHA256{}, ErrInvalidSecretFormat + } + storedKey, serverKey, ok := strings.Cut(keys, ":") + + var res ScramSHA256 + var err error + res.StoredCredentials.Iters, err = strconv.Atoi(iter) + if err != nil { + return ScramSHA256{}, err + } + + var saltBytes []byte + saltBytes, err = base64.StdEncoding.DecodeString(salt) + if err != nil { + return ScramSHA256{}, err + } + res.StoredCredentials.Salt = string(saltBytes) + + res.StoredCredentials.StoredKey, err = base64.StdEncoding.DecodeString(storedKey) + if err != nil { + return ScramSHA256{}, err + } + + res.StoredCredentials.ServerKey, err = base64.StdEncoding.DecodeString(serverKey) + if err != nil { + return ScramSHA256{}, err + } + + return res, nil +} + +func (T ScramSHA256) SupportedSASLMechanisms() []auth.SASLMechanism { + return []auth.SASLMechanism{ + auth.ScramSHA256, + } +} + +func (T ScramSHA256) VerifySASL(mechanism auth.SASLMechanism) (auth.SASLVerifier, error) { + switch mechanism { + case auth.ScramSHA256: + return MakeStoredCredentialsScramVerifier(T.StoredCredentials, scram.SHA256) + default: + return nil, auth.ErrSASLMechanismNotSupported + } +} + +func (ScramSHA256) Credentials() {} + +var _ auth.Credentials = ScramSHA256{} +var _ auth.SASLServer = ScramSHA256{} diff --git a/lib/auth/credentials/string.go b/lib/auth/credentials/string.go index 4f04779d0e9164cc9873c7ce66c63005461a3bcb..cebd45dab79d1465743d488144f70185b1aefea8 100644 --- a/lib/auth/credentials/string.go +++ b/lib/auth/credentials/string.go @@ -1,32 +1,20 @@ package credentials import ( - "encoding/hex" - "strings" - "pggat/lib/auth" ) func FromString(user, password string) auth.Credentials { if password == "" { return nil - } else if strings.HasPrefix(password, "md5") { - hexHash := strings.TrimPrefix(password, "md5") - hash, err := hex.DecodeString(hexHash) - if err != nil { - return Cleartext{ - Username: user, - Password: password, - } - } - return MD5{ - Username: user, - Hash: hash, - } + } else if v, err := ScramSHA256FromString(password); err == nil { + return v + } else if v, err := MD5FromString(password); err == nil { + return v } else { return Cleartext{ Username: user, - Password: password, // TODO(garet) sasl + Password: password, } } } diff --git a/lib/bouncer/backends/v0/accept.go b/lib/bouncer/backends/v0/accept.go index d07616d003ba72360e8cd733c25997e159edf1f7..fe11b1698ad6c12795bb7d8e8d24f1562b93197a 100644 --- a/lib/bouncer/backends/v0/accept.go +++ b/lib/bouncer/backends/v0/accept.go @@ -51,7 +51,7 @@ func authenticationSASLChallenge(ctx *AcceptContext, encoder auth.SASLEncoder) ( } } -func authenticationSASL(ctx *AcceptContext, mechanisms []string, creds auth.SASL) error { +func authenticationSASL(ctx *AcceptContext, mechanisms []string, creds auth.SASLClient) error { mechanism, encoder, err := creds.EncodeSASL(mechanisms) if err != nil { return err @@ -86,7 +86,7 @@ func authenticationSASL(ctx *AcceptContext, mechanisms []string, creds auth.SASL return nil } -func authenticationMD5(ctx *AcceptContext, salt [4]byte, creds auth.MD5) error { +func authenticationMD5(ctx *AcceptContext, salt [4]byte, creds auth.MD5Client) error { pw := packets.PasswordMessage{ Password: creds.EncodeMD5(salt), } @@ -98,7 +98,7 @@ func authenticationMD5(ctx *AcceptContext, salt [4]byte, creds auth.MD5) error { return nil } -func authenticationCleartext(ctx *AcceptContext, creds auth.Cleartext) error { +func authenticationCleartext(ctx *AcceptContext, creds auth.CleartextClient) error { pw := packets.PasswordMessage{ Password: creds.EncodeCleartext(), } @@ -122,7 +122,7 @@ func authentication(ctx *AcceptContext) (done bool, err error) { err = errors.New("kerberos v5 is not supported") return case 3: - c, ok := ctx.Options.Credentials.(auth.Cleartext) + c, ok := ctx.Options.Credentials.(auth.CleartextClient) if !ok { return false, auth.ErrMethodNotSupported } @@ -134,7 +134,7 @@ func authentication(ctx *AcceptContext) (done bool, err error) { return } - c, ok := ctx.Options.Credentials.(auth.MD5) + c, ok := ctx.Options.Credentials.(auth.MD5Client) if !ok { return false, auth.ErrMethodNotSupported } @@ -156,7 +156,7 @@ func authentication(ctx *AcceptContext) (done bool, err error) { return } - c, ok := ctx.Options.Credentials.(auth.SASL) + c, ok := ctx.Options.Credentials.(auth.SASLClient) if !ok { return false, auth.ErrMethodNotSupported } @@ -272,7 +272,7 @@ func enableSSL(ctx *AcceptContext) (bool, error) { } func Accept(ctx *AcceptContext) (AcceptParams, error) { - username := ctx.Options.Credentials.GetUsername() + username := ctx.Options.Username if ctx.Options.Database == "" { ctx.Options.Database = username diff --git a/lib/bouncer/backends/v0/options.go b/lib/bouncer/backends/v0/options.go index b75821792c0eb02ee912423a2a954d8e14fe22bf..b05b8ba5d00a4d1024bd569be561d41fb5134850 100644 --- a/lib/bouncer/backends/v0/options.go +++ b/lib/bouncer/backends/v0/options.go @@ -11,6 +11,7 @@ import ( type AcceptOptions struct { SSLMode bouncer.SSLMode SSLConfig *tls.Config + Username string Credentials auth.Credentials Database string StartupParameters map[strutil.CIString]string diff --git a/lib/bouncer/frontends/v0/authenticate.go b/lib/bouncer/frontends/v0/authenticate.go index ad05e298f60ceb1364847d8c0532dfe34d220db9..abcff8d37e0f263d794f986c10d5d8a8c79e8622 100644 --- a/lib/bouncer/frontends/v0/authenticate.go +++ b/lib/bouncer/frontends/v0/authenticate.go @@ -9,7 +9,7 @@ import ( "pggat/lib/perror" ) -func authenticationSASLInitial(ctx *AuthenticateContext, creds auth.SASL) (tool auth.SASLVerifier, resp []byte, done bool, err perror.Error) { +func authenticationSASLInitial(ctx *AuthenticateContext, creds auth.SASLServer) (tool auth.SASLVerifier, resp []byte, done bool, err perror.Error) { // check which authentication method the client wants var err2 error ctx.Packet, err2 = ctx.Conn.ReadPacket(true, ctx.Packet) @@ -66,7 +66,7 @@ func authenticationSASLContinue(ctx *AuthenticateContext, tool auth.SASLVerifier return } -func authenticationSASL(ctx *AuthenticateContext, creds auth.SASL) perror.Error { +func authenticationSASL(ctx *AuthenticateContext, creds auth.SASLServer) perror.Error { saslInitial := packets.AuthenticationSASL{ Mechanisms: creds.SupportedSASLMechanisms(), } @@ -108,7 +108,7 @@ func authenticationSASL(ctx *AuthenticateContext, creds auth.SASL) perror.Error return nil } -func authenticationMD5(ctx *AuthenticateContext, creds auth.MD5) perror.Error { +func authenticationMD5(ctx *AuthenticateContext, creds auth.MD5Server) perror.Error { var salt [4]byte _, err := rand.Read(salt[:]) if err != nil { @@ -141,27 +141,21 @@ func authenticationMD5(ctx *AuthenticateContext, creds auth.MD5) perror.Error { } func authenticate(ctx *AuthenticateContext) (params AuthenticateParams, err perror.Error) { - if ctx.Options.Credentials == nil { - err = perror.New( - perror.FATAL, - perror.InvalidPassword, - "User or database not found", - ) - return - } - if credsSASL, ok := ctx.Options.Credentials.(auth.SASL); ok { - err = authenticationSASL(ctx, credsSASL) - } else if credsMD5, ok := ctx.Options.Credentials.(auth.MD5); ok { - err = authenticationMD5(ctx, credsMD5) - } else { - err = perror.New( - perror.FATAL, - perror.InternalError, - "Auth method not supported", - ) - } - if err != nil { - return + if ctx.Options.Credentials != nil { + if credsSASL, ok := ctx.Options.Credentials.(auth.SASLServer); ok { + err = authenticationSASL(ctx, credsSASL) + } else if credsMD5, ok := ctx.Options.Credentials.(auth.MD5Server); ok { + err = authenticationMD5(ctx, credsMD5) + } else { + err = perror.New( + perror.FATAL, + perror.InternalError, + "Auth method not supported", + ) + } + if err != nil { + return + } } // send auth Ok diff --git a/lib/gat/modes/cloud_sql_discovery/config.go b/lib/gat/modes/cloud_sql_discovery/config.go new file mode 100644 index 0000000000000000000000000000000000000000..28eaa55db5a568707333ac393fcf7fafc215eed2 --- /dev/null +++ b/lib/gat/modes/cloud_sql_discovery/config.go @@ -0,0 +1,144 @@ +package cloud_sql_discovery + +import ( + "context" + "crypto/tls" + "errors" + "net" + "strings" + "time" + + "gfx.cafe/util/go/gun" + sqladmin "google.golang.org/api/sqladmin/v1beta4" + "tuxpa.in/a/zlog/log" + + "pggat/lib/auth/credentials" + "pggat/lib/bouncer" + "pggat/lib/bouncer/backends/v0" + "pggat/lib/bouncer/frontends/v0" + "pggat/lib/gat" + "pggat/lib/gat/pool" + "pggat/lib/gat/pool/dialer" + "pggat/lib/gat/pool/pools/transaction" + "pggat/lib/gat/pool/recipe" + "pggat/lib/util/flip" + "pggat/lib/util/strutil" +) + +type Config struct { + Project string `env:"PGGAT_GC_PROJECT"` + IpAddressType string `env:"PGGAT_GC_IP_ADDR_TYPE" default:"PRIMARY"` +} + +func Load() (Config, error) { + var conf Config + gun.Load(&conf) + if conf.Project == "" { + return Config{}, errors.New("expected google cloud project id") + } + return conf, nil +} + +func (T *Config) ListenAndServe() error { + service, err := sqladmin.NewService(context.Background()) + if err != nil { + return err + } + + instances, err := service.Instances.List(T.Project).Do() + if err != nil { + return err + } + + var pools gat.PoolsMap + + for _, instance := range instances.Items { + if !strings.HasPrefix(instance.DatabaseVersion, "POSTGRES_") { + continue + } + + var address string + for _, ip := range instance.IpAddresses { + if ip.Type != T.IpAddressType { + continue + } + address = net.JoinHostPort(ip.IpAddress, "5432") + } + if address == "" { + continue + } + + users, err := service.Users.List(T.Project, instance.Name).Do() + if err != nil { + return err + } + databases, err := service.Databases.List(T.Project, instance.Name).Do() + if err != nil { + return err + } + for _, user := range users.Items { + creds := credentials.Cleartext{ + Username: user.Name, + Password: "password", + } + + for _, database := range databases.Items { + d := dialer.Net{ + Network: "tcp", + Address: address, + AcceptOptions: backends.AcceptOptions{ + SSLMode: bouncer.SSLModePrefer, + SSLConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + Username: user.Name, + Credentials: creds, + Database: database.Name, + }, + } + + options := transaction.Apply(pool.Options{ + Credentials: creds, + ServerReconnectInitialTime: 5 * time.Second, + ServerReconnectMaxTime: 5 * time.Second, + ServerIdleTimeout: 5 * time.Minute, + TrackedParameters: []strutil.CIString{ + strutil.MakeCIString("client_encoding"), + strutil.MakeCIString("datestyle"), + strutil.MakeCIString("timezone"), + strutil.MakeCIString("standard_conforming_strings"), + strutil.MakeCIString("application_name"), + }, + }) + + p := pool.NewPool(options) + p.AddRecipe(instance.Name, recipe.NewRecipe(recipe.Options{ + Dialer: d, + })) + + pools.Add(user.Name, database.Name, p) + log.Printf("registered database user=%s database=%s", user.Name, database.Name) + } + } + } + + var b flip.Bank + + b.Queue(func() error { + log.Print("listening on :5432") + return gat.ListenAndServe("tcp", ":5432", frontends.AcceptOptions{ + // TODO(garet) ssl config + AllowedStartupOptions: []strutil.CIString{ + strutil.MakeCIString("client_encoding"), + strutil.MakeCIString("datestyle"), + strutil.MakeCIString("timezone"), + strutil.MakeCIString("standard_conforming_strings"), + strutil.MakeCIString("application_name"), + strutil.MakeCIString("extra_float_digits"), + strutil.MakeCIString("options"), + }, + }, &pools) + }) + + return b.Wait() +} diff --git a/lib/gat/modes/digitalocean_discovery/config.go b/lib/gat/modes/digitalocean_discovery/config.go index f09a0472cd59ab211f0374433cfe71587ef8a987..4edf63e4318fcccc5dbacb6e6af1f7f86cf06770 100644 --- a/lib/gat/modes/digitalocean_discovery/config.go +++ b/lib/gat/modes/digitalocean_discovery/config.go @@ -122,6 +122,7 @@ func (T *Config) ListenAndServe() error { SSLConfig: &tls.Config{ InsecureSkipVerify: true, }, + Username: user.Name, Credentials: creds, Database: dbname, } diff --git a/lib/gat/modes/pgbouncer/pools.go b/lib/gat/modes/pgbouncer/pools.go index da870275974aa4307e1b552a9b8e4755a5f25882..77123a7c4a15e82609e1706ac4e2eb6933ccbbf5 100644 --- a/lib/gat/modes/pgbouncer/pools.go +++ b/lib/gat/modes/pgbouncer/pools.go @@ -191,6 +191,7 @@ func (T *Pools) Lookup(user, database string) *pool.Pool { SSLConfig: &tls.Config{ InsecureSkipVerify: true, // TODO(garet) }, + Username: user, Credentials: dbCreds, Database: backendDatabase, StartupParameters: db.StartupParameters, diff --git a/lib/gat/modes/zalando_operator_discovery/server.go b/lib/gat/modes/zalando_operator_discovery/server.go index a705590ee5e8549f451aa64b97e8e1800e267266..8115889caa15c55f4f275b130ff7b0c04c2ded1a 100644 --- a/lib/gat/modes/zalando_operator_discovery/server.go +++ b/lib/gat/modes/zalando_operator_discovery/server.go @@ -153,7 +153,7 @@ func (T *Server) addPostgresql(psql *acidv1.Postgresql) { T.updatePostgresql(nil, psql) } -func (T *Server) addPool(name string, userCreds, serverCreds auth.Credentials, database string) { +func (T *Server) addPool(name string, userCreds, serverCreds auth.Credentials, userUser, serverUser, database string) { d := dialer.Net{ Network: "tcp", Address: fmt.Sprintf("%s.%s.svc.%s:5432", name, T.config.Namespace, T.opConfig.ClusterDomain), @@ -162,6 +162,7 @@ func (T *Server) addPool(name string, userCreds, serverCreds auth.Credentials, d SSLConfig: &tls.Config{ InsecureSkipVerify: true, }, + Username: serverUser, Credentials: serverCreds, Database: database, }, @@ -205,7 +206,7 @@ func (T *Server) addPool(name string, userCreds, serverCreds auth.Credentials, d p.AddRecipe("service", r) - T.pools.Add(userCreds.GetUsername(), database, p) + T.pools.Add(userUser, database, p) } func (T *Server) updatePostgresql(oldPsql *acidv1.Postgresql, newPsql *acidv1.Postgresql) { @@ -305,7 +306,7 @@ func (T *Server) updatePostgresql(oldPsql *acidv1.Postgresql, newPsql *acidv1.Po Username: pair.User, Password: creds.Password, } - T.addPool(details.Name, userCreds, creds, pair.Database) + T.addPool(details.Name, userCreds, creds, pair.User, details.SecretUser, pair.Database) log.Print("added pool username=", pair.User, " database=", pair.Database) } } diff --git a/lib/gat/pool/scaler.go b/lib/gat/pool/scaler.go index 224c37fbcedfed63b74be006e0fd864a444f58a5..ef61951b7f1a83558a883db71606185a8be007fb 100644 --- a/lib/gat/pool/scaler.go +++ b/lib/gat/pool/scaler.go @@ -2,6 +2,7 @@ package pool import ( "time" + "tuxpa.in/a/zlog/log" ) @@ -19,7 +20,7 @@ type Scaler struct { func NewScaler(pool *Pool) *Scaler { s := &Scaler{ pool: pool, - backoff: pool.options.ServerIdleTimeout, + backoff: pool.options.ServerReconnectInitialTime, } if pool.options.ServerIdleTimeout != 0 { diff --git a/lib/gsql/query_test.go b/lib/gsql/query_test.go index 09b7f76fd0fbd7844620742e575edc2627ffcc25..ec9c8e78641ce6d18e487ce032eec3a329cdeede 100644 --- a/lib/gsql/query_test.go +++ b/lib/gsql/query_test.go @@ -28,6 +28,7 @@ func TestQuery(t *testing.T) { ctx := backends.AcceptContext{ Conn: server, Options: backends.AcceptOptions{ + Username: "postgres", Credentials: credentials.Cleartext{ Username: "postgres", Password: "password", diff --git a/test/tester_test.go b/test/tester_test.go index 3555fa5c9066c487205dea478054a536a5bbf3b5..b0dcdfce9ffd0e477d78581da74b6ff69d97bffc 100644 --- a/test/tester_test.go +++ b/test/tester_test.go @@ -6,6 +6,9 @@ import ( "fmt" "net" _ "net/http/pprof" + "strconv" + "testing" + "pggat/lib/auth" "pggat/lib/auth/credentials" "pggat/lib/bouncer/backends/v0" @@ -18,8 +21,6 @@ import ( "pggat/lib/gat/pool/recipe" "pggat/test" "pggat/test/tests" - "strconv" - "testing" ) func daisyChain(creds auth.Credentials, control dialer.Net, n int) (dialer.Net, error) { @@ -59,6 +60,7 @@ func daisyChain(creds auth.Credentials, control dialer.Net, n int) (dialer.Net, Network: "tcp", Address: ":" + strconv.Itoa(port), AcceptOptions: backends.AcceptOptions{ + Username: "runner", Credentials: creds, Database: "pool", }, @@ -73,6 +75,7 @@ func TestTester(t *testing.T) { Network: "tcp", Address: "localhost:5432", AcceptOptions: backends.AcceptOptions{ + Username: "postgres", Credentials: credentials.Cleartext{ Username: "postgres", Password: "password", @@ -137,6 +140,7 @@ func TestTester(t *testing.T) { Network: "tcp", Address: ":" + strconv.Itoa(port), AcceptOptions: backends.AcceptOptions{ + Username: "runner", Credentials: creds, Database: "transaction", }, @@ -145,6 +149,7 @@ func TestTester(t *testing.T) { Network: "tcp", Address: ":" + strconv.Itoa(port), AcceptOptions: backends.AcceptOptions{ + Username: "runner", Credentials: creds, Database: "session", },