From 4668245249896b4f50ffb6d662c98ced69f5bc86 Mon Sep 17 00:00:00 2001 From: Aaron Elkiss Date: Wed, 3 Mar 2021 17:51:33 -0500 Subject: [PATCH 1/2] Custom auth proxy connector with additional headers --- connector/authproxyshib/authproxy_shib.go | 125 ++++++++++++++++++++++ server/server.go | 2 + 2 files changed, 127 insertions(+) create mode 100644 connector/authproxyshib/authproxy_shib.go diff --git a/connector/authproxyshib/authproxy_shib.go b/connector/authproxyshib/authproxy_shib.go new file mode 100644 index 0000000000..184bd70793 --- /dev/null +++ b/connector/authproxyshib/authproxy_shib.go @@ -0,0 +1,125 @@ +// Package authproxyshib implements a connector which relies on external +// authentication (e.g. mod_auth in Apache2) and returns an identity with +// claims populated with configurable header values. +// +// The primary use is to proxy a SAML SP running Shibboleth to OIDC +package authproxyshib + +import ( + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/dexidp/dex/connector" + "github.com/dexidp/dex/pkg/log" +) + +// Config holds the configuration parameters for a connector which returns an +// identity with the HTTP header X-Remote-User as verified email. +type Config struct { + UserIDHeader string `json:"userIDHeader"` + UsernameHeader string `json:"usernameHeader"` + PreferredUsernameHeader string `json:"preferredUsernameHeader"` + EmailHeader string `json:"emailHeader"` + EmailVerifiedIfPresent bool `json:"emailVerifiedIfPresent"` + GroupsHeader string `json:"groupsHeader"` + GroupsDelimiter string `json:"groupsDelimiter"` +} + +// Open returns an authentication strategy which requires no user interaction. +func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) { + userIDHeader := c.UserIDHeader + if userIDHeader == "" { + userIDHeader = "X-Remote-User" + } + + groupsDelimiter := c.GroupsDelimiter + if groupsDelimiter == "" { + groupsDelimiter = ";" + } + + return &callback{ + userIDHeader: userIDHeader, + usernameHeader: c.UsernameHeader, + preferredUsernameHeader: c.PreferredUsernameHeader, + emailHeader: c.EmailHeader, + emailVerifiedIfPresent: c.EmailVerifiedIfPresent, + groupsHeader: c.GroupsHeader, + groupsDelimiter: groupsDelimiter, + logger: logger, + pathSuffix: "/" + id, + }, nil +} + +// Callback is a connector which returns an identity with the HTTP header +// X-Remote-User as verified email. +type callback struct { + userIDHeader string + usernameHeader string + preferredUsernameHeader string + emailHeader string + emailVerifiedIfPresent bool + groupsHeader string + groupsDelimiter string + logger log.Logger + pathSuffix string +} + +// LoginURL returns the URL to redirect the user to login with. +func (m *callback) LoginURL(s connector.Scopes, callbackURL, state string) (string, error) { + u, err := url.Parse(callbackURL) + if err != nil { + return "", fmt.Errorf("failed to parse callbackURL %q: %v", callbackURL, err) + } + u.Path += m.pathSuffix + v := u.Query() + v.Set("state", state) + u.RawQuery = v.Encode() + return u.String(), nil +} + +// HandleCallback parses the request and returns the user's identity +func (m *callback) HandleCallback(s connector.Scopes, r *http.Request) (connector.Identity, error) { + m.logger.Debugf("Headers: %v", r.Header) + userID := r.Header.Get(m.userIDHeader) + if userID == "" { + return connector.Identity{}, fmt.Errorf("required HTTP header %s is not set", m.userIDHeader) + } + + identity := connector.Identity{ + UserID: userID, + } + + if m.usernameHeader != "" { + username := r.Header.Get(m.usernameHeader) + if username != "" { + identity.Username = username + } + } + + if m.preferredUsernameHeader != "" { + preferredUsername := r.Header.Get(m.preferredUsernameHeader) + if preferredUsername != "" { + identity.PreferredUsername = preferredUsername + } + } + + if m.emailHeader != "" { + email := r.Header.Get(m.emailHeader) + if email != "" { + identity.Email = email + // TODO: what happens if missing from the config? + identity.EmailVerified = m.emailVerifiedIfPresent + } + } + + if m.groupsHeader != "" { + groups := r.Header.Get(m.groupsHeader) + if groups != "" { + identity.Groups = strings.Split(groups, m.groupsDelimiter) + } + } + + return identity, nil +} diff --git a/server/server.go b/server/server.go index a79b7cfd3b..7e9e8bf015 100644 --- a/server/server.go +++ b/server/server.go @@ -25,6 +25,7 @@ import ( "github.com/dexidp/dex/connector" "github.com/dexidp/dex/connector/atlassiancrowd" "github.com/dexidp/dex/connector/authproxy" + "github.com/dexidp/dex/connector/authproxyshib" "github.com/dexidp/dex/connector/bitbucketcloud" "github.com/dexidp/dex/connector/gitea" "github.com/dexidp/dex/connector/github" @@ -503,6 +504,7 @@ var ConnectorsConfig = map[string]func() ConnectorConfig{ "oidc": func() ConnectorConfig { return new(oidc.Config) }, "saml": func() ConnectorConfig { return new(saml.Config) }, "authproxy": func() ConnectorConfig { return new(authproxy.Config) }, + "authproxy-shib": func() ConnectorConfig { return new(authproxyshib.Config) }, "linkedin": func() ConnectorConfig { return new(linkedin.Config) }, "microsoft": func() ConnectorConfig { return new(microsoft.Config) }, "bitbucket-cloud": func() ConnectorConfig { return new(bitbucketcloud.Config) }, From 17e15e609bdbfe1bcfcd3e75036f0a365e9e925d Mon Sep 17 00:00:00 2001 From: Aaron Elkiss Date: Tue, 24 Nov 2020 13:54:33 -0500 Subject: [PATCH 2/2] github actions for local ht shib proxy dex --- .github/workflows/build-master.yml | 25 +++++++ .github/workflows/{ci.yaml => ci.yml} | 10 ++- .github/workflows/codeql-analysis.yaml | 67 ------------------ .github/workflows/deploy-test.yml | 20 ++++++ .github/workflows/deploy.yml | 20 ++++++ .github/workflows/docker.yaml | 94 -------------------------- .github/workflows/tag-release.yml | 26 +++++++ 7 files changed, 95 insertions(+), 167 deletions(-) create mode 100644 .github/workflows/build-master.yml rename .github/workflows/{ci.yaml => ci.yml} (93%) delete mode 100644 .github/workflows/codeql-analysis.yaml create mode 100644 .github/workflows/deploy-test.yml create mode 100644 .github/workflows/deploy.yml delete mode 100644 .github/workflows/docker.yaml create mode 100644 .github/workflows/tag-release.yml diff --git a/.github/workflows/build-master.yml b/.github/workflows/build-master.yml new file mode 100644 index 0000000000..d46c58fd6e --- /dev/null +++ b/.github/workflows/build-master.yml @@ -0,0 +1,25 @@ +name: Docker Build master + +on: + push: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Clone latest repository + uses: actions/checkout@v2 + - name: Build dex container image and push to DockerHub + uses: docker/build-push-action@v2 + with: + push: true + tags: 'hathitrust/dex-shib-proxy-unstable:${{ github.sha }},hathitrust/dex-shib-proxy-unstable:latest' + file: Dockerfile diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yml similarity index 93% rename from .github/workflows/ci.yaml rename to .github/workflows/ci.yml index 0ea01b98d0..0adf5faed1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yml @@ -47,19 +47,17 @@ jobs: steps: - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v1 with: go-version: 1.16 - name: Checkout code uses: actions/checkout@v2 - - name: Start services - run: docker-compose -f docker-compose.test.yaml up -d - - - name: Test + - name: Run tests run: make testall env: + DEX_FOO_USER_PASSWORD: $2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy DEX_MYSQL_DATABASE: dex DEX_MYSQL_USER: root DEX_MYSQL_PASSWORD: root @@ -79,7 +77,7 @@ jobs: DEX_KEYSTONE_ADMIN_USER: demo DEX_KEYSTONE_ADMIN_PASS: DEMO_PASS - - name: Lint + - name: Run linter run: make lint # Ensure proto generation doesn't depend on external packages. diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml deleted file mode 100644 index 0c0b47c94e..0000000000 --- a/.github/workflows/codeql-analysis.yaml +++ /dev/null @@ -1,67 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ master, v1 ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ master ] - schedule: - - cron: '28 10 * * 6' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - language: [ 'go' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml new file mode 100644 index 0000000000..b3ab9f5eca --- /dev/null +++ b/.github/workflows/deploy-test.yml @@ -0,0 +1,20 @@ +name: Manual Deploy (Testing) + +on: + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: azure/setup-kubectl@v1 + - name: Authenticate with kubernetes + run: | + mkdir -p ${HOME}/.kube/certs/cluster + echo ${{ secrets.KUBERNETES_CA }} | base64 -d > ${HOME}/.kube/certs/cluster/k8s-ca.crt + kubectl config set-cluster cluster --certificate-authority=${HOME}/.kube/certs/cluster/k8s-ca.crt --server=https://macc.kubernetes.hathitrust.org + kubectl config set-credentials github --token=${{ secrets.KUBERNETES_TOKEN }} + kubectl config set-context github --cluster=cluster --user=github --namespace=oidc-saml-proxy-testing + kubectl config use-context github + - name: Manual Deploy (Testing) + run: kubectl set image deployment oidc-saml-proxy oidc-saml-proxy=hathitrust/dex-shib-proxy-unstable:latest diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000..7010701965 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,20 @@ +name: Manual Deploy (Production) + +on: + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: azure/setup-kubectl@v1 + - name: Authenticate with kubernetes + run: | + mkdir -p ${HOME}/.kube/certs/cluster + echo ${{ secrets.KUBERNETES_CA }} | base64 -d > ${HOME}/.kube/certs/cluster/k8s-ca.crt + kubectl config set-cluster cluster --certificate-authority=${HOME}/.kube/certs/cluster/k8s-ca.crt --server=https://macc.kubernetes.hathitrust.org + kubectl config set-credentials github --token=${{ secrets.KUBERNETES_TOKEN }} + kubectl config set-context github --cluster=cluster --user=github --namespace=oidc-saml-proxy-production + kubectl config use-context github + - name: Manual Deploy (Production) + run: kubectl set image deployment oidc-saml-proxy oidc-saml-proxy=hathitrust/dex-shib-proxy:latest diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml deleted file mode 100644 index 5fd46ebcc4..0000000000 --- a/.github/workflows/docker.yaml +++ /dev/null @@ -1,94 +0,0 @@ -name: Docker - -on: - push: - branches: - - master - tags: - - v[0-9]+.[0-9]+.[0-9]+ - pull_request: - -jobs: - docker: - name: Docker - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Calculate Docker image tags - id: tags - env: - DOCKER_IMAGES: "ghcr.io/dexidp/dex dexidp/dex" - run: | - case $GITHUB_REF in - refs/tags/*) VERSION=${GITHUB_REF#refs/tags/};; - refs/heads/*) VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g');; - refs/pull/*) VERSION=pr-${{ github.event.number }};; - *) VERSION=sha-${GITHUB_SHA::8};; - esac - - TAGS=() - for image in $DOCKER_IMAGES; do - TAGS+=("${image}:${VERSION}") - - if [[ "${{ github.event.repository.default_branch }}" == "$VERSION" ]]; then - TAGS+=("${image}:latest") - fi - done - - echo ::set-output name=version::${VERSION} - echo ::set-output name=tags::$(IFS=,; echo "${TAGS[*]}") - echo ::set-output name=commit_hash::${GITHUB_SHA::8} - echo ::set-output name=build_date::$(git show -s --format=%cI) - - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - with: - platforms: all - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - with: - install: true - version: latest - # TODO: Remove driver-opts once fix is released docker/buildx#386 - driver-opts: image=moby/buildkit:master - - - name: Login to GitHub Container Registry - uses: docker/login-action@v1 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.CR_PAT }} - if: github.event_name == 'push' - - - name: Login to Docker Hub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - if: github.event_name == 'push' - - - name: Build and push - uses: docker/build-push-action@v2 - with: - context: . - platforms: linux/amd64,linux/arm/v7,linux/arm64 - push: ${{ github.event_name == 'push' }} - tags: ${{ steps.tags.outputs.tags }} - build-args: | - VERSION=${{ steps.tags.outputs.version }} - COMMIT_HASH=${{ steps.tags.outputs.commit_hash }} - BUILD_DATE=${{ steps.tags.outputs.build_date }} - labels: | - org.opencontainers.image.title=${{ github.event.repository.name }} - org.opencontainers.image.description=${{ github.event.repository.description }} - org.opencontainers.image.url=${{ github.event.repository.html_url }} - org.opencontainers.image.source=${{ github.event.repository.clone_url }} - org.opencontainers.image.version=${{ steps.tags.outputs.version }} - org.opencontainers.image.created=${{ steps.tags.outputs.build_date }} - org.opencontainers.image.revision=${{ github.sha }} - org.opencontainers.image.licenses=${{ github.event.repository.license.spdx_id }} - org.opencontainers.image.documentation=https://dexidp.io/docs/ diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml new file mode 100644 index 0000000000..9ed28ccafa --- /dev/null +++ b/.github/workflows/tag-release.yml @@ -0,0 +1,26 @@ +name: Docker Tag Latest Release + +on: + release: + types: [ released ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Clone latest repository + uses: actions/checkout@v2 + - name: Tag latest release in DockerHub + run: | + docker pull hathitrust/dex-shib-proxy-unstable:${{ github.sha }} + docker tag hathitrust/dex-shib-proxy-unstable:${{ github.sha }} hathitrust/dex-shib-proxy:${{ github.event.release.tag_name }} + docker tag hathitrust/dex-shib-proxy-unstable:${{ github.sha }} hathitrust/dex-shib-proxy:latest + docker push hathitrust/dex-shib-proxy:${{ github.event.release.tag_name }} + docker push hathitrust/dex-shib-proxy:latest