Merge pull request #1958 from vmware-tanzu/jtc/merge-main-at-6b3f175-into-github

Merge main at `6b3f175` into `github_identity_provider`
This commit is contained in:
Joshua Casey
2024-05-16 11:02:50 -05:00
committed by GitHub
23 changed files with 1040 additions and 272 deletions

View File

@@ -3,7 +3,7 @@
# Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
ARG BUILD_IMAGE=golang:1.22.3@sha256:b1e05e2c918f52c59d39ce7d5844f73b2f4511f7734add8bb98c9ecdd4443365
ARG BUILD_IMAGE=golang:1.22.3@sha256:ab48cd7b8e2cffb6fa1199de232f61c76d3c33dc158be8a998c5407a8e5eb583
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:e9ac71e2b8e279a8372741b7a0293afda17650d926900233ec3a7b2b7c22a246
# Prepare to cross-compile by always running the build stage in the build platform, not the target platform.

View File

@@ -2,17 +2,17 @@
| Maintainer | GitHub ID | Affiliation |
|-----------------|-----------------------------------------------------------|------------------------------------------|
| Ben Petersen | [benjaminapetersen](https://github.com/benjaminapetersen) | [VMware](https://www.github.com/vmware/) |
| Ryan Richard | [cfryanr](https://github.com/cfryanr) | [VMware](https://www.github.com/vmware/) |
| Joshua T. Casey | [joshuatcasey](https://github.com/joshuatcasey) | [VMware](https://www.github.com/vmware/) |
## Emeritus Maintainers
| Maintainer | GitHub ID |
|-------------------|---------------------------------------------------------|
| Andrew Keesler | [ankeesler](https://github.com/ankeesler) |
| Anjali Telang | [anjaltelang](https://github.com/anjaltelang) |
| Margo Crawford | [margocrawf](https://github.com/margocrawf) |
| Matt Moyer | [mattmoyer](https://github.com/mattmoyer) |
| Mo Khan | [enj](https://github.com/enj) |
| Pablo Schuhmacher | [pabloschuhmacher](https://github.com/pabloschuhmacher) |
| Maintainer | GitHub ID |
|-------------------|-----------------------------------------------------------|
| Andrew Keesler | [ankeesler](https://github.com/ankeesler) |
| Anjali Telang | [anjaltelang](https://github.com/anjaltelang) |
| Ben Petersen | [benjaminapetersen](https://github.com/benjaminapetersen) |
| Margo Crawford | [margocrawf](https://github.com/margocrawf) |
| Matt Moyer | [mattmoyer](https://github.com/mattmoyer) |
| Mo Khan | [enj](https://github.com/enj) |
| Pablo Schuhmacher | [pabloschuhmacher](https://github.com/pabloschuhmacher) |

View File

@@ -47,4 +47,4 @@ Please follow the procedure described in [SECURITY.md](SECURITY.md).
Pinniped is open source and licensed under Apache License Version 2.0. See [LICENSE](LICENSE).
Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.

View File

@@ -5,8 +5,6 @@
This document provides a high-level overview of the next big features the maintainers are planning to work on. This
should serve as a reference point for Pinniped users and contributors to understand where the project is heading, and
help determine if a contribution could be conflicting with a longer term plan.
The [Pinniped project backlog](https://github.com/orgs/vmware-tanzu/projects/43/) is prioritized based on this roadmap,
and it provides a more granular view of what the maintainers are working on a day-to-day basis.
### How to help
@@ -23,19 +21,6 @@ a [proposal](https://github.com/vmware-tanzu/pinniped/tree/main/proposals) in ou
For smaller enhancements, you can open an issue to track that initiative or feature request.
We work with and rely on community feedback to focus our efforts to improve Pinniped and maintain a healthy roadmap.
### Current Roadmap
The following table includes the current roadmap for Pinniped. Please take the timelines and dates as proposals and
goals. Priorities and requirements change based on community feedback, roadblocks encountered, community contributions,
Priorities and requirements change based on community feedback, roadblocks encountered, community contributions,
etc. If you depend on a specific item, we encourage you to reach out for updated status information, or help us deliver
that feature by [contributing](https://github.com/vmware-tanzu/pinniped/blob/main/CONTRIBUTING.md) to Pinniped.
Last Updated: Sept 2022
|Theme|Description|Timeline|
|--|--|--|
|Improving Usability|Dynamic Oauth Client Support for integrating with UI/Dashboards |Sept/Oct 2022|
|Improving Usability|Support for custom claim mappings in OIDCIdentityProvider |Q4 2022|
|Improving Usability|Support for Multiple Identity Providers |Q4 2022|
|Improving Security Posture|Support Audit logging of security events related to Authentication |Q4 2022|
|Improving Security Posture|Session Management |2022/2023|
|Improving Security Posture|Secrets Rotation and Management |2022/2023|

View File

@@ -21,12 +21,3 @@ The following items are out of scope for the Pinniped project.
- Standalone identity provider for general use.
- Machine-to-machine (service) identity.
- Running outside of Kubernetes.
## Roadmap
See our [open milestones][milestones] and the [`priority/backlog` label][backlog] for an idea about what's next on our roadmap.
For more details on proposing features and bugs, check out our [contributing](./CONTRIBUTING.md) doc.
[milestones]: https://github.com/vmware-tanzu/pinniped/milestones
[backlog]: https://github.com/vmware-tanzu/pinniped/labels/priority%2Fbacklog

22
go.mod
View File

@@ -32,7 +32,7 @@ replace github.com/coreos/go-oidc/v3 => github.com/coreos/go-oidc/v3 v3.9.0
require (
github.com/MakeNowJust/heredoc/v2 v2.0.1
github.com/chromedp/cdproto v0.0.0-20240501202034-ef67d660e9fd
github.com/chromedp/cdproto v0.0.0-20240512230644-b3296df1660c
github.com/chromedp/chromedp v0.9.5
github.com/coreos/go-oidc/v3 v3.10.0
github.com/coreos/go-semver v0.3.1
@@ -60,7 +60,7 @@ require (
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
github.com/tdewolff/minify/v2 v2.20.20
github.com/tdewolff/minify/v2 v2.20.22
go.uber.org/mock v0.4.0
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.23.0
@@ -69,15 +69,15 @@ require (
golang.org/x/sync v0.7.0
golang.org/x/term v0.20.0
golang.org/x/text v0.15.0
k8s.io/api v0.30.0
k8s.io/apiextensions-apiserver v0.30.0
k8s.io/apimachinery v0.30.0
k8s.io/apiserver v0.30.0
k8s.io/client-go v0.30.0
k8s.io/component-base v0.30.0
k8s.io/api v0.30.1
k8s.io/apiextensions-apiserver v0.30.1
k8s.io/apimachinery v0.30.1
k8s.io/apiserver v0.30.1
k8s.io/client-go v0.30.1
k8s.io/component-base v0.30.1
k8s.io/gengo v0.0.0-20240404160639-a0386bf69313
k8s.io/klog/v2 v2.120.1
k8s.io/kube-aggregator v0.30.0
k8s.io/kube-aggregator v0.30.1
k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0
sigs.k8s.io/yaml v1.4.0
@@ -160,7 +160,7 @@ require (
github.com/spf13/viper v1.16.0 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/tdewolff/parse/v2 v2.7.13 // indirect
github.com/tdewolff/parse/v2 v2.7.14 // indirect
go.etcd.io/etcd/api/v3 v3.5.10 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect
go.etcd.io/etcd/client/v3 v3.5.10 // indirect
@@ -197,7 +197,7 @@ require (
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/kms v0.30.0 // indirect
k8s.io/kms v0.30.1 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect

44
go.sum
View File

@@ -69,8 +69,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20240501202034-ef67d660e9fd h1:5/HXKq8EaAWVmnl6Hnyl4SVq7FF5990DBW6AuTrWtVw=
github.com/chromedp/cdproto v0.0.0-20240501202034-ef67d660e9fd/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20240512230644-b3296df1660c h1:IrHOOrmmJtVS1Z7tW+z71ZHTe6nYUqARg19Od8ECsJg=
github.com/chromedp/cdproto v0.0.0-20240512230644-b3296df1660c/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/chromedp v0.9.5 h1:viASzruPJOiThk7c5bueOUY91jGLJVximoEMGoH93rg=
github.com/chromedp/chromedp v0.9.5/go.mod h1:D4I2qONslauw/C7INoCir1BJkSwBYMyZgx8X276z3+Y=
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
@@ -567,10 +567,10 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/tdewolff/minify/v2 v2.20.20 h1:vhULb+VsW2twkplgsawAoUY957efb+EdiZ7zu5fUhhk=
github.com/tdewolff/minify/v2 v2.20.20/go.mod h1:GYaLXFpIIwsX99apQHXfGdISUdlA98wmaoWxjT9C37k=
github.com/tdewolff/parse/v2 v2.7.13 h1:iSiwOUkCYLNfapHoqdLcqZVgvQ0jrsao8YYKP/UJYTI=
github.com/tdewolff/parse/v2 v2.7.13/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
github.com/tdewolff/minify/v2 v2.20.22 h1:N3SK8oIuAEF911qP0LR21dtD8ppXulEfy1HkIL/hZH8=
github.com/tdewolff/minify/v2 v2.20.22/go.mod h1:1TJni7+mATKu24cBQQpgwakrYRD27uC1/rdJOgdv8ns=
github.com/tdewolff/parse/v2 v2.7.14 h1:100KJ+QAO3PpMb3uUjzEU/NpmCdbBYz6KPmCIAfWpR8=
github.com/tdewolff/parse/v2 v2.7.14/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
@@ -1084,27 +1084,27 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs=
k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y=
k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/apiserver v0.30.0 h1:QCec+U72tMQ+9tR6A0sMBB5Vh6ImCEkoKkTDRABWq6M=
k8s.io/apiserver v0.30.0/go.mod h1:smOIBq8t0MbKZi7O7SyIpjPsiKJ8qa+llcFCluKyqiY=
k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
k8s.io/component-base v0.30.0 h1:cj6bp38g0ainlfYtaOQuRELh5KSYjhKxM+io7AUIk4o=
k8s.io/component-base v0.30.0/go.mod h1:V9x/0ePFNaKeKYA3bOvIbrNoluTSG+fSJKjLdjOoeXQ=
k8s.io/api v0.30.1 h1:kCm/6mADMdbAxmIh0LBjS54nQBE+U4KmbCfIkF5CpJY=
k8s.io/api v0.30.1/go.mod h1:ddbN2C0+0DIiPntan/bye3SW3PdwLa11/0yqwvuRrJM=
k8s.io/apiextensions-apiserver v0.30.1 h1:4fAJZ9985BmpJG6PkoxVRpXv9vmPUOVzl614xarePws=
k8s.io/apiextensions-apiserver v0.30.1/go.mod h1:R4GuSrlhgq43oRY9sF2IToFh7PVlF1JjfWdoG3pixk4=
k8s.io/apimachinery v0.30.1 h1:ZQStsEfo4n65yAdlGTfP/uSHMQSoYzU/oeEbkmF7P2U=
k8s.io/apimachinery v0.30.1/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/apiserver v0.30.1 h1:BEWEe8bzS12nMtDKXzCF5Q5ovp6LjjYkSp8qOPk8LZ8=
k8s.io/apiserver v0.30.1/go.mod h1:i87ZnQ+/PGAmSbD/iEKM68bm1D5reX8fO4Ito4B01mo=
k8s.io/client-go v0.30.1 h1:uC/Ir6A3R46wdkgCV3vbLyNOYyCJ8oZnjtJGKfytl/Q=
k8s.io/client-go v0.30.1/go.mod h1:wrAqLNs2trwiCH/wxxmT/x3hKVH9PuV0GGW0oDoHVqc=
k8s.io/component-base v0.30.1 h1:bvAtlPh1UrdaZL20D9+sWxsJljMi0QZ3Lmw+kmZAaxQ=
k8s.io/component-base v0.30.1/go.mod h1:e/X9kDiOebwlI41AvBHuWdqFriSRrX50CdwA9TFaHLI=
k8s.io/gengo v0.0.0-20240404160639-a0386bf69313 h1:wBIDZID8ju9pwOiLlV22YYKjFGtiNSWgHf5CnKLRUuM=
k8s.io/gengo v0.0.0-20240404160639-a0386bf69313/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kms v0.30.0 h1:ZlnD/ei5lpvUlPw6eLfVvH7d8i9qZ6HwUQgydNVks8g=
k8s.io/kms v0.30.0/go.mod h1:GrMurD0qk3G4yNgGcsCEmepqf9KyyIrTXYR2lyUOJC4=
k8s.io/kube-aggregator v0.30.0 h1:+Opc0lmhRmHbNM4m3mLSsUFmK/ikMapO9rvGirX5CEM=
k8s.io/kube-aggregator v0.30.0/go.mod h1:KbZZkSSjYE6vkB2TSuZ9GBjU3ucgL7YxT8yX8wll0iQ=
k8s.io/kms v0.30.1 h1:gEIbEeCbFiaN2tNfp/EUhFdGr5/CSj8Eyq6Mkr7cCiY=
k8s.io/kms v0.30.1/go.mod h1:GrMurD0qk3G4yNgGcsCEmepqf9KyyIrTXYR2lyUOJC4=
k8s.io/kube-aggregator v0.30.1 h1:ymR2BsxDacTKwzKTuNhGZttuk009c+oZbSeD+IPX5q4=
k8s.io/kube-aggregator v0.30.1/go.mod h1:SFbqWsM6ea8dHd3mPLsZFzJHbjBOS5ykIgJh4znZ5iQ=
k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f h1:0LQagt0gDpKqvIkAMPaRGcXawNMouPECM1+F9BVxEaM=
k8s.io/kube-openapi v0.0.0-20240430033511-f0e62f92d13f/go.mod h1:S9tOR0FxgyusSNR+MboCuiDpVWkAifZvaYI1Q2ubgro=
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=

View File

@@ -16,7 +16,7 @@
# See https://go.googlesource.com/go/+/dev.boringcrypto/README.boringcrypto.md
# and https://kupczynski.info/posts/fips-golang/ for details.
ARG BUILD_IMAGE=golang:1.22.3@sha256:b1e05e2c918f52c59d39ce7d5844f73b2f4511f7734add8bb98c9ecdd4443365
ARG BUILD_IMAGE=golang:1.22.3@sha256:ab48cd7b8e2cffb6fa1199de232f61c76d3c33dc158be8a998c5407a8e5eb583
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:e9ac71e2b8e279a8372741b7a0293afda17650d926900233ec3a7b2b7c22a246
# This is not currently using --platform to prepare to cross-compile because we use gcc below to build

View File

@@ -1,72 +0,0 @@
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//go:build !fips_strict
package ptls
import (
"crypto/tls"
"crypto/x509"
)
func Default(rootCAs *x509.CertPool) *tls.Config {
return &tls.Config{
// Can't use SSLv3 because of POODLE and BEAST
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
// Can't use TLSv1.1 because of RC4 cipher usage
//
// The Kubernetes API Server must use TLS 1.2, at a minimum,
// to protect the confidentiality of sensitive data during electronic dissemination.
// https://stigviewer.com/stig/kubernetes/2021-06-17/finding/V-242378
MinVersion: tls.VersionTLS12,
// the order does not matter in go 1.17+ https://go.dev/blog/tls-cipher-suites
// we match crypto/tls.cipherSuitesPreferenceOrder because it makes unit tests easier to write
// this list is ignored when TLS 1.3 is used
//
// as of 2021-10-19, Mozilla Guideline v5.6, Go 1.17.2, intermediate configuration, supports:
// - Firefox 27
// - Android 4.4.2
// - Chrome 31
// - Edge
// - IE 11 on Windows 7
// - Java 8u31
// - OpenSSL 1.0.1
// - Opera 20
// - Safari 9
// https://ssl-config.mozilla.org/#server=go&version=1.17.2&config=intermediate&guideline=5.6
//
// The Kubernetes API server must use approved cipher suites.
// https://stigviewer.com/stig/kubernetes/2021-06-17/finding/V-242418
CipherSuites: []uint16{
// these are all AEADs with ECDHE, some use ChaCha20Poly1305 while others use AES-GCM
// this provides forward secrecy, confidentiality and authenticity of data
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
},
// enable HTTP2 for go's 1.7 HTTP Server
// setting this explicitly is only required in very specific circumstances
// it is simpler to just set it here than to try and determine if we need to
NextProtos: []string{"h2", "http/1.1"},
// optional root CAs, nil means use the host's root CA set
RootCAs: rootCAs,
}
}
func DefaultLDAP(rootCAs *x509.CertPool) *tls.Config {
c := Default(rootCAs)
// add less secure ciphers to support the default AWS Active Directory config
c.CipherSuites = append(c.CipherSuites,
// CBC with ECDHE
// this provides forward secrecy and confidentiality of data but not authenticity
// MAC-then-Encrypt CBC ciphers are susceptible to padding oracle attacks
// See https://crypto.stackexchange.com/a/205 and https://crypto.stackexchange.com/a/224
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
)
return c
}

View File

@@ -0,0 +1,144 @@
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//go:build !fips_strict
package ptls
import (
"crypto/tls"
"crypto/x509"
"os"
"path/filepath"
"runtime"
"k8s.io/apiserver/pkg/server/options"
"go.pinniped.dev/internal/plog"
)
// init prints a log message to tell the operator how Pinniped was compiled. This makes it obvious
// that they are using Pinniped in FIPS-mode or not, which is otherwise hard to observe.
func init() { //nolint:gochecknoinits
switch filepath.Base(os.Args[0]) {
case "pinniped-server", "pinniped-supervisor", "pinniped-concierge", "pinniped-concierge-kube-cert-agent":
default:
return // do not print FIPS logs if we cannot confirm that we are running a server binary
}
// this init runs before we have parsed our config to determine our log level
// thus we must use a log statement that will always print instead of conditionally print
plog.Always("this server was not compiled in FIPS-only mode",
"go version", runtime.Version())
}
// SecureTLSConfigMinTLSVersion is the minimum tls version in the format expected by tls.Config.
const SecureTLSConfigMinTLSVersion = tls.VersionTLS13
// Default TLS profile should be used by:
// A. servers whose clients are outside our control and who may reasonably wish to use TLS 1.2, and
// B. clients who need to interact with servers that might not support TLS 1.3.
// Note that this will behave differently when compiled in FIPS mode (see profiles_fips_strict.go).
func Default(rootCAs *x509.CertPool) *tls.Config {
return &tls.Config{
// Can't use SSLv3 because of POODLE and BEAST
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
// Can't use TLSv1.1 because of RC4 cipher usage
//
// The Kubernetes API Server must use TLS 1.2, at a minimum,
// to protect the confidentiality of sensitive data during electronic dissemination.
// https://stigviewer.com/stig/kubernetes/2021-06-17/finding/V-242378
MinVersion: tls.VersionTLS12,
// the order does not matter in go 1.17+ https://go.dev/blog/tls-cipher-suites
// we match crypto/tls.cipherSuitesPreferenceOrder because it makes unit tests easier to write
// this list is ignored when TLS 1.3 is used
//
// as of 2021-10-19, Mozilla Guideline v5.6, Go 1.17.2, intermediate configuration, supports:
// - Firefox 27
// - Android 4.4.2
// - Chrome 31
// - Edge
// - IE 11 on Windows 7
// - Java 8u31
// - OpenSSL 1.0.1
// - Opera 20
// - Safari 9
// https://ssl-config.mozilla.org/#server=go&version=1.17.2&config=intermediate&guideline=5.6
//
// The Kubernetes API server must use approved cipher suites.
// https://stigviewer.com/stig/kubernetes/2021-06-17/finding/V-242418
CipherSuites: []uint16{
// these are all AEADs with ECDHE, some use ChaCha20Poly1305 while others use AES-GCM
// this provides forward secrecy, confidentiality and authenticity of data
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
},
// enable HTTP2 for go's 1.7 HTTP Server
// setting this explicitly is only required in very specific circumstances
// it is simpler to just set it here than to try and determine if we need to
NextProtos: []string{"h2", "http/1.1"},
// optional root CAs, nil means use the host's root CA set
RootCAs: rootCAs,
}
}
// DefaultLDAP TLS profile should be used by clients who need to interact with potentially old LDAP servers
// that might not support TLS 1.3 and that might use older ciphers.
// Note that this will behave differently when compiled in FIPS mode (see profiles_fips_strict.go).
func DefaultLDAP(rootCAs *x509.CertPool) *tls.Config {
c := Default(rootCAs)
// add less secure ciphers to support the default AWS Active Directory config
c.CipherSuites = append(c.CipherSuites,
// CBC with ECDHE
// this provides forward secrecy and confidentiality of data but not authenticity
// MAC-then-Encrypt CBC ciphers are susceptible to padding oracle attacks
// See https://crypto.stackexchange.com/a/205 and https://crypto.stackexchange.com/a/224
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
)
return c
}
// Secure TLS profile should be used by:
// A. servers whose clients are entirely known by us and who may reasonably be told that they must use TLS 1.3, and
// B. clients who only need to interact with servers that are known by us to support TLS 1.3 (e.g. the Kubernetes API).
// Note that this will behave differently when compiled in FIPS mode (see profiles_fips_strict.go).
func Secure(rootCAs *x509.CertPool) *tls.Config {
// as of 2021-10-19, Mozilla Guideline v5.6, Go 1.17.2, modern configuration, supports:
// - Firefox 63
// - Android 10.0
// - Chrome 70
// - Edge 75
// - Java 11
// - OpenSSL 1.1.1
// - Opera 57
// - Safari 12.1
// https://ssl-config.mozilla.org/#server=go&version=1.17.2&config=modern&guideline=5.6
c := Default(rootCAs)
c.MinVersion = SecureTLSConfigMinTLSVersion // max out the security
c.CipherSuites = nil // TLS 1.3 ciphers are not configurable
return c
}
// SecureServing modifies the given options to have the appropriate MinTLSVersion and CipherSuites.
// This function should only be used by the implementation of ptls.SecureRecommendedOptions, which
// is called to help configure our aggregated API servers. This exists only because it needs
// to behave differently in FIPS mode.
// This function is only public so we can integration test it in ptls_fips_test.go.
// Note that this will behave differently when compiled in FIPS mode (see profiles_fips_strict.go).
func SecureServing(opts *options.SecureServingOptionsWithLoopback) {
// secureServingOptionsMinTLSVersion is the minimum tls version in the format
// expected by SecureServingOptions.MinTLSVersion from
// k8s.io/apiserver/pkg/server/options.
opts.MinTLSVersion = "VersionTLS13"
opts.CipherSuites = nil
}

View File

@@ -20,9 +20,7 @@ import (
"go.pinniped.dev/internal/plog"
)
// Until goboring supports TLS 1.3, use TLS 1.2.
const SecureTLSConfigMinTLSVersion = tls.VersionTLS12
// init: see comment in profiles.go.
func init() {
switch filepath.Base(os.Args[0]) {
case "pinniped-server", "pinniped-supervisor", "pinniped-concierge", "pinniped-concierge-kube-cert-agent":
@@ -32,9 +30,16 @@ func init() {
// this init runs before we have parsed our config to determine our log level
// thus we must use a log statement that will always print instead of conditionally print
plog.Always("using boring crypto in fips only mode", "go version", runtime.Version())
plog.Always("this server was compiled to use boring crypto in FIPS-only mode",
"go version", runtime.Version())
}
// SecureTLSConfigMinTLSVersion: see comment in profiles.go.
// Until goboring supports TLS 1.3, use TLS 1.2.
const SecureTLSConfigMinTLSVersion = tls.VersionTLS12
// Default: see comment in profiles.go.
// This chooses different cipher suites and/or TLS versions compared to non-FIPS mode.
func Default(rootCAs *x509.CertPool) *tls.Config {
return &tls.Config{
MinVersion: tls.VersionTLS12,
@@ -64,16 +69,22 @@ func Default(rootCAs *x509.CertPool) *tls.Config {
}
}
// DefaultLDAP: see comment in profiles.go.
// This chooses different cipher suites and/or TLS versions compared to non-FIPS mode.
func DefaultLDAP(rootCAs *x509.CertPool) *tls.Config {
return Default(rootCAs)
}
// Secure: see comment in profiles.go.
// This chooses different cipher suites and/or TLS versions compared to non-FIPS mode.
// Until goboring supports TLS 1.3, make the Secure profile the same as the Default profile in FIPS mode.
func Secure(rootCAs *x509.CertPool) *tls.Config {
return Default(rootCAs)
}
func DefaultLDAP(rootCAs *x509.CertPool) *tls.Config {
return Default(rootCAs)
}
// Until goboring supports TLS 1.3, make secureServing use the same as the defaultServing profile in FIPS mode.
func secureServing(opts *options.SecureServingOptionsWithLoopback) {
// SecureServing: see comment in profiles.go.
// This chooses different cipher suites and/or TLS versions compared to non-FIPS mode.
// Until goboring supports TLS 1.3, make SecureServing use the same as the defaultServing profile in FIPS mode.
func SecureServing(opts *options.SecureServingOptionsWithLoopback) {
defaultServing(opts)
}

View File

@@ -0,0 +1,9 @@
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package ptls
// If you are coming here to look for unit tests for the FIPS-mode profiles,
// please instead see test/integration/ptls_fips_test.go.
// CI does not currently run the unit tests in FIPS mode, so these unit tests
// were instead written as integration tests.

View File

@@ -0,0 +1,91 @@
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package ptls
import (
"crypto/tls"
"crypto/x509"
"testing"
"github.com/stretchr/testify/require"
"k8s.io/apiserver/pkg/server/options"
)
func TestDefault(t *testing.T) {
t.Parallel()
aCertPool := x509.NewCertPool()
actual := Default(aCertPool)
expected := &tls.Config{
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
},
NextProtos: []string{"h2", "http/1.1"},
RootCAs: aCertPool,
}
require.Equal(t, expected, actual)
}
func TestDefaultLDAP(t *testing.T) {
t.Parallel()
aCertPool := x509.NewCertPool()
actual := DefaultLDAP(aCertPool)
expected := &tls.Config{
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, //nolint:gosec // this is a test
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
},
NextProtos: []string{"h2", "http/1.1"},
RootCAs: aCertPool,
}
require.Equal(t, expected, actual)
}
func TestSecure(t *testing.T) {
t.Parallel()
aCertPool := x509.NewCertPool()
actual := Secure(aCertPool)
expected := &tls.Config{
MinVersion: tls.VersionTLS13,
CipherSuites: nil, // TLS 1.3 ciphers are not configurable
NextProtos: []string{"h2", "http/1.1"},
RootCAs: aCertPool,
}
require.Equal(t, expected, actual)
}
func TestSecureServing(t *testing.T) {
t.Parallel()
opts := &options.SecureServingOptionsWithLoopback{SecureServingOptions: &options.SecureServingOptions{}}
SecureServing(opts)
require.Equal(t, options.SecureServingOptionsWithLoopback{
SecureServingOptions: &options.SecureServingOptions{
MinTLSVersion: "VersionTLS13",
},
}, *opts)
}

View File

@@ -77,7 +77,7 @@ func DefaultRecommendedOptions(opts *options.RecommendedOptions, f RestConfigFun
// certain well known clients which we expect will always use modern TLS settings (like the Kube API server).
// It returns a PrepareServerConfigFunc which must be used on a RecommendedConfig before passing it to RecommendedOptions.ApplyTo().
func SecureRecommendedOptions(opts *options.RecommendedOptions, f RestConfigFunc) (PrepareServerConfigFunc, error) {
secureServing(opts.SecureServing)
SecureServing(opts.SecureServing)
return secureClient(opts, f)
}

View File

@@ -34,18 +34,6 @@ func TestDefaultServing(t *testing.T) {
}, *opts)
}
func TestSecureServing(t *testing.T) {
t.Parallel()
opts := &options.SecureServingOptionsWithLoopback{SecureServingOptions: &options.SecureServingOptions{}}
secureServing(opts)
require.Equal(t, options.SecureServingOptionsWithLoopback{
SecureServingOptions: &options.SecureServingOptions{
MinTLSVersion: "VersionTLS13",
},
}, *opts)
}
func TestMerge(t *testing.T) {
t.Parallel()

View File

@@ -1,44 +0,0 @@
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//go:build !fips_strict
package ptls
import (
"crypto/tls"
"crypto/x509"
"k8s.io/apiserver/pkg/server/options"
)
// secureServingOptionsMinTLSVersion is the minimum tls version in the format
// expected by SecureServingOptions.MinTLSVersion from
// k8s.io/apiserver/pkg/server/options.
const secureServingOptionsMinTLSVersion = "VersionTLS13"
// SecureTLSConfigMinTLSVersion is the minimum tls version in the format expected
// by tls.Config.
const SecureTLSConfigMinTLSVersion = tls.VersionTLS13
func Secure(rootCAs *x509.CertPool) *tls.Config {
// as of 2021-10-19, Mozilla Guideline v5.6, Go 1.17.2, modern configuration, supports:
// - Firefox 63
// - Android 10.0
// - Chrome 70
// - Edge 75
// - Java 11
// - OpenSSL 1.1.1
// - Opera 57
// - Safari 12.1
// https://ssl-config.mozilla.org/#server=go&version=1.17.2&config=modern&guideline=5.6
c := Default(rootCAs)
c.MinVersion = SecureTLSConfigMinTLSVersion // max out the security
c.CipherSuites = nil // TLS 1.3 ciphers are not configurable
return c
}
func secureServing(opts *options.SecureServingOptionsWithLoopback) {
opts.MinTLSVersion = secureServingOptionsMinTLSVersion
opts.CipherSuites = nil
}

View File

@@ -1,7 +1,7 @@
---
title: "Multiple Identity Providers"
authors: [ "@cfryanr" ]
status: "accepted"
status: "implemented"
sponsor: []
approval_date: "July 12, 2023"
---

View File

@@ -1,9 +1,9 @@
---
title: "Concierge Impersonation Proxy | External Certificate Management"
authors: [ "@joshuatcasey" ]
status: "in-review"
status: "implemented"
sponsor: [ "@cfryanr", "@benjaminapetersen" ]
approval_date: ""
approval_date: "August 8, 2023"
---
*Disclaimer*: Proposals are point-in-time designs and decisions.

View File

@@ -0,0 +1,581 @@
---
title: "Authenticating Users via GitHub"
authors: [ "@cfryanr" ]
status: "accepted"
sponsor: [ ]
approval_date: "March 27, 2024"
---
*Disclaimer*: Proposals are point-in-time designs and decisions.
Once approved and implemented, they become historical documents.
If you are reading an old proposal, please be aware that the
features described herein might have continued to evolve since.
# Authenticating Users via GitHub
## Problem Statement
Many developers in the world have a GitHub account, and many enterprises use GitHub.
This makes GitHub a convenient identity provider for an enterprise to use to control
access to Kubernetes clusters for its developers.
This document proposes adding a GitHubIdentityProvider resource to the Pinniped Supervisor,
which would allow GitHub to be used as an identity provider for authentication to Kubernetes
clusters.
### How Pinniped Works Today (as of version v0.28.0)
The Pinniped Supervisor currently supports OIDC-compliant identity providers and
LDAP identity providers (including Active Directory). Unfortunately, GitHub does not offer
either of these protocols for authenticating users. GitHub uses a slightly customized version
of OAuth 2.0 for authenticating users, so Pinniped will need to add new features to support
using GitHub as an identity provider.
## Terminology / Concepts
### GitHub OAuth 2.0 Clients
For web browser-based authentication, GitHub supports two different types of OAuth 2.0 clients (compared in
[this doc](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/deciding-when-to-build-a-github-app)
and [this doc](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps)):
- GitHub Apps (newer)
- OAuth Apps (we will call these "GitHub OAuth Apps" below to differentiate them from the generic concept of an OAuth
2.0 client)
Both flavors of Apps allow delegated authentication and authorization, but GitHub Apps are newer and allow
GitHub Organization administrators to have more control over the management of the apps.
Astute readers may note that OAuth 2.0 is typically used only for delegated authorization, not authentication.
GitHub's REST API enables the use of OAuth 2.0 for authentication by offering endpoints that can be used to
learn about the user's identity from their OAuth 2.0 access token (see below).
### GitHub PATs
For CLI-based (no browser required) authentication, GitHub supports two different types of
Personal Access Tokens (PATs) (compared in
[this doc](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)):
- Fine-grained PATs (newer)
- Classic PATs
Both flavors of PATs allow authenticating to the GitHub API, but Fine-grained PATs are newer and allow
GitHub organization administrators to have more control over how the PAT can be used to access API resources
related to the organization.
Note that at the time of writing this document, Fine-grained PATs are a Beta feature of GitHub, and are not
enabled by default for use on an organization. The organization admin must opt in to allowing them, and may
also choose to opt-out of allowing classic PATs.
### GitHub's REST API
GitHub offers a REST API which can be used to find out about, among other things, the
[identity of the currently authenticated user](https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user)
(`/user`), the
[organization memberships of the currently authenticated user](https://docs.github.com/en/rest/orgs/orgs?apiVersion=2022-11-28#list-organizations-for-the-authenticated-user)
(`/user/orgs`), the
[team memberships of the currently authenticated user](https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#list-teams-for-the-authenticated-user)
(`/user/teams`).
These are the only three GitHub APIs that would be used by Pinniped.
Access tokens from both types of OAuth 2.0 clients and both types PATs can be used to call the GitHub APIs
on behalf of a GitHub user.
The permissions model for API calls authenticated via access tokens from GitHub OAuth Apps and Classic PATs are the
same, and are based on granted scopes. Scopes are configured on the Classic PAT by the user who creates it.
Scopes are not configured on a GitHub OAuth App, but are instead requested by the client at authorization time,
and can be approved by the individual user at authorization time. To use the above APIs the client or PAT must be
granted the `read:user` and `read:org` scopes.
The permissions model for API calls authenticated via access tokens from GitHub Apps and Fine-grained PATs are the same,
and are based on GitHub's fine-grained permissions model. These do not use OAuth scopes. Fine-grained permissions
are configured on the GitHub App or the Fine-grained PAT. To use the above APIs, the client or PAT must be configured
to allow reading the organization's memberships. Additionally, the org owners may need to approve the GitHub App
or Fine-grained PAT's permissions for that org.
All REST API calls are subject to per-user
[rate limits](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28).
## Proposal
### Goals and Non-goals
Goals:
- Add new GitHubIdentityProvider resource which allows Supervisor admins to configure GitHub
as an identity provider. Offer configuration options for extracting usernames and group memberships, as well
as for preventing authentication unless a user belongs to a certain GitHub organization.
- Allow GitHubIdentityProvider(s) to be configured by admins on FederationDomains, and enable all features of
FederationDomains on them (e.g. identity transformations).
- Allow end users to use a web browser-based authentication flow, similar to other supported identity providers,
where the end user only needs to authenticate using a web browser once a day.
- Allow end users to use a CLI-based authentication flow which does not require a web browser,
similar to other supported identity providers. This enables using Pinniped authentication for scripting
and CI purposes. Allow the admin to disable this feature if they prefer.
- Regardless of which flow the user uses to authenticate, regularly check with GitHub to see if the user should
be allowed to continue their session. If the user's GitHub tokens were revoked, or if the user no longer belongs
to the configured GitHub organization(s), then end their Pinniped Supervisor session to block their continued
access to Kubernetes clusters.
- Regardless of which flow the user uses to authenticate, regularly check with GitHub to see if the user's group
memberships have changed and update them accordingly.
- Any client of the Supervisor should be able to authenticate GitHub users using the web browser-based
authentication flow, including the Pinniped CLI and third-party clients registered with the Supervisor as OIDCClients.
- The IDP chooser page and the IDP discovery endpoints should both include the configured GitHub identity provider(s)
for the FederationDomain in the lists that they return.
- Avoid requiring any changes to the Pinniped CLI or other clients. From a client's point of view,
which external identity provider is used for authentication should be transparent.
### Specification / How it Solves the Use Cases
#### New GitHubIdentityProvider resource
Add a new CRD called GitHubIdentityProvider. Here is an example:
```yaml
---
apiVersion: idp.supervisor.pinniped.dev/v1alpha1
kind: GitHubIdentityProvider
metadata:
namespace: supervisor
name: github
spec:
githubAPI:
# Required only for GitHub Enterprise Server (on-prem).
# Defaults to using the GitHub's public API.
# Can be hostname, IPv4 address, or IPv6 address.
# May optionally end with `:port` for some port number.
# Note that an IPv6 address must not be surrounded by
# square brackets unless it has a port number.
host: my.example.com
# X.509 Certificate Authority (base64-encoded PEM bundle).
# If omitted, a default set of system roots will be trusted.
# Required only for GitHub Enterprise Server (on-prem), and only
# when its CA will not be trusted by the default system roots.
certificateAuthorityData: LS0tLS1CRUdJTiBDRV...0tLS0tCg==
claims:
# Which property of the GitHub user record shall determine
# the username in Kubernetes. Can be either "id", "login",
# or "login:id".
# Login names can only contain alphanumeric characters and
# non-repeating hyphens, and may not start or end with hyphens.
# GitHub's users are allowed to change their login name,
# although it is inconvenient. If a GitHub user changed
# their login name from "foo" to "bar", then a second user
# might change their name from "baz" to "foo" in order
# to take the old username of the first user. For this
# reason, it is not as safe to make authorization decisions
# based only on the login name.
# If desired, an admin could configure identity
# transformation expressions on a FederationDomain to
# further customize these usernames.
# Defaults to "login:id", which is the login name, followed
# by a colon, followed by the unique and unchanging integer
# ID number. This blends human-readable login names with the
# unchanging ID value. Note that colons are not allowed in
# GitHub login names or in ID numbers, so the colon in the
# "login:id" name will always be the login/id separator colon.
username: id
# Which property of the GitHub team record shall determine
# the group name in Kubernetes. Can be either "name" or "slug".
# Team names can contain upper and lower case characters,
# whitespace, and punctuation (e.g. "Kube admins!").
# Slug names are lower case alphanumeric characters and may
# contain dashes and underscores (e.g. "kube-admins").
# Either way, group names will always be prefixed by the
# GitHub org name followed by a slash (e.g. my-org/my-team).
# Note that slashes are not allowed in GitHub org names,
# so the first slash in the group name will always be the
# org/team separator slash.
# Groups names will only include the teams which are
# returned by the GitHub API's /user/teams endpoint,
# which are the teams to which the user directly belongs
# and the immediate parent teams of those teams
# (and Pinniped will remove duplicates from the list).
# The entire hierarchy of nested team memberships is not
# returned by the GitHub API, and so will not be
# reflected in the user's group memberships.
# Also, the GitHub OAuth App or GitHub App must have
# permission to see the teams in a particular org,
# or else none of the teams from that org will be
# reflected in the user's group memberships.
# If desired, an admin could configure identity
# transformation expressions on a FederationDomain to
# further customize these group names.
# Defaults to "slug".
groups: slug
allowAuthentication:
organizations:
# Optionally reject any user who attempts to authenticate
# unless they belong to one of these GitHub orgs.
# The GitHub App or GitHub OAuth App provided in the
# spec.client.secretName must be allowed by the org
# owners to view org memberships, or else the
# user will not be considered to belong to that org
# or belong to any teams within that org.
# When specified, users' group memberships will be
# filtered to include only those GitHub teams which
# are owned by these orgs. org membership comparisons
# shall be done as case-insensitive string comparisons,
# but case shall be preserved in the org names when
# they appear in downstream group names
# If desired, an admin could configure identity
# policy expressions on a FederationDomain to
# further customize which users and groups are allowed
# to authenticate.
allowed: [ vmware-tanzu, broadcom ]
# Policy decides how the allowed list will be used.
# Defaults to OnlyUsersFromAllowedOrganizations,
# which means that the user must belong to one
# of the allowed list, and the allow list must
# be non-empty. May be set to AllGitHubUsers,
# which means any GitHub user can authenticate
# regardless of org membership, and all the team
# memberships returned by the GitHub API for that
# user will be reflected as groups regardless of
# which orgs own those teams. When set to AllGitHubUsers,
# the allow list must be empty.
policy: OnlyUsersFromAllowedOrganizations
# Allow or disallow users to use GitHub Personal Access Tokens
# for CLI-based (no web browser) authentication.
# Setting these both to false disables all non-interactive
# CLI-based auth, but still allows browser-based auth.
personalAccessTokens:
# Allow or disallow the use of GitHub Fine-grained PATs
# to authenticate to Kubernetes clusters. If they
# are disallowed for your GitHub org, then also
# disallow them here so your users can get a nice
# error message when trying to use a fine-grained PAT.
# Defaults to false.
fineGrained: true
# Allow or disallow the use of GitHub Classic PATs
# to authenticate to Kubernetes clusters. If they
# are disallowed for your GitHub org, then also
# disallow them here so your users can get a nice
# error message when trying to use a classic PAT.
# Defaults to false.
classic: false
client:
# The name of Secret in the same namespace which holds the
# client ID and client secret of a GitHub App or
# Github OAuth App. This will only be used for web
# browser-based auth flows (not CLI-based flows).
# Required.
secretName: github-client-credentials
status:
conditions: # Discussed below.
phase: # Pending, Ready, or Error. To summarize the conditions.
---
apiVersion: v1
kind: Secret
type: secrets.pinniped.dev/github-client # must use this type
metadata:
namespace: supervisor
name: github-client-credentials
data:
clientID: <some-github-client-id>
clientSecret: <some-github-client-secret>
```
The status conditions will be populated by a controller. The conditions will include:
- `HostValid`. This is not a URL, so it must not contain `://`. It must be parsable as defined by our `endpointaddr.Parse()` helper function.
- `TLSConfigurationValid`. The CA bundle can be parsed as a CA bundle.
- `OrganizationsPolicyValid`. Use the same logic and same error text in CEL validations for this field, and repeat it in the controller in case an old version of Kubernetes is being used.
- `ClientCredentialsValid`. This is what we call the same check in the OIDCIdentityProvider's status.
- `GitHubConnectionValid`. Report if the host can be dialed with TLS verification. (In LDAPIdentityProvider we called this `LDAPConnectionValid`.)
#### Web Browser-based Authentication using GitHubIdentityProvider
The client ID and client secret configured on the GitHubIdentityProvider can be for either a GitHub App or a
GitHub OAuth App. This flexibility can be made possible because there are many similarities in the
OAuth 2.0 authorization flow used by both. To the extent that we can ignore the differences, Pinniped
will not need to know if it is acting as a GitHub App or a GitHub OAuth App.
The authorization flow for GitHub apps is described in
[this doc](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app)
and the flow for GitHub OAuth Apps is described in
[this doc](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps).
From a client's point of view, the main difference is that access tokens for a GitHub App may expire, depending how the
app was configured on GitHub. However, if the access token expires, then it is always after 8 hours. That duration is
not currently configurable. This gives Pinniped a nice option to save implementation time and complexity: we can simply
ignore GitHub's OAuth refresh flow. The user's Pinniped session should expire either when their GitHub access token
stops working (i.e. was expired or revoked) or whenever Pinniped's hardcoded 9-hour session limit is reached.
If it is an expiring access token, then the session will end after 8 hours due to the token expiration.
If it is not an expiring access token, then the session will end after 9 hours. That's close enough to Pinniped's
concept of "once-per 9 hours authentication" that it's not worth the extra effort to implement the refresh
flow with GitHub.
The other difference from a client's point of view is that GitHub Apps do not use scopes. However, the `scope` param
may be sent on the authorization request for either a GitHub App or a GitHub OAuth App the same way, and the
authorization endpoint will simply ignore it for a GitHub App. The token response for a GitHub app will not
include the list of granted scopes, so we would need to ignore that too. If we request scopes but ignore
validating that the requested scopes were actually granted, then we can treat GitHub Apps and GitHub OAuth Apps
interchangeably.
There are other differences which would need to be documented for the user who is configuring the GitHub App or
GitHub OAuth App, but those differences would not change the Pinniped code. The user would need to know how
each of these two app types are installed or approved by a GitHub org, because without taking the appropriate
steps for the org, the `/user/orgs` GitHub API will not return any results related to that org.
This would prevent users of that org from being able to authenticate, because Pinniped would not be
able to discover that they are members of that org.
During initial login (authorization) the Supervisor can get an access token from GitHub and use it to learn the
identity, org memberships, and team memberships of that user by calling the GitHub API. It can then store that
access token into the Supervisor's session storage. During the Supervisor session refresh flow, it can make the same
calls to the GitHub API to ensure that the access token is still valid, the user's identity has not changed,
the user's org membership has not changed, the user's org membership still meets the critera defined in the
`allowedOrganziations` list, and to update the users groups by getting their current team memberships.
The three GitHub API requests during login, and the three more GitHub API requests during each refresh, will all count
against that GitHub user's hourly API request limits. Note that refreshes only happen when users are actively
interacting with Kubernetes clusters during their session.
For one session started using browser-based authentication, and used actively throughout an hour, this would
be a maximum of approximately 36 GitHub API requests per hour against the user's 5,000 requests per hour limit.
Additionally, each new concurrent Supervisor session started
by that same user will cause the same API calls again (new sessions are started when the user's home directory is not
shared between them, e.g. because the user is authenticating from multiple computers at the same time).
#### CLI-based Authentication using GitHubIdentityProvider
Non-interactive authentication at the CLI (no web browser) is desirable for scripting and CI/CD use cases.
Some enterprises prefer to create "bot" users in their regular identity providers and use Pinniped for authentication,
rather than using Kubernetes' other features for authentication of non-human actors. OIDCIdentityProviders,
LDAPIdentityProviders, and ActiveDirectoryIdentityProviders already support CLI-based authentication, so it would be
preferable for GitHubIdentityProvider to also support it, if possible. In a CLI-based flow, the CLI collects the
user's username and password from the user, and no web browser is needed.
Note that CLI-based (non-interactive) authentication is only allowed on the `pinniped-cli` client, not on any
configured third-party OIDCClient. This pre-existing limitation is to prevent 3rd party apps from directly handling
the user's credential.
This feature will not be included in the initial release of GitHub support. It is planned as a follow-up feature
after web-browser based authentication is fully supported. Therefore, some of the details in this section may
change before implementation. This section is meant to give an overview of the general intended approach.
Since we will use the GitHub API to discover a user's identity, we could use a PAT provided by the user
at the CLI to authenticate that user. To keep the experience (and code) similar to OIDC and LDAP/AD identity providers,
the CLI could interactively prompt for their username and password, and the user could paste the PAT as their password.
To avoid the prompts, the user could set the existing `PINNIPED_USERNAME` and `PINNIPED_PASSWORD` environment variables
that are already used to skips the prompts for OIDC and LDAP/AD password-based authentication. Note that the PAT
indirectly identifies the username by itself, but we could still require that the user submit their GitHub username
during CLI-based authentication to keep the client-side code and user experience consistent with OIDCIdentityProviders,
LDAPIdentityProviders, and ActiveDirectoryIdentityProviders, which all require both username and password
for CLI-based authentication.
Pinniped could accept either Fine-grained PATs or Classic PATs (or both), at the discretion of the Supervisor admin.
PATs of each of these types have different prefixes and can be distinguished from each other by the prefix.
Fine-grained PATs would need to be created with the permission to read org memberships. Classic PATs would need to
be created to allow the `read:user` and `read:org` scopes.
However, using PATs for this purpose would have some important implications:
1. At first glance, it might appear that the Supervisor would need to hold the PAT in its session storage
(until the Supervisor session has expired) so it can validate the user again upon session refresh.
However, this PAT would be usable to grant access to Kubernetes
clusters to anyone who holds it, similar to an end-user password for CLI-based (non-interactive) OIDC or
LDAP/AD logins. The Supervisor does not store those OIDC/LDAP/AD passwords, and it should not store these
PATs either, if possible.
2. PATs have no concept of intended audience, so there is no way for a user to consent that one particular PAT is
intended to control their access to Kubernetes clusters. A GitHub user can have many PATs for different purposes,
and we should not treat them as interchangeable.
Solving the above points are key to designing CLI-based authentication for GitHubIdentityProviders.
##### How to Avoid Storing PATs in the Supervisor
We could avoid storing PATs on the Supervisor by simply preventing token refreshes for any Supervisor sessions that
were started using a GitHub PAT. There's no need to store the PAT for the purposes of refreshing the session if
there is no concept of refreshing these sessions. By always returning an error on these refresh requests,
the Pinniped CLI will automatically submit the PAT again (if stored in the user's env vars for the CLI process)
to start a new session. There will be no observable user experience impact from always rejecting refreshes.
By disabling refresh for these sessions, these users, when actively interacting with Kubernetes clusters, will
create many more sessions per hour compared to users who can refresh their current sessions. Typical usage of
Pinniped grants access to Kubernetes clusters for about five minutes before the next session refresh.
This will cause two concerns:
1. Each new session creates several Kubernetes Secrets for Supervisor session storage, so increasing the number of
sessions will increase the number of Secrets.
2. Each new session will need to make several calls to the GitHub API, impacting the GitHub user's hourly rate limit.
We can mitigate both effects using the techniques outlined in https://github.com/vmware-tanzu/pinniped/pull/1857.
By making the access token lifetime slightly longer, the user will be able to use their clusters for about 10 minutes,
rather than 5 minutes, reducing the number of new sessions that they need to start per hour.
By garbage collecting the session storage much faster for these sessions, the number of session storage Secrets
on the Supervisor's cluster will remain manageable.
For one user using CLI-based authentication, and actively making requests to Kubernetes clusters
throughout an hour, this would be a maximum of approximately 18 GitHub API requests per hour against the
user's 5,000 requests per hour limit.
##### How to Avoid Treating All PATs Interchangeably
Pinniped could offer a new feature to allow an end user to consent to use a particular PAT to enable
Kubernetes authentication for that user for that GitHubIdentityProvider for that FederationDomain.
1. This would be technically possible if the user authenticated to the Supervisor first using a
browser-based GitHub authentication flow, and then user called an API on the Supervisor FederationDomain
(using their FederationDomain-issued access token to prove their identity)
to consent to having a specific PAT be used for Kubernetes authentication in the future.
2. The user should be advised that anyone who holds the PAT will be able to authenticate using their
identity to all Kubernetes clusters in the FederationDomain. Advise the user that the PAT:
- should be treated like a password to Kubernetes clusters and therefore should never be shared with other people,
- should be kept in a safe place such as a password manager,
- should never be used for any other purpose aside from authenticating with this FederationDomain,
- would ideally have an expiration date set on it (not required),
- should never have any extra permissions/scopes besides the minimum required by the Pinniped docs,
- and should be immediately revoked if accidentally leaked or shared.
These are the responsibility of the user and cannot be enforced by Pinniped.
3. The implementation of the consent endpoint could validate that the currently authenticated Supervisor user is from
GitHub. Then it could call the GitHub API using the submitted PAT to validate that
that the current Supervisor user matches the user identified by the PAT. Then it could store the SHA-256 hash
of the PAT into an allow list of consented PATs for that user for that FederationDomain for
future CLI-based authentication attempts.
4. Other similar endpoints could be used to revoke consents for PATs.
Ideally a user could have multiple consented PATs registered at the same time
to allow them to rotate PATs gracefully. The user should also be able to revoke consent for all PATs,
in case they have lost or forgotten a PAT.
5. This consent would need to be durably stored by the Supervisor outside of session storage, since it would be used
to help start future sessions based on the PAT. This could be done using Kubernetes Secrets. One Secret
per-FederationDomain could hold a map of all usernames with the hashes of all consented PATs for each username.
The new Supervisor API endpoints would be:
- `POST https://<federation_domain_issuer_string>/v1alpha1/consent/githubpats` to add a new PAT
- `DELETE https://<federation_domain_issuer_string>/v1alpha1/consent/githubpats/<sha_256_hash_of_pat>`
to remove a specific PAT
- `DELETE https://<federation_domain_issuer_string>/v1alpha1/consent/githubpats` to remove all PATs
New Pinniped CLI commands could be added to wrap these consent APIs, and to help the user authenticate with
the Supervisor before calling these APIs. Note that the user would typically need a kubeconfig to authenticate with
a Supervisor today, because that kubeconfig has the URL, CA, etc. required to start the authentication attempt.
For convenience, these new CLI commands would need a Pinniped-compatible kubeconfig for a cluster
which is using the same FederationDomain from which to read those settings. It can ignore the portions of
the kubeconfig which identify the Kubernetes cluster itself, and only pay attention to the portions which
relate to the Supervisor. These commands would accept the typical methods to choose a current kubeconfig
(e.g. `--kubeconfig` flag, `KUBECONFIG` env var, etc.).
These CLI commands would be:
- `pinniped consent github-pat add <pat>` to add a PAT to the consent list for the current user
for this FederationDomain
- `pinniped consent github-pat remove <pat>` to remove a specific PAT from the consent list for the current user
for this FederationDomain
- `pinniped consent github-pat remove-all` to remove all PATs from the consent list for the current user
for this FederationDomain
The implementation of these CLI commands would be as follows:
1. Read the credential exec specification from the kubeconfig. Confirm that it has the right flags to be
an invocation of the "pinniped login oidc" command. Remember the value of the flags that are related to
the Pinniped Supervisor (e.g. `--issuer`, `--ca-bundle-data`, `--client-id`, `--scopes`, `--request-audience`, and
`--upstream-identity-provider-*`).
2. This command could only work if the issuer specified by these flags is a Pinniped Supervisor.
The command could call the issuer's discovery endpoint to confirm that it is a Supervisor and to get the
location of its PAT consent endpoint (assuming that it is a new enough Supervisor to have this endpoint).
3. The command would need to trigger a Pinniped login or refresh flow because it needs to be sure that it has a
valid/non-expired Supervisor-issued access token. One way to do this would be for the CLI to invoke its own
"pinniped oidc login" command in a subprocess, perhaps by using the same code that kubectl
would use to invoke it. Or it could invoke the implementation of that subcommand within the same process,
which might be safer than starting a new process. Either way, it will need to change some of the options
to this command compared to the options that were listed in the kubeconfig's credential exec spec:
- It should remove the flag to enable the Concierge (if present), because it does
not need an actual mTLS credential for the cluster.
- It should add (or overwrite) the credential cache location flag to prevent credential caching
(not session caching) so it does not overwrite the user's current credential in the
user's credential cache (if they have one).
- It should explicitly set the PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW env var for the subcommand to force it
to use a web-based flow for login, in case the kubeconfig was intended for a CLI-based (non-interactive)
login.
- It should leave intact (or add) the CLI flag to request a different audience, because that's the only way to force
the "pinniped login oidc" command to check the expiration date of the cached access token to make sure that
it has not expired or is about to expire (otherwise, it checks the expiry of the cached ID token). Note that
this is assuming that the related bug in the Pinniped CLI is fixed as outlined in
[this PR](https://github.com/vmware-tanzu/pinniped/pull/1857).
- It should take care to correctly handle unicode characters that might exist in flag values when
invoking the subcommand, e.g. emojis in the upstream IDP name flag.
As long as the subcommand is successful, its stdout results can be ignored, because we only care about
what it cached in the session cache as a side effect.
4. Now that the user is authenticated, the command will need to be able to construct a session cache key
exactly the same way that the "pinniped login oidc" subcommand did it, so it can be sure that it will be
looking up the same session in the cache.
5. Next, it could open the session cache and read the access token from it. This access token should still be
valid/non-expired because of the "pinniped login oidc" subcommand that just succeeded.
6. Finally, it could call the new Supervisor API to add consent for the PAT by sending the access token
for authentication and sending the SHA-256 hash of the PAT to add it to the allow list for the current user
identified by the access token. Revoking consent for a specific PAT would be a similar request,
and revoking consent for all PATs associated with the current user could work in a similar way
(but without needing to submit any hashed value of a specific PAT).
#### Upgrades
All changes will be backwards-compatible additions.
#### Tests
All aspects of the new features will be unit tested. Appropriate integration tests will also be added.
#### New Dependencies
We may like to use Google's golang client package for GitHub to help us call the GitHub API, although this would not
be strictly necessary since these are simple REST API calls. It may help us implement things like pagination.
#### Performance Considerations
None.
#### Observability Considerations
Follow our pre-existing standards for error messages, log messages, custom resource status, etc.
#### Security Considerations
Some GitHub-specific considerations are already discussed above. Otherwise, this fits into the existing design
of Pinniped without changing any of its existing security considerations.
#### Usability Considerations
No significant changes for end users in terms of how they authenticate or use kubectl.
#### Documentation Considerations
We will add API docs for GitHubIdentityProvider, and add docs on the website for authenticating with GitHub.
### Other Approaches Considered
None.
## Open Questions
- Should consent for a PAT work across all GitHubIdentityProviders in a FederationDomain,
or just one specific GitHubIdentityProvider in that FederationDomain?
- Would it be helpful to offer a `GET` endpoint to list current PAT consents? What would a user do with this
information?
- Should we also reduce the lifetime of the Supervisor-issued refresh tokens? This would be a signal to the client
that the token is not going to work. Would this help the Pinniped CLI remove stale entries from the session
cache file more quickly?
- For the consent CLI commands, what if the kubeconfig's exec plugin is a path to a different CLI? It could be
a different path to a different version of the Pinniped CLI, or it could be a different CLI entirely
(like the `tanzu` CLI). Does this matter?
- Are the three GitHub API endpoints that we intend to use different for GitHub Enterprise Server (on-prem)?
Do they have different paths or different inputs and outputs? Need to check the GitHub docs.
## Answered Questions
- Does the GitHub Identity Provider need `additionalClaimMappings`? No. This feature was only added for
OIDCIdentityProvider, and not yet added for other identity provider types. Please raise an issue in
this repo if you need this feature.
## Implementation Plan
The maintainers will most likely implement this proposal, if it is accepted.
Community contributions to the effort would be welcomed. Contact the maintainers if you might wish to get involved.
Implementing browser-based authentication will happen first. It is simpler and is a pre-requisite for
the PAT consent feature for CLI-based authentication.

View File

@@ -16,13 +16,6 @@
<p class="position">Engineer</p>
</div>
</div>
<div class="bio">
<div class="image"><img src="/img/ben-petersen.png" /></div>
<div class="info">
<p class="name">Ben Petersen</p>
<p class="position">Engineer</p>
</div>
</div>
</div>
<h3>Contributing:</h3>
<p>The Pinniped project team welcomes contributions from the community, please see the <a href="https://github.com/vmware-tanzu/pinniped/blob/main/CONTRIBUTING.md">contributors guide</a> for more information.</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,144 @@
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//go:build fips_strict
package integration
import (
"crypto/tls"
"crypto/x509"
"net/http"
"testing"
"github.com/stretchr/testify/require"
"k8s.io/apiserver/pkg/server/options"
"k8s.io/client-go/util/cert"
"go.pinniped.dev/internal/crypto/ptls"
"go.pinniped.dev/internal/testutil/tlsserver"
"go.pinniped.dev/test/testlib"
)
// Note: Everything in this file is an integration test only because we do not support build tags on unit tests.
// These are effectively unit tests for the ptls package when compiled in FIPS mode.
// TestFIPSCipherSuites_Parallel ensures that if the list of default FIPS cipher suites changes, then we will know.
// If this test ever fails during a golang upgrade, then we may need to change which ciphers we are using in
// the ptls package in FIPS mode.
func TestFIPSCipherSuites_Parallel(t *testing.T) {
_ = testlib.IntegrationEnv(t) // this function call is required for integration tests
server, ca := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// use the default fips config which contains a hard coded list of cipher suites
// that should be equal to the default list of fips cipher suites.
// assert that the client hello response has the same tls config as this test server.
tlsserver.AssertTLS(t, r, ptls.Default)
}), tlsserver.RecordTLSHello)
pool, err := cert.NewPoolFromBytes(ca)
require.NoError(t, err)
// create a tls config that does not explicitly set cipher suites,
// and therefore uses goboring's default fips ciphers.
defaultConfig := &tls.Config{
RootCAs: pool,
NextProtos: ptls.Default(nil).NextProtos, // we do not care about field for this test, so just make it match
}
transport := http.Transport{
TLSClientConfig: defaultConfig,
ForceAttemptHTTP2: true,
}
// make a request against the test server, which will validate that the
// tls config of the client without explicitly set ciphers
// is the same as the tls config of the test server with explicitly
// set ciphers from ptls.
request, _ := http.NewRequest("GET", server.URL, nil)
response, err := transport.RoundTrip(request)
require.NoError(t, err)
require.Equal(t, http.StatusOK, response.StatusCode)
}
// Every profile should use the same cipher suites in FIPS mode, because FIPS requires these ciphers.
// Please treat this as a read-only const.
var expectedFIPSCipherSuites = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
}
func TestDefault_Parallel(t *testing.T) {
_ = testlib.IntegrationEnv(t) // this function call is required for integration tests
aCertPool := x509.NewCertPool()
actual := ptls.Default(aCertPool)
expected := &tls.Config{
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS12, // goboring does not currently support TLS 1.3, so prevent its use
CipherSuites: expectedFIPSCipherSuites,
NextProtos: []string{"h2", "http/1.1"},
RootCAs: aCertPool,
}
require.Equal(t, expected, actual)
}
func TestDefaultLDAP_Parallel(t *testing.T) {
_ = testlib.IntegrationEnv(t) // this function call is required for integration tests
aCertPool := x509.NewCertPool()
actual := ptls.DefaultLDAP(aCertPool)
expected := &tls.Config{
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS12, // goboring does not currently support TLS 1.3, so prevent its use
CipherSuites: expectedFIPSCipherSuites,
NextProtos: []string{"h2", "http/1.1"},
RootCAs: aCertPool,
}
require.Equal(t, expected, actual)
}
func TestSecure_Parallel(t *testing.T) {
_ = testlib.IntegrationEnv(t) // this function call is required for integration tests
aCertPool := x509.NewCertPool()
actual := ptls.Secure(aCertPool)
expected := &tls.Config{
// goboring does not currently support TLS 1.3, so where we would normally require it by making it the
// min version for the secure profile, we cannot do that in FIPS mode
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS12, // goboring does not currently support TLS 1.3, so prevent its use
CipherSuites: expectedFIPSCipherSuites,
NextProtos: []string{"h2", "http/1.1"},
RootCAs: aCertPool,
}
require.Equal(t, expected, actual)
}
func TestSecureServing_Parallel(t *testing.T) {
_ = testlib.IntegrationEnv(t) // this function call is required for integration tests
opts := &options.SecureServingOptionsWithLoopback{SecureServingOptions: &options.SecureServingOptions{}}
ptls.SecureServing(opts)
expectedFIPSCipherSuiteNames := make([]string, len(expectedFIPSCipherSuites))
for i, suite := range expectedFIPSCipherSuites {
expectedFIPSCipherSuiteNames[i] = tls.CipherSuiteName(suite)
}
require.Equal(t, options.SecureServingOptionsWithLoopback{
SecureServingOptions: &options.SecureServingOptions{
CipherSuites: expectedFIPSCipherSuiteNames,
// goboring does not currently support TLS 1.3, so where we would normally require it by making it the
// min version for secure serving for aggregated API servers, we cannot do that in FIPS mode
MinTLSVersion: "VersionTLS12",
},
}, *opts)
}

View File

@@ -1,53 +0,0 @@
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//go:build fips_strict
package integration
import (
"crypto/tls"
"net/http"
"testing"
"github.com/stretchr/testify/require"
"k8s.io/client-go/util/cert"
"go.pinniped.dev/internal/crypto/ptls"
"go.pinniped.dev/internal/testutil/tlsserver"
"go.pinniped.dev/test/testlib"
)
// TestFIPSCipherSuites_Parallel ensures that if the list of default fips cipher suites changes,
// we will know. This is an integration test because we do not support build tags on unit tests.
func TestFIPSCipherSuites_Parallel(t *testing.T) {
_ = testlib.IntegrationEnv(t)
server, ca := tlsserver.TestServerIPv4(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// use the default fips config which contains a hard coded list of cipher suites
// that should be equal to the default list of fips cipher suites.
// assert that the client hello response has the same tls config as this test server.
tlsserver.AssertTLS(t, r, ptls.Default)
}), tlsserver.RecordTLSHello)
pool, err := cert.NewPoolFromBytes(ca)
require.NoError(t, err)
// create a tls config that does not explicitly set cipher suites,
// and therefore uses goboring's default fips ciphers.
defaultConfig := &tls.Config{
RootCAs: pool,
NextProtos: ptls.Default(nil).NextProtos, // we do not care about field for this test, so just make it match
}
transport := http.Transport{
TLSClientConfig: defaultConfig,
ForceAttemptHTTP2: true,
}
// make a request against the test server, which will validate that the
// tls config of the client without explicitly set ciphers
// is the same as the tls config of the test server with explicitly
// set ciphers from ptls.
request, _ := http.NewRequest("GET", server.URL, nil)
response, err := transport.RoundTrip(request)
require.NoError(t, err)
require.Equal(t, http.StatusOK, response.StatusCode)
}