Skip to content

Commit

Permalink
Merge pull request #7 from fydrah/config-username-claim
Browse files Browse the repository at this point in the history
Config username claim and conf checks
  • Loading branch information
fydrah committed Oct 24, 2018
2 parents d3cb58f + f44cca8 commit 3ba9c7b
Show file tree
Hide file tree
Showing 13 changed files with 268 additions and 100 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM golang:1.8-alpine3.6 AS build
ARG REPO=github.com/fydrah/loginapp

RUN apk add --no-cache git build-base
ADD . /go/src/${REPO}
COPY . /go/src/${REPO}
WORKDIR /go/src/${REPO}
RUN make build-static

Expand Down
31 changes: 22 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,11 @@ log:
# Configure the web behavior
web_output:
# ClientID to output (useful for cross_client)
# default: 'oidc.client.id'
# default: value of 'oidc.client.id'
main_client_id: loginapp
# Claims to use for kubeconfig username.
# default: name
main_username_claim: email
# Assets directory
# default: ${pwd}/assets
assets_dir: /assets
Expand All @@ -101,6 +104,10 @@ web_output:
skip_main_page: false
```
Two main examples are available:
* [Full configuration example](./example/config-loginapp-full.yaml) (each config option is set)
* [Minimal configuration example](./example/config-loginapp-minimal.yaml) (only mandatory options)
## Kubernetes
This application is built to run on a Kubernetes cluster. You will find usage examples here:
Expand All @@ -112,14 +119,20 @@ This application is built to run on a Kubernetes cluster. You will find usage ex
###### Setup Dex
```
* (Optional) Configure GitHub OAuth App
```shell
# Configure github oauth secrets if needed.
# You must create an app in your github account before.
cat <<EOF > dev.env
GITHUB_CLIENT_ID=yourclientid
GITHUB_CLIENT_SECRET=yoursecretid
EOF
# Configure hosts entry
```

* Configure host entry

```shell
echo "127.0.0.1 dex.example.com" | sudo tee -a /etc/hosts
docker-compose up -d
```
Expand All @@ -131,7 +144,7 @@ EOF

Loginapp uses [golang dep](https://golang.github.io/dep/docs/installation.html).

```
```shell
# Update dependencies
dep ensure
```
Expand All @@ -140,15 +153,15 @@ Loginapp uses [golang dep](https://golang.github.io/dep/docs/installation.html).

Configuration files are located in [example directory](./example/)

```
```shell
make
bin/loginapp serve example/config-loginapp.yaml
bin/loginapp serve example/config-loginapp-full.yaml
```

You can also build a temporary Docker image for loginapp, and
run it with docker-compose (uncomment lines and replace image name):

```
```shell
make docker-tmp
```

Expand All @@ -159,13 +172,13 @@ Some checks can be launched before commits:
* gocyclo: cyclomatic complexities of functions
* gosimple: simplify code

```
```shell
make checks
```

Run also gofmt before any new commit:

```
```shell
make gofmt
```

Expand Down
5 changes: 4 additions & 1 deletion cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import (
)

var (
// GitVersion returns latest tag
GitVersion = "X.X.X"
GitHash = "XXXXXXX"
// GitHash return hash of latest commit
GitHash = "XXXXXXX"
)

// NewCli configure loginapp CLI
func NewCli() *cli.App {
app := cli.NewApp()
cli.AppHelpTemplate = `
Expand Down
112 changes: 72 additions & 40 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,19 @@ type AppConfig struct {
Listen string `yaml:"listen"`
OIDC struct {
Client struct {
Id string `yaml:"id"`
ID string `yaml:"id"`
Secret string `yaml:"secret"`
RedirectURL string `yaml:"redirect_url"`
} `yaml:"client"`
Issuer struct {
Url string `yaml:"url"`
URL string `yaml:"url"`
RootCA string `yaml:"root_ca"`
} `yaml:"issuer"`
ExtraScopes []string `yaml:"extra_scopes"`
OfflineAsScope *bool `yaml:"offline_as_scope"`
CrossClients []string `yaml:"cross_clients"`
} `yaml:"oidc"`
Tls struct {
TLS struct {
Enabled bool `yaml:"enabled"`
Cert string `yaml:"cert"`
Key string `yaml:"key"`
Expand All @@ -51,13 +51,40 @@ type AppConfig struct {
Format string `yaml:"format"`
} `yaml:"log"`
WebOutput struct {
MainClientID string `yaml:"main_client_id"`
AssetsDir string `yaml:"assets_dir"`
SkipMainPage bool `yaml:"skip_main_page"`
MainUsernameClaim string `yaml:"main_username_claim"`
MainClientID string `yaml:"main_client_id"`
AssetsDir string `yaml:"assets_dir"`
SkipMainPage bool `yaml:"skip_main_page"`
} `yaml:"web_output"`
}

func ConfigLogger(format string, logLevel string) {
// appCheck struct
// used by check function
type appCheck struct {
Condition bool
Message string
DefaultAction func()
}

// check checks each appCheck, if one
// check fails, return true
func check(checks []appCheck) bool {
checkFailed := false
for _, c := range checks {
if c.Condition {
logger.Error(c.Message)
checkFailed = true
if c.DefaultAction != nil {
c.DefaultAction()
}
}
}
return checkFailed
}

// configLogger setup application logger
// Default loglevel is info
func configLogger(format string, logLevel string) {
switch f := format; f {
case "json":
logger.Formatter = &logrus.JSONFormatter{}
Expand Down Expand Up @@ -86,6 +113,9 @@ func ConfigLogger(format string, logLevel string) {
logger.Debugf("Using %s log level", logLevel)
}

// Init load configuration,
// setup logger and run
// error/warning checks
func (a *AppConfig) Init(config string) error {
/*
Extract data from yaml configuration file
Expand All @@ -103,7 +133,7 @@ func (a *AppConfig) Init(config string) error {
/*
Configure log level
*/
ConfigLogger(strings.ToLower(a.Log.Format), strings.ToLower(a.Log.Level))
configLogger(strings.ToLower(a.Log.Format), strings.ToLower(a.Log.Level))

/*
Configuration checks
Expand All @@ -113,41 +143,43 @@ func (a *AppConfig) Init(config string) error {
if err != nil {
return fmt.Errorf("error getting current directory: %v", err)
}
if a.WebOutput.AssetsDir == "" {
a.WebOutput.AssetsDir = fmt.Sprintf("%v/assets", currentDir)
logger.Infof("no assets dir specified, using default: %v", a.WebOutput.AssetsDir)
}
if a.WebOutput.MainClientID == "" {
a.WebOutput.MainClientID = a.OIDC.Client.Id
logger.Infof("no output main_client_id specified, using default: %v", a.WebOutput.MainClientID)
}
configChecks := []struct {
failed bool
msg string
}{
{a.Name == "", "no name specified"},
{a.Listen == "", "no bind 'ip:port' specified"},
{a.OIDC.Client.Id == "", "no client id specified"},
{a.OIDC.Client.Secret == "", "no client secret specified"},
{a.OIDC.Client.RedirectURL == "", "no redirect url specified"},
{a.OIDC.Issuer.Url == "", "no issuer url specified"},
{a.OIDC.Issuer.RootCA == "", "no issuer root_ca specified"},
{a.Tls.Enabled && a.Tls.Cert == "", "no tls cert specified"},
{a.Tls.Enabled && a.Tls.Key == "", "no tls key specified"},
defaultAssetsDir := fmt.Sprintf("%v/assets", currentDir)
/*
Error checks: list of checks which make loginapp failed
*/
errorChecks := []appCheck{
{a.Name == "", "no name specified", nil},
{a.Listen == "", "no bind 'ip:port' specified", nil},
{a.OIDC.Client.ID == "", "no client id specified", nil},
{a.OIDC.Client.Secret == "", "no client secret specified", nil},
{a.OIDC.Client.RedirectURL == "", "no redirect url specified", nil},
{a.OIDC.Issuer.URL == "", "no issuer url specified", nil},
{a.OIDC.Issuer.RootCA == "", "no issuer root_ca specified", nil},
{a.TLS.Enabled && a.TLS.Cert == "", "no tls cert specified", nil},
{a.TLS.Enabled && a.TLS.Key == "", "no tls key specified", nil},
}
checksFailed := func() bool {
checkFailed := false
for _, c := range configChecks {
if c.failed {
logger.Errorf("Check failed: %v", c.msg)
checkFailed = true
}
}
return checkFailed
}()
if checksFailed {
if check(errorChecks) {
return fmt.Errorf("Error while loading configuration")
}
/*
Default checks: list of check which make loginapp setup a default value
Even if logger report this as an error log, this is not handle as an error.
Hope the following issue will be merged to use loglevel as a parameter:
https://github.com/sirupsen/logrus/issues/646
*/
defaultChecks := []appCheck{
{a.WebOutput.MainClientID == "", fmt.Sprintf("no output main_client_id specified, using default: %v", a.OIDC.Client.ID), func() {
a.WebOutput.MainClientID = a.OIDC.Client.ID
}},
{a.WebOutput.AssetsDir == "", fmt.Sprintf("no assets_dir specified, using default: %v", defaultAssetsDir), func() {
a.WebOutput.AssetsDir = defaultAssetsDir
}},
{a.WebOutput.MainUsernameClaim == "", "no output main_username_claim specified, using default: 'name'", func() {
a.WebOutput.MainUsernameClaim = "name"
}},
}
_ = check(defaultChecks)

logger.Debugf("Configuration loaded: %+v", a)
return nil
Expand Down
3 changes: 2 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ services:
image: quay.io/coreos/dex:v2.10.0
volumes:
- ./example/ssl:/ssl
- ./example/config-dex.yaml:/config.yaml
- ./example/config-dex-full.yaml:/config.yaml
#- ./example/config-dex-minimal.yaml:/config.yaml
- dex:/var/dex/
env_file:
- dev.env
Expand Down
41 changes: 41 additions & 0 deletions example/config-dex-full.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
issuer: https://dex.example.com:5556
storage:
type: sqlite3
config:
file: /var/dex/dex.db
web:
https: 0.0.0.0:5556
tlsCert: /ssl/cert.pem
tlsKey: /ssl/key.pem
connectors:
#- type: github
# id: github
# name: GitHub
# config:
# clientID: $GITHUB_CLIENT_ID
# clientSecret: $GITHUB_CLIENT_SECRET
# redirectURI: https://dex.example.com:5556/callback
oauth2:
skipApprovalScreen: true

staticClients:
- id: loginapp
redirectURIs:
- 'https://127.0.0.1:5555/callback'
name: 'Loginapp'
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
- id: web
redirectURIs:
- 'https://127.0.0.1:5555/callback'
name: 'Web'
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
trustedPeers:
- loginapp

enablePasswordDB: true
staticPasswords:
- email: "admin@example.com"
# bcrypt hash of the string "password"
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
username: "admin"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
26 changes: 26 additions & 0 deletions example/config-dex-minimal.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
issuer: https://dex.example.com:5556
storage:
type: sqlite3
config:
file: /var/dex/dex.db
web:
https: 0.0.0.0:5556
tlsCert: /ssl/cert.pem
tlsKey: /ssl/key.pem
oauth2:
skipApprovalScreen: true

staticClients:
- id: loginapp
redirectURIs:
- 'http://127.0.0.1:5555/callback'
name: 'Loginapp'
secret: ZXhhbXBsZS1hcHAtc2VjcmV0

enablePasswordDB: true
staticPasswords:
- email: "admin@example.com"
# bcrypt hash of the string "password"
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
username: "admin"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
27 changes: 27 additions & 0 deletions example/config-loginapp-full.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: "Kubernetes Auth"
listen: "0.0.0.0:5555"
oidc:
client:
id: "loginapp"
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
redirect_url: "https://127.0.0.1:5555/callback"
issuer:
root_ca: "example/ssl/ca.pem"
url: "https://dex.example.com:5556"
extra_scopes:
- groups
offline_as_scope: true
cross_clients:
- web
tls:
enabled: true
cert: example/ssl/cert.pem
key: example/ssl/key.pem
log:
level: Debug
format: json
web_output:
main_client_id: "loginapp"
main_username_claim: "email"
assets_dir: "../assets/"
skip_main_page: true
10 changes: 10 additions & 0 deletions example/config-loginapp-minimal.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: "Kubernetes Auth"
listen: "0.0.0.0:5555"
oidc:
client:
id: "loginapp"
secret: ZXhhbXBsZS1hcHAtc2VjcmV0
redirect_url: "http://127.0.0.1:5555/callback"
issuer:
root_ca: "example/ssl/ca.pem"
url: "https://dex.example.com:5556"
Loading

0 comments on commit 3ba9c7b

Please sign in to comment.