diff --git a/sda-auth/config.go b/sda-auth/config.go index c5dba8cf1..0c7af64b2 100644 --- a/sda-auth/config.go +++ b/sda-auth/config.go @@ -53,6 +53,7 @@ type Config struct { Server ServerConfig S3Inbox string ResignJwt bool + C4ghPubKey string } // NewConfig initializes and parses the config file and/or environment using @@ -90,6 +91,7 @@ func (c *Config) readConfig() error { c.JwtPrivateKey = viper.GetString("JwtPrivateKey") c.JwtSignatureAlg = viper.GetString("JwtSignatureAlg") c.JwtIssuer = viper.GetString("jwtIssuer") + c.C4ghPubKey = viper.GetString("c4ghPubKey") viper.SetDefault("ResignJwt", true) c.ResignJwt = viper.GetBool("resignJwt") @@ -163,7 +165,7 @@ func (c *Config) readConfig() error { log.Printf("Setting log level to '%s'", stringLevel) } - for _, s := range []string{"jwtIssuer", "JwtPrivateKey", "JwtSignatureAlg", "s3Inbox"} { + for _, s := range []string{"jwtIssuer", "JwtPrivateKey", "JwtSignatureAlg", "s3Inbox", "C4ghPubKey"} { if viper.GetString(s) == "" { return fmt.Errorf("%s not set", s) } diff --git a/sda-auth/config.yaml b/sda-auth/config.yaml index 0a3baa07d..58ff1bcb8 100644 --- a/sda-auth/config.yaml +++ b/sda-auth/config.yaml @@ -22,3 +22,4 @@ jwtIssuer: "http://auth:8080" jwtPrivateKey: "keys/sign-jwt.key" jwtSignatureAlg: "ES256" resignJwt: true +c4ghPubKey: "/c4gh_key.pub.pem" diff --git a/sda-auth/config_test.go b/sda-auth/config_test.go index 9a2d22bba..6664ace2c 100644 --- a/sda-auth/config_test.go +++ b/sda-auth/config_test.go @@ -30,6 +30,7 @@ type ConfigTests struct { JwtPrivateKeyFile *os.File JwtSignatureAlg string ResignJwt bool + C4ghPubKey string } func TestConfigTestSuite(t *testing.T) { @@ -85,6 +86,7 @@ func (suite *ConfigTests) SetupTest() { suite.JwtPrivateKey = suite.JwtPrivateKeyFile.Name() suite.JwtSignatureAlg = "RS256" suite.ResignJwt = true + suite.C4ghPubKey = "/c4gh.pub.pem" // Write config to temp config file configYaml, err := yaml.Marshal(Config{ @@ -96,6 +98,7 @@ func (suite *ConfigTests) SetupTest() { JwtPrivateKey: suite.JwtPrivateKey, JwtSignatureAlg: suite.JwtSignatureAlg, ResignJwt: suite.ResignJwt, + C4ghPubKey: suite.C4ghPubKey, }) if err != nil { log.Errorf("Error marshalling config yaml: %v", err) @@ -147,6 +150,9 @@ func (suite *ConfigTests) TestConfig() { assert.Equal(suite.T(), suite.JwtSignatureAlg, config.JwtSignatureAlg, "JwtSignatureAlg misread from config file") assert.Equal(suite.T(), suite.ResignJwt, config.ResignJwt, "ResignJwt misread from config file") + // Check C4ghPubKey value + assert.Equal(suite.T(), suite.C4ghPubKey, config.C4ghPubKey, "C4ghPubKey misread from config file") + // sanitycheck without config file or ENVs // this should fail os.Remove(suite.ConfigFile.Name()) @@ -174,6 +180,8 @@ func (suite *ConfigTests) TestConfig() { os.Setenv("JWTSIGNATUREALG", fmt.Sprintf("env_%v", suite.JwtSignatureAlg)) os.Setenv("RESIGNJWT", fmt.Sprintf("%t", suite.ResignJwt)) + os.Setenv("C4ghPubKey", fmt.Sprintf("env_%v", suite.C4ghPubKey)) + // re-read the config config, err = NewConfig() assert.NoError(suite.T(), err) @@ -197,6 +205,8 @@ func (suite *ConfigTests) TestConfig() { assert.Equal(suite.T(), fmt.Sprintf("env_%v", suite.JwtSignatureAlg), config.JwtSignatureAlg, "JwtSignatureAlg misread from environment variable") assert.Equal(suite.T(), fmt.Sprintf("%t", suite.ResignJwt), strconv.FormatBool(config.ResignJwt), "ResignJwt misread from environment variable") + assert.Equal(suite.T(), fmt.Sprintf("env_%v", suite.C4ghPubKey), config.C4ghPubKey, "C4ghPubKey misread from environment variable") + // Check missing private key os.Setenv("JWTPRIVATEKEY", "nonexistent-key-file") diff --git a/sda-auth/dev-server/docker-compose.yml b/sda-auth/dev-server/docker-compose.yml index 8d0b1551b..ef771dfa6 100644 --- a/sda-auth/dev-server/docker-compose.yml +++ b/sda-auth/dev-server/docker-compose.yml @@ -43,7 +43,7 @@ services: build: context: ../ args: - GOLANG_VERSION: ${GOLANG_VERSION-1.18} + GOLANG_VERSION: ${GOLANG_VERSION-1.20.5} dockerfile: Dockerfile depends_on: oidc: @@ -69,6 +69,8 @@ services: volumes: - ../keys:/keys - ../:/sda-auth + - ../keys/crypt4gh_key.pub.pem:/crypt4gh_key.pub.pem + - ../keys/crypt4gh_key.sec.pem:/crypt4gh_key.sec.pem image: sda-auth ports: - 8080:8080 diff --git a/sda-auth/go.mod b/sda-auth/go.mod index a2023fb35..d325ef017 100644 --- a/sda-auth/go.mod +++ b/sda-auth/go.mod @@ -13,13 +13,14 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.16.0 github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.9.0 + golang.org/x/crypto v0.11.0 golang.org/x/net v0.10.0 golang.org/x/oauth2 v0.8.0 gopkg.in/yaml.v3 v3.0.1 ) require ( + filippo.io/edwards25519 v1.0.0 // indirect github.com/BurntSushi/toml v1.2.1 // indirect github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect github.com/CloudyKit/jet/v6 v6.2.0 // indirect @@ -28,6 +29,7 @@ require ( github.com/andybalholm/brotli v1.0.5 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a // indirect github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/flosch/pongo2/v4 v4.0.2 // indirect @@ -51,6 +53,7 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/microcosm-cc/bluemonday v1.0.23 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/neicnordic/crypt4gh v1.7.6 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -68,8 +71,8 @@ require ( github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/yosssi/ace v0.0.5 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.30.0 // indirect diff --git a/sda-auth/go.sum b/sda-auth/go.sum index 049cea1c3..25550d125 100644 --- a/sda-auth/go.sum +++ b/sda-auth/go.sum @@ -36,6 +36,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -69,6 +71,8 @@ github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHo github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU= +github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0= github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= @@ -213,6 +217,8 @@ github.com/microcosm-cc/bluemonday v1.0.23/go.mod h1:mN70sk7UkkF8TUr2IGBpNN0jAgS github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/neicnordic/crypt4gh v1.7.6 h1:Vqcb8Yb950oaBBJFepDK1oLeu9rZzpywYWVHLmO0oI8= +github.com/neicnordic/crypt4gh v1.7.6/go.mod h1:rqmVXsprDFBRRLJkm1cK9kLETBPGEZmft9lHD/V40wk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282 h1:TQMyrpijtkFyXpNI3rY5hsZQZw+paiH+BfAlsb81HBY= @@ -304,6 +310,8 @@ golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -437,6 +445,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -448,6 +458,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/sda-auth/info.go b/sda-auth/info.go new file mode 100644 index 000000000..0104085c5 --- /dev/null +++ b/sda-auth/info.go @@ -0,0 +1,74 @@ +package main + +import ( + "encoding/hex" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/kataras/iris/v12" + "github.com/neicnordic/crypt4gh/keys" + log "github.com/sirupsen/logrus" +) + +type Info struct { + ClientID string `json:"client_id"` + OidcURI string `json:"oidc_uri"` + PublicKey string `json:"public_key"` + InboxURI string `json:"inbox_uri"` +} + +func readPublicKeyFile(filename string) (key *[32]byte, err error) { + log.Info("Reading Public key file") + file, err := os.Open(filepath.Clean(filename)) + if err != nil { + return nil, err + } + + publicKey, err := readPublicKey(file) + if err != nil { + return nil, fmt.Errorf("error while reading public key file %s: %v", filename, err) + } + + return &publicKey, err +} + +// Wrapper for the respective crypt4gh function. Since this function panics if the key is +// malformed, so we handle that as well as errors. +func readPublicKey(reader io.Reader) (key [32]byte, err error) { + + defer func() { + if recover() != nil { + err = fmt.Errorf("malformed key file") + } + }() + + publicKey, err := keys.ReadPublicKey(reader) + + return publicKey, err +} + +func (auth AuthHandler) getInfo(ctx iris.Context) { + + // Read the c4gh pub key from file. This is the same across + // different deployments and set in the config + publicKey, err := readPublicKeyFile(auth.Config.C4ghPubKey) + pub := hex.EncodeToString(publicKey[:]) + if err != nil { + log.Error("Failure to get public key: ", err) + } + + // TODO: remove. hardcoding for testing + auth.OAuth2Config.ClientID = "8b7b0168-6b16-4fd2-baec-b0a28b0d5cb0" + auth.Config.JwtIssuer = "https://login.elixir-czech.org/oidc/" + auth.Config.S3Inbox = "https://login.elixir-czech.org/oidc" + + info := Info{ClientID: auth.OAuth2Config.ClientID, OidcURI: auth.Config.JwtIssuer, PublicKey: pub, InboxURI: auth.Config.S3Inbox} + err = ctx.JSON(info) + if err != nil { + log.Error("Failure to get Info ", err) + + return + } +} diff --git a/sda-auth/keys/c4gh_key.pub.pem b/sda-auth/keys/c4gh_key.pub.pem new file mode 100644 index 000000000..edcc13966 --- /dev/null +++ b/sda-auth/keys/c4gh_key.pub.pem @@ -0,0 +1,3 @@ +-----BEGIN CRYPT4GH PUBLIC KEY----- +J75CRF/Z45yb455rNqVeYeOAH8hF9jeBqBPT/pl34Xo= +-----END CRYPT4GH PUBLIC KEY----- diff --git a/sda-auth/keys/c4gh_key.sec.pem b/sda-auth/keys/c4gh_key.sec.pem new file mode 100644 index 000000000..974cd81bf --- /dev/null +++ b/sda-auth/keys/c4gh_key.sec.pem @@ -0,0 +1,5 @@ +-----BEGIN CRYPT4GH ENCRYPTED PRIVATE KEY----- +YzRnaC12MQAGc2NyeXB0ABQAAAAAq5Zoea6wEWm2lT5gad3aZQARY2hhY2hhMjBf +cG9seTEzMDUAPBfY7N9POe/m4L+x1Z1QYEhv7x3HOU47G+wqRFVXq2WBHfrrQNHp +1YIpd3rGNnXI8O5WoB+qZ8ynAIn2tA== +-----END CRYPT4GH ENCRYPTED PRIVATE KEY----- \ No newline at end of file diff --git a/sda-auth/keys/crypt4gh_key.pub.pem b/sda-auth/keys/crypt4gh_key.pub.pem new file mode 100644 index 000000000..edcc13966 --- /dev/null +++ b/sda-auth/keys/crypt4gh_key.pub.pem @@ -0,0 +1,3 @@ +-----BEGIN CRYPT4GH PUBLIC KEY----- +J75CRF/Z45yb455rNqVeYeOAH8hF9jeBqBPT/pl34Xo= +-----END CRYPT4GH PUBLIC KEY----- diff --git a/sda-auth/keys/crypt4gh_key.sec.pem b/sda-auth/keys/crypt4gh_key.sec.pem new file mode 100644 index 000000000..974cd81bf --- /dev/null +++ b/sda-auth/keys/crypt4gh_key.sec.pem @@ -0,0 +1,5 @@ +-----BEGIN CRYPT4GH ENCRYPTED PRIVATE KEY----- +YzRnaC12MQAGc2NyeXB0ABQAAAAAq5Zoea6wEWm2lT5gad3aZQARY2hhY2hhMjBf +cG9seTEzMDUAPBfY7N9POe/m4L+x1Z1QYEhv7x3HOU47G+wqRFVXq2WBHfrrQNHp +1YIpd3rGNnXI8O5WoB+qZ8ynAIn2tA== +-----END CRYPT4GH ENCRYPTED PRIVATE KEY----- \ No newline at end of file diff --git a/sda-auth/main.go b/sda-auth/main.go index 6bd384234..a17009eeb 100644 --- a/sda-auth/main.go +++ b/sda-auth/main.go @@ -389,6 +389,7 @@ func main() { app.Get("/elixir/s3conf", authHandler.getElixirConf) app.Get("/elixir/login", authHandler.getElixirLogin) app.Get("/elixir/cors_login", authHandler.getElixirCORSLogin) + app.Get("/info", authHandler.getInfo) app.UseGlobal(globalHeaders)