Skip to content

Commit

Permalink
Issue #2: Fixes and a documentation page about the new config
Browse files Browse the repository at this point in the history
  • Loading branch information
mrtamm committed Feb 3, 2024
1 parent 7a202b5 commit 65ab8ae
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 8 deletions.
1 change: 1 addition & 0 deletions cmd/server/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ func NewServer(ctx context.Context, conf config.Config, log *logger.Logger) (*Se
RPCAddress: ":" + conf.Server.RPCPort,
HTTPPort: conf.Server.HTTPPort,
BasicAuth: conf.Server.BasicAuth,
OidcAuth: conf.Server.OidcAuth,
DisableHTTPCache: conf.Server.DisableHTTPCache,
Log: log,
Tasks: &server.TaskService{
Expand Down
28 changes: 28 additions & 0 deletions cmd/util/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package util

import (
"io/ioutil"
"log"
"math/rand"
"os"
"path/filepath"

Expand Down Expand Up @@ -34,6 +36,22 @@ func MergeConfigFileWithFlags(file string, flagConf config.Config) (config.Confi
}
}

// Make sure that User/Password are defined for conf.RPCClient:
// 1) when conf.Server.BasicAuth has credentials
// 2) when conf.Server.OidcAuth is enabled (clients still need to provide Basic credentials)
if conf.RPCClient.User == "" && conf.RPCClient.Password == "" {
if len(conf.Server.BasicAuth) > 0 {
log.Fatal("RPCClient User and Password are undefined while Server.BasicAuth is enabled.")
} else if conf.Server.OidcAuth.ServiceConfigUrl != "" {
// Generating random user/password credentials for RPC:
conf.RPCClient.User = randomCredential()
conf.RPCClient.Password = randomCredential()
conf.Server.BasicAuth = append(conf.Server.BasicAuth, config.BasicCredential{
User: conf.RPCClient.User,
Password: conf.RPCClient.Password,
})
}
}
return conf, nil
}

Expand All @@ -58,3 +76,13 @@ func TempConfigFile(c config.Config, name string) (path string, cleanup func())
}
return p, cleanup
}

// RandomString generates a random string of length 20.
func randomCredential() string {
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyz0123456789")
b := make([]rune, 20)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
}
return string(b)
}
2 changes: 0 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ type BasicCredential struct {

type OidcAuth struct {
ServiceConfigUrl string
ClientId string
ClientSecret string
RequireScope string
RequireAudience string
}
Expand Down
2 changes: 0 additions & 2 deletions config/default-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ Server:
# OidcAuth:
# # URL of the OIDC service configuration:
# ServiceConfigUrl: ""
# ClientId: ""
# ClientSecret: ""
# # Optional: if specified, this scope value must be in the token:
# RequireScope:
# # Optional: if specified, this audience value must be in the token:
Expand Down
2 changes: 1 addition & 1 deletion server/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (

var (
errMissingMetadata = status.Errorf(codes.InvalidArgument, "Missing metadata in the context")
errTokenRequired = status.Errorf(codes.Unauthenticated, "Bearer/Basic authorization token missing")
errTokenRequired = status.Errorf(codes.Unauthenticated, "Basic/Bearer authorization token missing")
errInvalidBasicToken = status.Errorf(codes.Unauthenticated, "Invalid Basic authorization token")
errInvalidBearerToken = status.Errorf(codes.Unauthenticated, "Invalid Bearer authorization token")
)
Expand Down
12 changes: 9 additions & 3 deletions server/auth_oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package server

import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
Expand Down Expand Up @@ -30,9 +31,7 @@ type OidcConfig struct {
}

func initOidcConfig(config config.OidcAuth) *OidcConfig {
if config.ServiceConfigUrl == "" ||
config.ClientId == "" ||
config.ClientSecret == "" {
if config.ServiceConfigUrl == "" {
return nil
}

Expand Down Expand Up @@ -85,6 +84,11 @@ func (c *OidcConfig) ParseJwt(jwtString string) *jwt.Token {
jwt.WithIssuer(c.remote.Issuer),
)

if err != nil {
fmt.Println("Token is not valid.", err)
return nil
}

// If audience is required, it must be in the token.
if c.local.RequireAudience != "" {
found := false
Expand All @@ -95,6 +99,7 @@ func (c *OidcConfig) ParseJwt(jwtString string) *jwt.Token {
}
}
if !found {
fmt.Printf("Audience [%s] not found in %v.", c.local.RequireAudience, token.Audience())
return nil
}
}
Expand All @@ -112,6 +117,7 @@ func (c *OidcConfig) ParseJwt(jwtString string) *jwt.Token {
}
}
if !found {
fmt.Printf("Scope [%s] not found in [%s]", c.local.RequireScope, value)
return nil
}
}
Expand Down
46 changes: 46 additions & 0 deletions website/content/docs/security/oauth2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: OAuth2
menu:
main:
parent: Security
weight: 10
---
# OAuth2

By default, a Funnel server allows open access to its API endpoints, but in
addition to Basic authentication it can also be configured to require a valid
JWT in the request.

Funnel itself does not redirect users to perform the login.
It just validates that the presented token is issued by a trusted service
(specified in the configuration file) and the token has not expired.

Optionally, Funnel can also validate the scope and audience claims to contain
specific values.

To enable JWT authentication, specify `OidcAuth` section in your config file:

```yaml
Server:
OidcAuth:
# URL of the OIDC service configuration:
ServiceConfigUrl: "https://my.oidc.service/.well-known/openid-configuration"
# Optional: if specified, this scope value must be in the token:
RequireScope: funnel-id
# Optional: if specified, this audience value must be in the token:
RequireAudience: tes-api
```
Make sure to properly protect the configuration file so that it's not readable
by everyone:
```bash
$ chmod 600 funnel.config.yml
```

To use the token, set the `FUNNEL_SERVER_BEARER` environment variable:

```bash
$ export FUNNEL_SERVER_BEARER=eyJraWQiOiJyc2ExIiwi...
$ funnel task list
```

0 comments on commit 65ab8ae

Please sign in to comment.