Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# .github/workflows/build-simple.yml
# 简化版本 - 只构建Linux版本
name: Build Vouch Proxy (Simple)

on:
push:
branches: [ feat-* ]
tags: [ 'v*' ]
pull_request:
branches: [ main, master ]
workflow_dispatch:

jobs:
build-linux:
name: Build Linux Binary
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.23'

- name: Get dependencies
run: go mod download

- name: Build Linux binary
env:
CGO_ENABLED: 0
GOOS: linux
GOARCH: amd64
run: |
# 获取构建信息
VERSION=$(git describe --always --long --dirty 2>/dev/null || echo "dev-$(date +%s)")
DT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
BRANCH=$(git rev-parse --abbrev-ref HEAD)
SEMVER=$(git tag --list --sort="-version:refname" | head -n1 || echo "v0.0.0-dev")

echo "Building version: $VERSION"
echo "Build date: $DT"
echo "Branch: $BRANCH"
echo "Semver: $SEMVER"

# 构建
go build -v -trimpath \
-ldflags="-w -s -X main.version=$VERSION -X main.uname=Linux -X main.builddt=$DT -X main.host=github-actions -X main.semver=$SEMVER -X main.branch=$BRANCH" \
-o vouch-proxy .

# 检查文件
ls -la vouch-proxy
chmod +x vouch-proxy
./vouch-proxy -version || echo "Build completed"

- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: vouch-proxy-linux
path: vouch-proxy
retention-days: 30

# 如果是tag推送,创建release
- name: Create Release
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v1
with:
files: vouch-proxy
name: Release ${{ github.ref_name }}
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5 changes: 1 addition & 4 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
name: coverage

on:
workflow_dispatch:
push:
pull_request:


jobs:
coverage:
Expand Down
5 changes: 1 addition & 4 deletions .github/workflows/docker-release-arm.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
name: Docker build and push voucher/vouch-proxy:latest-arm

on:
push:
branches:
- master


jobs:
publish-to-docker-arm:
Expand Down
7 changes: 1 addition & 6 deletions .github/workflows/docker-release-quayio-alpine.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
name: Publish Docker image to Quay.io using Dockerfile.alpine

on:
push:
branches:
- master
tags:
- 'v*'



jobs:
Expand Down
7 changes: 1 addition & 6 deletions .github/workflows/docker-release-quayio.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
name: Publish Docker image to Quay.io

on:
push:
branches:
- master
tags:
- 'v*'



jobs:
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/notify-irc.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
name: "Push Notification"
on: [push, pull_request, create]

jobs:
notify-irc:
Expand Down
1 change: 0 additions & 1 deletion handlers/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ func AuthStateHandler(w http.ResponseWriter, r *http.Request) {
oauth2.SetAuthURLParam("code_verifier", session.Values["codeVerifier"].(string)),
}
}

if err := getUserInfo(r, &user, &customClaims, &ptokens, authCodeOptions...); err != nil {
responses.Error400(w, r, fmt.Errorf("/auth Error while retrieving user info after successful login at the OAuth provider: %w", err))
return
Expand Down
2 changes: 2 additions & 0 deletions handlers/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ var (
func ValidateRequestHandler(w http.ResponseWriter, r *http.Request) {
fastlog.Debug("/validate")



jwt := jwtmanager.FindJWT(r)
if jwt == "" {
send401or200PublicAccess(w, r, errNoJWT)
Expand Down
9 changes: 6 additions & 3 deletions pkg/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,13 @@ type Config struct {
TeamWhiteList []string `mapstructure:"teamWhitelist"`
AllowAllUsers bool `mapstructure:"allowAllUsers"`
PublicAccess bool `mapstructure:"publicAccess"`
IPWhiteList []string `mapstructure:"ipWhitelist"`
TLS struct {
Cert string `mapstructure:"cert"`
Key string `mapstructure:"key"`
Profile string `mapstructure:"profile"`
Cert string `mapstructure:"cert"`
Key string `mapstructure:"key"`
Profile string `mapstructure:"profile"`
ClientCertFile string `mapstructure:"clientCertFile"`
ClientKeyFile string `mapstructure:"clientKeyFile"`
}
JWT struct {
SigningMethod string `mapstructure:"signing_method"`
Expand Down
24 changes: 24 additions & 0 deletions pkg/jwtmanager/jwtcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ OR CONDITIONS OF ANY KIND, either express or implied.
package jwtmanager

import (
"net"
"net/http"
"strings"
"time"
Expand Down Expand Up @@ -50,6 +51,29 @@ func cacheConfigure() {
// all tests for JWTCacheHandler are present in `handlers/validate_test.go` to avoid circular imports
func JWTCacheHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// check to see if the request is from a whitelisted IP
ipWhitelist := cfg.Cfg.IPWhiteList
realIP := r.Header.Get("X-Forwarded-For")
if realIP == "" {
realIP = r.Header.Get("X-Real-IP")
}
if realIP == "" {
realIP, _, _ = net.SplitHostPort(r.RemoteAddr)
}
if strings.Contains(realIP, ",") {
parts := strings.Split(realIP, ",")
realIP = strings.TrimSpace(parts[0])
}
log.Debugf("request ip: %s", realIP)
for _, wip := range ipWhitelist {
if realIP == wip {
logger.Debug("IP whitelisted, access granted")
w.Header().Add(cfg.Cfg.Headers.User, "whitelisted")
w.Header().Add(cfg.Cfg.Headers.Success, "true")
responses.OK200(w, r)
return
}
}

jwt := FindJWT(r)

Expand Down
49 changes: 47 additions & 2 deletions pkg/providers/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package common

import (
"context"
"crypto/tls"
"encoding/json"
"net/http"

Expand All @@ -31,7 +32,9 @@ func Configure() {

// PrepareTokensAndClient setup the client, usually for a UserInfo request
func PrepareTokensAndClient(r *http.Request, ptokens *structs.PTokens, setProviderToken bool, opts ...oauth2.AuthCodeOption) (*http.Client, *oauth2.Token, error) {
providerToken, err := cfg.OAuthClient.Exchange(context.TODO(), r.URL.Query().Get("code"), opts...)
sslClient := ClientWithCert(&http.Client{})
ctx := context.WithValue(context.TODO(), oauth2.HTTPClient, sslClient)
providerToken, err := cfg.OAuthClient.Exchange(ctx, r.URL.Query().Get("code"), opts...)
if err != nil {
return nil, nil, err
}
Expand All @@ -49,7 +52,49 @@ func PrepareTokensAndClient(r *http.Request, ptokens *structs.PTokens, setProvid

log.Debugf("ptokens: accessToken length: %d, IdToken length: %d", len(ptokens.PAccessToken), len(ptokens.PIdToken))
client := cfg.OAuthClient.Client(context.TODO(), providerToken)
return client, providerToken, err

return ClientWithCert(client), providerToken, err
}

func ClientWithCert(client *http.Client) *http.Client {
log.Debugf("ClientWithCert")
certFile := cfg.Cfg.TLS.ClientCertFile
keyFile := cfg.Cfg.TLS.ClientKeyFile
if certFile == "" || keyFile == "" {
log.Debugf("client ssl is null")
return client
}
// 加载客户端证书
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
log.Debugf("client ssl load error: %v", err)
return client
}

// 给 Transport 注入 TLS,仅发送客户端证书
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
}

// 给 Transport 注入 TLS
switch tr := client.Transport.(type) {
case *http.Transport:
tr.TLSClientConfig = tlsConfig
case *oauth2.Transport: // 如果是 oauth2.Transport 包装的
if baseTr, ok := tr.Base.(*http.Transport); ok {
baseTr.TLSClientConfig = tlsConfig
} else {
tr.Base = &http.Transport{
TLSClientConfig: tlsConfig,
}
}
default:
client.Transport = &http.Transport{
TLSClientConfig: tlsConfig,
}
}
log.Debugf("ClientWithCert success")
return client
}

// MapClaims populate CustomClaims from userInfo for each configure claims header
Expand Down