Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: Luzifer/nginx-sso
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.22.0
Choose a base ref
...
head repository: Luzifer/nginx-sso
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Dec 28, 2019

  1. Allow to configure anonymous access (#48)

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer authored Dec 28, 2019

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c0886ce View commit details
  2. prepare release v0.23.0

    Luzifer committed Dec 28, 2019

    Verified

    This commit was signed with the committer’s verified signature. The key has expired and has been revoked.
    Luzifer Knut Ahlers
    Copy the full SHA
    3e9a009 View commit details

Commits on Jan 13, 2020

  1. [#50] Handle all 4xx errors as "user not found" (#52)

    * [#50] Handle all 4xx errors as "user not found"
    
    to ensure broad acceptance of OIDC providers
    
    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    
    * Fix: Error is reported earlier with Go default error
    
    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer authored Jan 13, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6d0d520 View commit details
  2. prepare release v0.24.0

    Luzifer committed Jan 13, 2020

    Verified

    This commit was signed with the committer’s verified signature. The key has expired and has been revoked.
    Luzifer Knut Ahlers
    Copy the full SHA
    df6201a View commit details

Commits on Apr 7, 2020

  1. Fix: Config loading after CookieStore init (#58)

    * fix config loading after CookieStore init
    
    * refactor according to suggestions
    
    * fix module init on SIGHUP
    nyanloutre authored Apr 7, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f9d9c02 View commit details
  2. Lint: Fix some minor linter errors

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Apr 7, 2020

    Verified

    This commit was signed with the committer’s verified signature. The key has expired and has been revoked.
    Luzifer Knut Ahlers
    Copy the full SHA
    4a72836 View commit details
  3. prepare release v0.24.1

    Luzifer committed Apr 7, 2020

    Verified

    This commit was signed with the committer’s verified signature. The key has expired and has been revoked.
    Luzifer Knut Ahlers
    Copy the full SHA
    827cc38 View commit details

Commits on Apr 9, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    5e40728 View commit details

Commits on Jun 22, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6878042 View commit details
  2. prepare release v0.25.0

    Luzifer committed Jun 22, 2020

    Verified

    This commit was signed with the committer’s verified signature. The key has expired and has been revoked.
    Luzifer Knut Ahlers
    Copy the full SHA
    e28a067 View commit details

Commits on Jun 29, 2020

  1. Remove vendored libraries

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Jun 29, 2020

    Verified

    This commit was signed with the committer’s verified signature. The key has expired and has been revoked.
    Luzifer Knut Ahlers
    Copy the full SHA
    f9118be View commit details
  2. Switch Dockerfile to readonly modules and recent alpine

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Jun 29, 2020

    Verified

    This commit was signed with the committer’s verified signature. The key has expired and has been revoked.
    Luzifer Knut Ahlers
    Copy the full SHA
    0efe62d View commit details
  3. Update dumb-init

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Jun 29, 2020

    Verified

    This commit was signed with the committer’s verified signature. The key has expired and has been revoked.
    Luzifer Knut Ahlers
    Copy the full SHA
    8738d62 View commit details

Commits on Nov 21, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    cae187e View commit details

Commits on Dec 20, 2022

  1. Add health-endpoint, fix copy on empty dir

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Dec 20, 2022

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    f7685d6 View commit details

Commits on Dec 21, 2022

  1. [ci] Fix missing utils

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Dec 21, 2022

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    9a5aa86 View commit details
  2. Fix: Compiler refuses to convert 0x0 to string

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Dec 21, 2022

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    89a1a2e View commit details
  3. prepare release v0.26.0

    Luzifer committed Dec 21, 2022

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    5c77fee View commit details
  4. Fix: Do not copy folder into folder

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Dec 21, 2022

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    ef64b71 View commit details

Commits on Jul 28, 2023

  1. Update to Go 1.20, update dependencies

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Jul 28, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    42a10a7 View commit details
  2. Rewrite ACL logic

    in order to
    
    - allow explicity deny for anon access
    - allow multiple rule-sets to apply to the same request
    
    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Jul 28, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    b515730 View commit details
  3. Add support for K8s ingress-nginx "rd" URL parameter

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Jul 28, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    2986da4 View commit details

Commits on Jul 29, 2023

  1. [ci] Switch to Github Actions

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Jul 29, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    6eb40a0 View commit details
  2. Move Docker image to use non-root user

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Jul 29, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    ad7c1dd View commit details
  3. [#78] Allow for sprig templating in configuration file

    This enables to move secrets from the configuration file into the
    environment and source them through Go templating with `env` template
    function
    
    closes #78
    
    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Jul 29, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    c60f01d View commit details
  4. Update go-oidc

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Jul 29, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    32245e2 View commit details
  5. prepare release v0.27.0

    Luzifer committed Jul 29, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    1cd016d View commit details
  6. [#79] Fix Docker image broken by user change

    - Properly check file existence (`-f`, not `-d`)
    - Create and chown the data directory in case no mount is present
    
    closes #79
    
    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Jul 29, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    4fca15f View commit details
  7. prepare release v0.27.1

    Luzifer committed Jul 29, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    58021f2 View commit details

Commits on Oct 14, 2023

  1. Update dependencies

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Oct 14, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    ceeda27 View commit details
  2. prepare release v0.27.2

    Luzifer committed Oct 14, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    Luzifer Knut Ahlers
    Copy the full SHA
    562c1b7 View commit details

Commits on Dec 19, 2023

  1. Update dependencies

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Dec 19, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    Luzifer Knut Ahlers
    Copy the full SHA
    1eebad9 View commit details
  2. prepare release v0.27.3

    Luzifer committed Dec 19, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    Luzifer Knut Ahlers
    Copy the full SHA
    21ad10c View commit details

Commits on Dec 12, 2024

  1. Update Go dependencies

    Signed-off-by: Knut Ahlers <knut@ahlers.me>
    Luzifer committed Dec 12, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    Luzifer Knut Ahlers
    Copy the full SHA
    dff1b1f View commit details
  2. prepare release v0.27.4

    Luzifer committed Dec 12, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    Luzifer Knut Ahlers
    Copy the full SHA
    cb34b54 View commit details
Showing 1,059 changed files with 684 additions and 424,469 deletions.
78 changes: 78 additions & 0 deletions .github/workflows/test-and-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---

name: test-and-build
on:
push:
branches: ['*']
tags: ['v*']

permissions:
contents: write

jobs:
test-and-build:
defaults:
run:
shell: bash

container:
image: luzifer/archlinux
env:
CGO_ENABLED: 0
GOPATH: /go

runs-on: ubuntu-latest

steps:
- name: Enable custom AUR package repo
run: echo -e "[luzifer]\nSigLevel = Never\nServer = https://archrepo.hub.luzifer.io/\$arch" >>/etc/pacman.conf

- name: Install required packages
run: |
pacman -Syy --noconfirm \
awk \
curl \
diffutils \
git \
go \
golangci-lint-bin \
make \
tar \
trivy \
unzip \
which \
zip
- uses: actions/checkout@v3

- name: Marking workdir safe
run: git config --global --add safe.directory /__w/nginx-sso/nginx-sso

- name: Lint and test code
run: |
go test -v ./...
- name: Build release
run: make publish
env:
FORCE_SKIP_UPLOAD: 'true'
MOD_MODE: readonly
NO_TESTS: 'true'
PACKAGES: '.'

- name: Execute Trivy scan
run: make trivy

- name: Extract changelog
run: 'awk "/^#/ && ++c==2{exit}; /^#/f" "History.md" | tail -n +2 >release_changelog.md'

- name: Release
uses: ncipollo/release-action@v1
if: startsWith(github.ref, 'refs/tags/')
with:
artifacts: '.build/*'
bodyFile: release_changelog.md
draft: false
generateReleaseNotes: false

...
10 changes: 0 additions & 10 deletions .repo-runner.yaml

This file was deleted.

20 changes: 10 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -3,29 +3,27 @@ FROM golang:alpine as builder
ADD . /go/src/github.com/Luzifer/nginx-sso
WORKDIR /go/src/github.com/Luzifer/nginx-sso

ENV CGO_ENABLED=1
ENV CGO_ENABLED=0

RUN set -ex \
&& apk add --update \
build-base \
&& apk add --no-cache \
git \
&& go install \
-ldflags "-X main.version=$(git describe --tags || git rev-parse --short HEAD || echo dev)" \
-mod=vendor
-mod=readonly

FROM alpine:3.8

FROM alpine

LABEL maintainer "Knut Ahlers <knut@ahlers.me>"

RUN set -ex \
&& apk --no-cache add \
bash \
ca-certificates \
curl \
&& curl -sSfLo /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.1/dumb-init_1.2.1_amd64 \
&& chmod +x /usr/local/bin/dumb-init \
&& apk --no-cache del --purge \
curl
dumb-init \
&& mkdir /data \
&& chown -R 1000:1000 /data

COPY --from=builder /go/bin/nginx-sso /usr/local/bin/
COPY --from=builder /go/src/github.com/Luzifer/nginx-sso/config.yaml /usr/local/share/nginx-sso/
@@ -35,6 +33,8 @@ COPY --from=builder /go/src/github.com/Luzifer/nginx-sso/frontend/* /usr/l
EXPOSE 8082
VOLUME ["/data"]

USER 1000:1000

ENTRYPOINT ["/usr/local/bin/docker-start.sh"]
CMD ["--"]

63 changes: 63 additions & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,66 @@
# 0.27.4 / 2024-12-12

* Update Go dependencies

# 0.27.3 / 2023-12-19

* Update dependencies

# 0.27.2 / 2023-10-14

* Update dependencies

# 0.27.1 / 2023-07-29

* [#79] Fix Docker image broken by user change

# 0.27.0 / 2023-07-29

* New Features
* Add support for K8s ingress-nginx "rd" URL parameter
* [#78] Allow for sprig templating in configuration file

* Improvements
* Move Docker image to use non-root user
* Rewrite ACL logic, add support for multiple rules to be applied to the same request and therefore also denying anonymous access to certain requests while allowing it on the general site

* Internal Changes
* Update to Go 1.20, update dependencies
* Update go-oidc to v3
* [ci] Switch to Github Actions

Please note this release makes changes how the ACL is applied. The test cases were (functionally) not altered, so the behavior **should** not change, though you really should test whether your rules are still working fine.

Also if you're using the Docker image please note the default user in the image has changed from root (ID 0) to an unprivileged user (ID 1000). You might need to adjust your config to be read or overwrite the Docker user if you relied on it to be the root user.

# 0.26.0 / 2022-12-21

* Add health-endpoint, fix copy on empty dir
* [#65] Provide Dockerfile for arm64v8 architecture (#66)
* Update dumb-init
* Switch Dockerfile to readonly modules and recent alpine
* Fix: Compiler refuses to convert 0x0 to string
* [ci] Fix missing utils
* Remove vendored libraries

# 0.25.0 / 2020-06-22

* [#62] Add support for multiple domain requirements (#63)
* Add cookie auth key environment variable (#59)

# 0.24.1 / 2020-04-08

* Lint: Fix some minor linter errors
* Fix: Config loading after CookieStore init (#58)

# 0.24.0 / 2020-01-13

* [#50] Handle all 4xx errors as "user not found" (#52)

# 0.23.0 / 2019-12-28

* Allow to configure anonymous access (#48)

# 0.22.0 / 2019-11-03

* Switch to Go1.12+ vendoring
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
publish:
curl -sSLo golang.sh https://raw.githubusercontent.com/Luzifer/github-publish/master/golang.sh
bash golang.sh

# -- Vulnerability scanning --

trivy:
trivy fs . \
--dependency-tree \
--exit-code 1 \
--format table \
--ignore-unfixed \
--quiet \
--scanners config,license,secret,vuln \
--severity HIGH,CRITICAL \
--skip-dirs docs
242 changes: 154 additions & 88 deletions acl.go
Original file line number Diff line number Diff line change
@@ -9,32 +9,145 @@ import (
"github.com/Luzifer/go_helpers/v2/str"
)

type aclRule struct {
Field string `yaml:"field"`
Invert bool `yaml:"invert"`
IsPresent *bool `yaml:"present"`
MatchRegex *string `yaml:"regexp"`
MatchString *string `yaml:"equals"`
}
const (
groupAnonymous = "@_anonymous"
groupAuthenticated = "@_authenticated"
)

func (a aclRule) Validate() error {
if a.Field == "" {
return fmt.Errorf("Field is not set")
type (
acl struct {
RuleSets []aclRuleSet `yaml:"rule_sets"`
}

if a.IsPresent == nil && a.MatchRegex == nil && a.MatchString == nil {
return fmt.Errorf("No matcher (present, regexp, equals) is set")
aclRule struct {
Field string `yaml:"field"`
Invert bool `yaml:"invert"`
IsPresent *bool `yaml:"present"`
MatchRegex *string `yaml:"regexp"`
MatchString *string `yaml:"equals"`
}

if a.MatchRegex != nil {
if _, err := regexp.Compile(*a.MatchRegex); err != nil {
return fmt.Errorf("Regexp is invalid: %s", err)
aclAccessResult uint

aclRuleSet struct {
Rules []aclRule `yaml:"rules"`

Allow []string `yaml:"allow"`
Deny []string `yaml:"deny"`
}
)

const (
accessDunno aclAccessResult = iota
accessAllow
accessDeny
)

// --- ACL

func (a acl) HasAccess(user string, groups []string, r *http.Request) bool {
var (
collectionAllow = map[string]bool{}
collectionDeny = map[string]bool{}
)

for _, rs := range a.RuleSets {
if !rs.AppliesToRequest(r) {
continue
}

// Collect the allows from all matching rulesets
for _, a := range rs.Allow {
collectionAllow[a] = true
}

// Collect the denies from all matching rulesets
for _, d := range rs.Deny {
collectionDeny[d] = true
}
}

// Form lists from the collections
var allowed, denied []string

for k := range collectionAllow {
allowed = append(allowed, k)
}
for k := range collectionDeny {
denied = append(denied, k)
}

return a.checkAccess(user, groups, allowed, denied)
}

func (a acl) Validate() error {
for i, r := range a.RuleSets {
if err := r.Validate(); err != nil {
return fmt.Errorf("RuleSet on position %d is invalid: %s", i+1, err)
}
}

return nil
}

func (a acl) checkAccess(user string, groups, allowed, denied []string) bool {
if !str.StringInSlice(user, []string{"", "\x00"}) {
// The user is set to a non-anon user, we add the pseudo-group
// authenticated to the groups list the user has
groups = append(groups, groupAuthenticated)
} else {
// The user did match anon, therefore we set the pseudo-group
// for anonymous to be used in group matching
groups = []string{groupAnonymous}
}

// Quoting the documentation here:
// "There is a simple logic: Users before groups, denies before allows."

// Lets check the user
if str.StringInSlice(user, denied) {
// Explicit deny on the user, they're out!
return false
}

if str.StringInSlice(user, allowed) {
// Explicit allow on the user, they're in!
return true
}

// The user yielded no result, lets check the groups
for _, group := range groups {
if str.StringInSlice(a.fixGroupName(group), denied) {
// The group is denied access
return false
}

if str.StringInSlice(a.fixGroupName(group), allowed) {
// The group is allowed access
return true
}
}

// We found no match for the user and/or group. Last chance is
// no ruleset denied anonymous access and at least one ruleset
// enabled anonymous access
if !str.StringInSlice(groupAnonymous, denied) && str.StringInSlice(groupAnonymous, allowed) {
return true
}

// We found neither a user nor a group with access or deny config
// so we fall back to the default: No access.
return false
}

func (acl) fixGroupName(group string) string {
return "@" + strings.TrimLeft(group, "@")
}

// --- ACL Rule

// AppliesToFields checks whether the given rule conditions matches
// the given fields
func (a aclRule) AppliesToFields(fields map[string]string) bool {
var field, value string

@@ -89,105 +202,58 @@ func (a aclRule) AppliesToFields(fields map[string]string) bool {
return true
}

type aclAccessResult uint

const (
accessDunno aclAccessResult = iota
accessAllow
accessDeny
)

type aclRuleSet struct {
Rules []aclRule `yaml:"rules"`

Allow []string `yaml:"allow"`
Deny []string `yaml:"deny"`
}
func (a aclRule) Validate() error {
if a.Field == "" {
return fmt.Errorf("field is not set")
}

func (a aclRuleSet) buildFieldSet(r *http.Request) map[string]string {
result := map[string]string{}
if a.IsPresent == nil && a.MatchRegex == nil && a.MatchString == nil {
return fmt.Errorf("no matcher (present, regexp, equals) is set")
}

for k := range r.Header {
result[strings.ToLower(k)] = r.Header.Get(k)
if a.MatchRegex != nil {
if _, err := regexp.Compile(*a.MatchRegex); err != nil {
return fmt.Errorf("regexp is invalid: %s", err)
}
}

return result
return nil
}

func (a aclRuleSet) HasAccess(user string, groups []string, r *http.Request) aclAccessResult {
// --- ACL Rule Set

// AppliesToRequest checks whether every rule in the aclRuleSet
// matches the http.Request. If not this rule-set must not be applied
// to the given request
func (a aclRuleSet) AppliesToRequest(r *http.Request) bool {
fields := a.buildFieldSet(r)

for _, rule := range a.Rules {
if !rule.AppliesToFields(fields) {
// At least one rule does not match the request
return accessDunno
}
}

// All rules do apply to this request, we can judge

if str.StringInSlice(user, a.Deny) {
// Explicit deny, final result
return accessDeny
}

if str.StringInSlice(user, a.Allow) {
// Explicit allow, final result
return accessAllow
}

for _, group := range groups {
if str.StringInSlice("@"+group, a.Deny) {
// Deny through group, final result
return accessDeny
}

if str.StringInSlice("@"+group, a.Allow) {
// Allow through group, final result
return accessAllow
return false
}
}

if str.StringInSlice("@_authenticated", a.Allow) && user != "" {
return accessAllow
}

// Neither user nor group are handled
return accessDunno
return true
}

func (a aclRuleSet) Validate() error {
for i, r := range a.Rules {
if err := r.Validate(); err != nil {
return fmt.Errorf("Rule on position %d is invalid: %s", i+1, err)
}
}

return nil
}

type acl struct {
RuleSets []aclRuleSet `yaml:"rule_sets"`
}

func (a acl) Validate() error {
for i, r := range a.RuleSets {
if err := r.Validate(); err != nil {
return fmt.Errorf("RuleSet on position %d is invalid: %s", i+1, err)
return fmt.Errorf("rule on position %d is invalid: %s", i+1, err)
}
}

return nil
}

func (a acl) HasAccess(user string, groups []string, r *http.Request) bool {
result := accessDunno
func (a aclRuleSet) buildFieldSet(r *http.Request) map[string]string {
result := map[string]string{}

for _, rs := range a.RuleSets {
if intermediateResult := rs.HasAccess(user, groups, r); intermediateResult > result {
result = intermediateResult
}
for k := range r.Header {
result[strings.ToLower(k)] = r.Header.Get(k)
}

return result == accessAllow
return result
}
68 changes: 55 additions & 13 deletions acl_test.go
Original file line number Diff line number Diff line change
@@ -3,11 +3,14 @@ package main
import (
"net/http"
"testing"

"github.com/stretchr/testify/assert"
)

var (
aclTestUser = "test"
aclTestGroups = []string{"group_a", "group_b"}
ptrBoolTrue = func(v bool) *bool { return &v }(true)
)

func aclTestRequest(headers map[string]string) *http.Request {
@@ -49,38 +52,77 @@ func TestRuleSetMatcher(t *testing.T) {
"field_c": "expected",
}

if r.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(fields)) != accessAllow {
t.Error("Access was denied")
}
assert.True(t, r.AppliesToRequest(aclTestRequest(fields)))

delete(fields, "field_c")
if r.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(fields)) != accessDunno {
t.Error("Access was not unknown")
}
assert.False(t, r.AppliesToRequest(aclTestRequest(fields)))
}

func TestGroupAuthenticated(t *testing.T) {
r := aclRuleSet{
a := acl{RuleSets: []aclRuleSet{{
Rules: []aclRule{
{
Field: "field_a",
MatchString: aclTestString("expected"),
},
},
Allow: []string{"@_authenticated"},
}
}}}
fields := map[string]string{
"field_a": "expected",
}

if r.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(fields)) != accessAllow {
t.Error("Access was denied")
assert.True(t, a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(fields)))
assert.False(t, a.HasAccess("\x00", nil, aclTestRequest(fields)), "access to anon user")
assert.False(t, a.HasAccess("", nil, aclTestRequest(fields)), "access to empty user")

a.RuleSets[0].Allow = []string{"testgroup"}
assert.False(t, a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(fields)))
}

func TestAnonymousAccess(t *testing.T) {
a := acl{RuleSets: []aclRuleSet{{
Rules: []aclRule{
{
Field: "field_a",
MatchString: aclTestString("expected"),
},
},
Allow: []string{groupAnonymous},
}}}
fields := map[string]string{
"field_a": "expected",
}

r.Allow = []string{"testgroup"}
if r.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(fields)) == accessAllow {
t.Error("Access was allowed")
assert.True(t, a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(fields)))
assert.True(t, a.HasAccess("", nil, aclTestRequest(fields)), "access to empty user")
assert.True(t, a.HasAccess("\x00", nil, aclTestRequest(fields)), "access to anon user")
}

func TestAnonymousAccessExplicitDeny(t *testing.T) {
a := acl{
RuleSets: []aclRuleSet{
{
Rules: []aclRule{{Field: "field_a", IsPresent: ptrBoolTrue}},
Allow: []string{groupAnonymous},
},
{
Rules: []aclRule{{Field: "field_b", IsPresent: ptrBoolTrue}},
Allow: []string{"somerandomuser"},
Deny: []string{groupAnonymous},
},
},
}

assert.True(t,
a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(map[string]string{"field_a": ""})),
"anon access with only allowed field should be possible")
assert.False(t,
a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(map[string]string{"field_b": ""})),
"anon access with only denied field should not be possible")
assert.False(t,
a.HasAccess(aclTestUser, aclTestGroups, aclTestRequest(map[string]string{"field_a": "", "field_b": ""})),
"anon access with one allowed and one denied field should not be possible")
}

func TestInvertedRegexMatcher(t *testing.T) {
7 changes: 6 additions & 1 deletion core.go
Original file line number Diff line number Diff line change
@@ -14,12 +14,17 @@ import (
)

func registerModules() {
// Start with very simple, local auth providers as they are cheap
// in their execution and therefore if they are used nginx-sso
// can process far more requests than through the other providers
registerAuthenticator(simple.New(cookieStore))
registerAuthenticator(token.New())

// Afterwards utilize the more expensive remove providers
registerAuthenticator(crowd.New())
registerAuthenticator(ldap.New(cookieStore))
registerAuthenticator(google.New(cookieStore))
registerAuthenticator(oidc.New(cookieStore))
registerAuthenticator(token.New())
registerAuthenticator(auth_yubikey.New(cookieStore))

registerMFAProvider(duo.New())
16 changes: 8 additions & 8 deletions docker-start.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
#!/usr/local/bin/dumb-init /bin/bash
#!/usr/bin/dumb-init /bin/bash
set -euo pipefail

# Copy frontend if not available
[ -d /data/frontend ] || cp -r /usr/local/share/nginx-sso/frontend /data/frontend
[ -f /data/frontend/index.html ] || cp -r /usr/local/share/nginx-sso/frontend /data/

[ -e /data/config.yaml ] || {
cp /usr/local/share/nginx-sso/config.yaml /data/config.yaml
echo "An example configuration was copied to /data/config.yaml - You want to edit that one!"
exit 1
cp /usr/local/share/nginx-sso/config.yaml /data/config.yaml
echo "An example configuration was copied to /data/config.yaml - You want to edit that one!"
exit 1
}

echo "Starting nginx-sso"
exec /usr/local/bin/nginx-sso \
--config /data/config.yaml \
--frontend-dir /data/frontend \
$@
--config /data/config.yaml \
--frontend-dir /data/frontend \
$@
91 changes: 57 additions & 34 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,43 +1,66 @@
module github.com/Luzifer/nginx-sso

go 1.13
go 1.23

toolchain go1.23.4

require (
cloud.google.com/go v0.47.0 // indirect
github.com/GeertJohan/yubigo v0.0.0-20190917122436-175bc097e60e
github.com/Luzifer/go_helpers/v2 v2.9.1
github.com/Luzifer/rconfig v1.2.0 // indirect
github.com/Luzifer/rconfig/v2 v2.2.1
github.com/apache/thrift v0.12.0 // indirect
github.com/coreos/go-oidc v2.1.0+incompatible
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect
github.com/gorilla/context v1.1.1
github.com/gorilla/sessions v1.2.0
github.com/hashicorp/golang-lru v0.5.3 // indirect
github.com/Luzifer/go_helpers/v2 v2.25.0
github.com/Luzifer/rconfig/v2 v2.5.2
github.com/Masterminds/sprig/v3 v3.3.0
github.com/coreos/go-oidc/v3 v3.11.0
github.com/duosecurity/duo_api_golang v0.0.0-20240408132100-cb1770897e66
github.com/flosch/pongo2 v0.0.0-20200913210552-0d938eb266f3
github.com/gorilla/context v1.1.2
github.com/gorilla/sessions v1.4.0
github.com/jda/go-crowd v0.0.0-20180225080536-9c6f17811dc6
github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/openzipkin/zipkin-go v0.1.6 // indirect
github.com/pkg/errors v0.8.1
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/pquerna/otp v1.2.0
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829 // indirect
github.com/sirupsen/logrus v1.4.2
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.31.0
golang.org/x/oauth2 v0.24.0
google.golang.org/api v0.211.0
gopkg.in/ldap.v2 v2.5.1
gopkg.in/yaml.v3 v3.0.1
)

require (
cloud.google.com/go/auth v0.12.1 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
cloud.google.com/go/compute/metadata v0.5.2 // indirect
dario.cat/mergo v1.0.1 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.1 // indirect
github.com/boombuler/barcode v1.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.14.0 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.opencensus.io v0.22.1 // indirect
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9 // indirect
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c // indirect
google.golang.org/api v0.13.0
google.golang.org/appengine v1.6.5 // indirect
google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 // indirect
google.golang.org/grpc v1.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect
go.opentelemetry.io/otel v1.32.0 // indirect
go.opentelemetry.io/otel/metric v1.32.0 // indirect
go.opentelemetry.io/otel/trace v1.32.0 // indirect
golang.org/x/net v0.32.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
google.golang.org/grpc v1.68.1 // indirect
google.golang.org/protobuf v1.35.2 // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/ldap.v2 v2.5.1
gopkg.in/square/go-jose.v2 v2.4.0 // indirect
gopkg.in/validator.v2 v2.0.0-20191029180049-30e574a82075 // indirect
gopkg.in/yaml.v2 v2.2.4
gopkg.in/validator.v2 v2.0.1 // indirect
)
440 changes: 124 additions & 316 deletions go.sum

Large diffs are not rendered by default.

67 changes: 54 additions & 13 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
package main

import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"os/signal"
"path"
"strings"
"syscall"
"text/template"

"github.com/Masterminds/sprig/v3"
"github.com/flosch/pongo2"
"github.com/gorilla/context"
"github.com/gorilla/sessions"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"

"github.com/Luzifer/nginx-sso/plugins"
"github.com/Luzifer/rconfig/v2"
@@ -45,6 +47,7 @@ type mainConfig struct {
var (
cfg = struct {
ConfigFile string `flag:"config,c" default:"config.yaml" env:"CONFIG" description:"Location of the configuration file"`
AuthKey string `flag:"authkey" env:"COOKIE_AUTHENTICATION_KEY" description:"Cookie authentication key"`
LogLevel string `flag:"log-level" default:"info" description:"Level of logs to display (debug, info, warn, error)"`
TemplateDir string `flag:"frontend-dir" default:"./frontend/" env:"FRONTEND_DIR" description:"Location of the directory containing the web assets"`
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
@@ -57,6 +60,7 @@ var (
)

func init() {
rconfig.AutoEnv(true)
if err := rconfig.Parse(&cfg); err != nil {
log.WithError(err).Fatal("Unable to parse commandline options")
}
@@ -81,44 +85,68 @@ func init() {
mainCfg.AuditLog.Headers = []string{"x-origin-uri"}
}

func loadConfiguration() error {
yamlSource, err := ioutil.ReadFile(cfg.ConfigFile)
func loadConfiguration() ([]byte, error) {
yamlSource, err := os.ReadFile(cfg.ConfigFile)
if err != nil {
return fmt.Errorf("Unable to read configuration file: %s", err)
return nil, errors.Wrap(err, "reading configuration file")
}

if err = yaml.Unmarshal(yamlSource, &mainCfg); err != nil {
return fmt.Errorf("Unable to load configuration file: %s", err)
tpl, err := template.New("config").Funcs(sprig.FuncMap()).Parse(string(yamlSource))
if err != nil {
return nil, errors.Wrap(err, "parsing config as template")
}

buf := new(bytes.Buffer)
if err = tpl.Execute(buf, nil); err != nil {
return nil, errors.Wrap(err, "executing config as template")
}

if err = yaml.Unmarshal(buf.Bytes(), &mainCfg); err != nil {
return nil, errors.Wrap(err, "loading configuration file")
}

if cfg.AuthKey != "" {
mainCfg.Cookie.AuthKey = cfg.AuthKey
}

return buf.Bytes(), nil
}

func initializeModules(yamlSource []byte) error {
if mainCfg.Plugins.Directory != "" {
if err = loadPlugins(mainCfg.Plugins.Directory); err != nil {
if err := loadPlugins(mainCfg.Plugins.Directory); err != nil {
return errors.Wrap(err, "Unable to load plugins")
}
}

if err = initializeAuthenticators(yamlSource); err != nil {
if err := initializeAuthenticators(yamlSource); err != nil {
return fmt.Errorf("Unable to configure authentication: %s", err)
}

if err = initializeMFAProviders(yamlSource); err != nil {
if err := initializeMFAProviders(yamlSource); err != nil {
log.WithError(err).Fatal("Unable to configure MFA providers")
}

return nil
}

func main() {
yamlSource, err := loadConfiguration()
if err != nil {
log.WithError(err).Fatal("Unable to load configuration")
}

cookieStore = sessions.NewCookieStore([]byte(mainCfg.Cookie.AuthKey))
registerModules()

if err := loadConfiguration(); err != nil {
log.WithError(err).Fatal("Unable to load configuration")
if err = initializeModules(yamlSource); err != nil {
log.WithError(err).Fatal("Unable to initialize modules")
}

http.HandleFunc("/", handleRootRequest)
http.HandleFunc("/auth", handleAuthRequest)
http.HandleFunc("/debug", handleLoginDebug)
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("OK")) })
http.HandleFunc("/login", handleLoginRequest)
http.HandleFunc("/logout", handleLogoutRequest)

@@ -133,8 +161,13 @@ func main() {
for sig := range sigChan {
switch sig {
case syscall.SIGHUP:
if err := loadConfiguration(); err != nil {
if yamlSource, err = loadConfiguration(); err != nil {
log.WithError(err).Error("Unable to reload configuration")
continue
}

if err = initializeModules(yamlSource); err != nil {
log.WithError(err).Error("Unable to initialize modules")
}

default:
@@ -153,6 +186,14 @@ func handleAuthRequest(res http.ResponseWriter, r *http.Request) {

switch err {
case plugins.ErrNoValidUserFound:
// No valid user found, check whether special anonymous "user" has access
// Username is set to 0x0 character to prevent accidental whitelist-match
if mainCfg.ACL.HasAccess(string(byte(0x0)), nil, r) {
mainCfg.AuditLog.Log(auditEventValidate, r, map[string]string{"result": "anonymous access granted"}) // #nosec G104 - This is only logging
res.WriteHeader(http.StatusOK)
return
}

mainCfg.AuditLog.Log(auditEventValidate, r, map[string]string{"result": "no valid user found"}) // #nosec G104 - This is only logging
http.Error(res, "No valid user found", http.StatusUnauthorized)

2 changes: 1 addition & 1 deletion plugins/auth/crowd/auth.go
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import (

crowd "github.com/jda/go-crowd"
log "github.com/sirupsen/logrus"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"

"github.com/Luzifer/nginx-sso/plugins"
)
27 changes: 21 additions & 6 deletions plugins/auth/google/auth.go
Original file line number Diff line number Diff line change
@@ -11,11 +11,12 @@ import (
"golang.org/x/oauth2/google"
v2 "google.golang.org/api/oauth2/v2"
"google.golang.org/api/option"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"

"github.com/gorilla/sessions"
"github.com/pkg/errors"

"github.com/Luzifer/go_helpers/v2/str"
"github.com/Luzifer/nginx-sso/plugins"
)

@@ -30,8 +31,9 @@ type AuthGoogleOAuth struct {
ClientSecret string `yaml:"client_secret"`
RedirectURL string `yaml:"redirect_url"`

RequireDomain string `yaml:"require_domain"`
UserIDMethod string `yaml:"user_id_method"`
RequireDomain string `yaml:"require_domain"` // Deprecated: Use RequireDomains
RequireDomains []string `yaml:"require_domains"`
UserIDMethod string `yaml:"user_id_method"`

cookie plugins.CookieConfig
cookieStore *sessions.CookieStore
@@ -77,7 +79,15 @@ func (a *AuthGoogleOAuth) Configure(yamlSource []byte) (err error) {
a.ClientID = envelope.Providers.GoogleOAuth.ClientID
a.ClientSecret = envelope.Providers.GoogleOAuth.ClientSecret
a.RedirectURL = envelope.Providers.GoogleOAuth.RedirectURL
a.RequireDomain = envelope.Providers.GoogleOAuth.RequireDomain
a.RequireDomains = envelope.Providers.GoogleOAuth.RequireDomains

if len(envelope.Providers.GoogleOAuth.RequireDomain) > 0 {
// Migration for old configuration with only single require_domain
a.RequireDomains = append(
a.RequireDomains,
envelope.Providers.GoogleOAuth.RequireDomain,
)
}

if envelope.Providers.GoogleOAuth.UserIDMethod != "" {
a.UserIDMethod = envelope.Providers.GoogleOAuth.UserIDMethod
@@ -216,15 +226,20 @@ func (a *AuthGoogleOAuth) getUserFromToken(ctx context.Context, token *oauth2.To
httpClient := conf.Client(ctx, token)
client, err := v2.NewService(ctx, option.WithHTTPClient(httpClient))
if err != nil {
return "", errors.Wrap(err, "Unable to instanciate OAuth2 API service")
return "", errors.Wrap(err, "Unable to instantiate OAuth2 API service")
}

tok, err := client.Tokeninfo().Context(ctx).Do()
if err != nil {
return "", errors.Wrap(err, "Unable to fetch token-info")
}

if a.RequireDomain != "" && !strings.HasSuffix(tok.Email, "@"+a.RequireDomain) {
var mailParts = strings.Split(tok.Email, "@")
if len(mailParts) != 2 {
return "", errors.New("Invalid email returned")
}

if len(a.RequireDomains) > 0 && !str.StringInSlice(mailParts[1], a.RequireDomains) {
// E-Mail domain is enforced, ignore all other users
return "", plugins.ErrNoValidUserFound
}
2 changes: 1 addition & 1 deletion plugins/auth/ldap/auth.go
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import (
"strings"

ldap "gopkg.in/ldap.v2"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"

"github.com/gorilla/sessions"

21 changes: 17 additions & 4 deletions plugins/auth/oidc/auth.go
Original file line number Diff line number Diff line change
@@ -5,12 +5,13 @@ import (
"encoding/gob"
"fmt"
"net/http"
"regexp"
"strings"

"golang.org/x/oauth2"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"

"github.com/coreos/go-oidc"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/gorilla/sessions"
"github.com/pkg/errors"

@@ -23,6 +24,8 @@ const (
userIDMethodSubject = "subject"
)

var http4xxErrorResponse = regexp.MustCompile(`^(4[0-9]{2}) (.*)`)

type AuthOIDC struct {
ClientID string `yaml:"client_id"`
ClientSecret string `yaml:"client_secret"`
@@ -224,10 +227,20 @@ func (a *AuthOIDC) getOAuthConfig() *oauth2.Config {
func (a *AuthOIDC) getUserFromToken(ctx context.Context, token *oauth2.Token) (string, error) {
ui, err := a.provider.UserInfo(ctx, oauth2.StaticTokenSource(token))
if err != nil {
if strings.Contains(err.Error(), "401 Unauthorized") {
// Handle Unauthorized as no user found instead of generic error
if http4xxErrorResponse.MatchString(err.Error()) {
/*
* Server answered with any 4xx error
*
* Google OIDC: 401 Unauthorized => Token expired
* Wordpress OIDC plugin: 400 Bad Request => Token expired
*
* As long as they can't agree on ONE status for that we need to
* handle all 4xx as "token expired" and therefore "no valid user"
*/
return "", plugins.ErrNoValidUserFound
}

// Other error: Report the error
return "", errors.Wrap(err, "Unable to fetch user info")
}

2 changes: 1 addition & 1 deletion plugins/auth/simple/auth.go
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import (
"strings"

"golang.org/x/crypto/bcrypt"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"

"github.com/gorilla/sessions"

2 changes: 1 addition & 1 deletion plugins/auth/token/auth.go
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import (
"github.com/Luzifer/go_helpers/v2/str"
"github.com/Luzifer/nginx-sso/plugins"

yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"
)

type AuthToken struct {
2 changes: 1 addition & 1 deletion plugins/auth/yubikey/auth.go
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import (

"github.com/GeertJohan/yubigo"
"github.com/gorilla/sessions"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"

"github.com/Luzifer/go_helpers/v2/str"
"github.com/Luzifer/nginx-sso/plugins"
2 changes: 1 addition & 1 deletion plugins/mfa/duo/mfa.go
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ import (
duoapi "github.com/duosecurity/duo_api_golang"
"github.com/duosecurity/duo_api_golang/authapi"
"github.com/pkg/errors"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"

"github.com/Luzifer/nginx-sso/plugins"
)
2 changes: 1 addition & 1 deletion plugins/mfa/totp/mfa.go
Original file line number Diff line number Diff line change
@@ -93,7 +93,7 @@ func (m MFATOTP) exec(c plugins.MFAConfig) (string, error) {
}

if n := len(secret) % 8; n != 0 {
secret = secret + strings.Repeat("=", 8-n)
secret += strings.Repeat("=", 8-n)
}

return totp.GenerateCodeCustom(strings.ToUpper(secret), time.Now(), generatorOpts)
2 changes: 1 addition & 1 deletion plugins/mfa/yubikey/mfa.go
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import (

"github.com/GeertJohan/yubigo"
"github.com/pkg/errors"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"

"github.com/Luzifer/nginx-sso/plugins"
)
22 changes: 16 additions & 6 deletions redirect.go
Original file line number Diff line number Diff line change
@@ -10,9 +10,10 @@ import (

func getRedirectURL(r *http.Request, fallback string) (string, error) {
var (
params url.Values
redirURL string
sessURL string
params url.Values
redirURL string
removeParam string
sessURL string
)

if cookieStore != nil {
@@ -23,9 +24,16 @@ func getRedirectURL(r *http.Request, fallback string) (string, error) {
}

switch {
case r.URL.Query().Get("rd") != "":
// We have a GET request with a "rd" query param (K8s ingress-nginx)
redirURL = r.URL.Query().Get("rd")
removeParam = "rd"
params = r.URL.Query()

case r.URL.Query().Get("go") != "":
// We have a GET request, use "go" query param
redirURL = r.URL.Query().Get("go")
removeParam = "go"
params = r.URL.Query()

case r.FormValue("go") != "":
@@ -42,13 +50,15 @@ func getRedirectURL(r *http.Request, fallback string) (string, error) {
return fallback, nil
}

// Remove the "go" parameter as it is a parameter for nginx-sso
params.Del("go")
// Remove the redirect parameter as it is a parameter for nginx-sso
if removeParam != "" {
params.Del(removeParam)
}

// Parse given URL to extract attached parameters
pURL, err := url.Parse(redirURL)
if err != nil {
return "", errors.Wrap(err, "Unable to parse redirect URL")
return "", errors.Wrap(err, "parsing redirect URL")
}

// Transfer parameters from URL to params set
202 changes: 0 additions & 202 deletions vendor/cloud.google.com/go/LICENSE

This file was deleted.

12 changes: 0 additions & 12 deletions vendor/cloud.google.com/go/compute/metadata/.repo-metadata.json

This file was deleted.

526 changes: 0 additions & 526 deletions vendor/cloud.google.com/go/compute/metadata/metadata.go

This file was deleted.

5 changes: 0 additions & 5 deletions vendor/github.com/GeertJohan/yubigo/.gitignore

This file was deleted.

22 changes: 0 additions & 22 deletions vendor/github.com/GeertJohan/yubigo/LICENSE

This file was deleted.

105 changes: 0 additions & 105 deletions vendor/github.com/GeertJohan/yubigo/readme.md

This file was deleted.

580 changes: 0 additions & 580 deletions vendor/github.com/GeertJohan/yubigo/yubigo.go

This file was deleted.

202 changes: 0 additions & 202 deletions vendor/github.com/Luzifer/go_helpers/v2/LICENSE

This file was deleted.

21 changes: 0 additions & 21 deletions vendor/github.com/Luzifer/go_helpers/v2/str/slice.go

This file was deleted.

12 changes: 0 additions & 12 deletions vendor/github.com/Luzifer/rconfig/v2/.travis.yml

This file was deleted.

26 changes: 0 additions & 26 deletions vendor/github.com/Luzifer/rconfig/v2/History.md

This file was deleted.

202 changes: 0 additions & 202 deletions vendor/github.com/Luzifer/rconfig/v2/LICENSE

This file was deleted.

88 changes: 0 additions & 88 deletions vendor/github.com/Luzifer/rconfig/v2/README.md

This file was deleted.

64 changes: 0 additions & 64 deletions vendor/github.com/Luzifer/rconfig/v2/autoenv.go

This file was deleted.

456 changes: 0 additions & 456 deletions vendor/github.com/Luzifer/rconfig/v2/config.go

This file was deleted.

7 changes: 0 additions & 7 deletions vendor/github.com/Luzifer/rconfig/v2/go.mod

This file was deleted.

7 changes: 0 additions & 7 deletions vendor/github.com/Luzifer/rconfig/v2/go.sum

This file was deleted.

27 changes: 0 additions & 27 deletions vendor/github.com/Luzifer/rconfig/v2/vardefault_providers.go

This file was deleted.

1 change: 0 additions & 1 deletion vendor/github.com/boombuler/barcode/.gitignore

This file was deleted.

21 changes: 0 additions & 21 deletions vendor/github.com/boombuler/barcode/LICENSE

This file was deleted.

53 changes: 0 additions & 53 deletions vendor/github.com/boombuler/barcode/README.md

This file was deleted.

42 changes: 0 additions & 42 deletions vendor/github.com/boombuler/barcode/barcode.go

This file was deleted.

1 change: 0 additions & 1 deletion vendor/github.com/boombuler/barcode/go.mod

This file was deleted.

66 changes: 0 additions & 66 deletions vendor/github.com/boombuler/barcode/qr/alphanumeric.go

This file was deleted.

23 changes: 0 additions & 23 deletions vendor/github.com/boombuler/barcode/qr/automatic.go

This file was deleted.

59 changes: 0 additions & 59 deletions vendor/github.com/boombuler/barcode/qr/blocks.go

This file was deleted.

416 changes: 0 additions & 416 deletions vendor/github.com/boombuler/barcode/qr/encoder.go

This file was deleted.

29 changes: 0 additions & 29 deletions vendor/github.com/boombuler/barcode/qr/errorcorrection.go

This file was deleted.

56 changes: 0 additions & 56 deletions vendor/github.com/boombuler/barcode/qr/numeric.go

This file was deleted.

166 changes: 0 additions & 166 deletions vendor/github.com/boombuler/barcode/qr/qrcode.go

This file was deleted.

27 changes: 0 additions & 27 deletions vendor/github.com/boombuler/barcode/qr/unicode.go

This file was deleted.

310 changes: 0 additions & 310 deletions vendor/github.com/boombuler/barcode/qr/versioninfo.go

This file was deleted.

Loading