-
Notifications
You must be signed in to change notification settings - Fork 137
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use cluster issuer as default builder id
This is probably the most "correct" value for this field - it should uniquely identify the cluster, and will match other signature data included in Fulcio certs, etc. This is technically a breaking change, but likely one worth making. Users can still override this behavior with the config map as before. If omitted, this field is not populated as an indication that we don't know how to accurately identify this cluster.
- Loading branch information
Showing
7 changed files
with
239 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package cluster | ||
|
||
import ( | ||
"context" | ||
"os" | ||
|
||
"github.com/golang-jwt/jwt/v5" | ||
"golang.org/x/oauth2" | ||
"knative.dev/pkg/logging" | ||
) | ||
|
||
var ( | ||
provider = &tokenSource{ | ||
path: "/var/run/secrets/kubernetes.io/serviceaccount/token", | ||
} | ||
) | ||
|
||
type tokenSource struct { | ||
path string | ||
|
||
token *oauth2.Token | ||
iss string | ||
} | ||
|
||
func (ts *tokenSource) Token() (*oauth2.Token, error) { | ||
if ts.token.Valid() { | ||
return ts.token, nil | ||
} | ||
b, err := os.ReadFile(ts.path) | ||
if err != nil { | ||
return nil, err | ||
} | ||
ts.token = &oauth2.Token{ | ||
AccessToken: string(b), | ||
} | ||
|
||
return ts.token, nil | ||
} | ||
|
||
func (ts *tokenSource) Issuer(ctx context.Context) string { | ||
log := logging.FromContext(ctx) | ||
|
||
if ts.token.Valid() && ts.iss != "" { | ||
return ts.iss | ||
} | ||
|
||
oauth, err := ts.Token() | ||
if err != nil { | ||
log.Errorf("failed to get cluster token: %v", err) | ||
return "" | ||
} | ||
|
||
// We're assuming that in order to place a token in the path | ||
// you already have some amount of privilege. | ||
// While we don't know if the token is actually real, even if we | ||
// wanted to verify it against the api server we'd need to trust | ||
// the cacert bundle also included in this same path, so trusting | ||
// the token is correctly set by the Kubernetes cluster is | ||
// likely a reasonable compromise. | ||
parser := jwt.NewParser() | ||
claims := new(jwt.RegisteredClaims) | ||
if _, _, err := parser.ParseUnverified(oauth.AccessToken, claims); err != nil { | ||
log.Errorf("failed to parse cluster token: %v", err) | ||
return "" | ||
} | ||
|
||
ts.iss = claims.Issuer | ||
return claims.Issuer | ||
} | ||
|
||
// BuilderID returns a builder ID that the current cluster. | ||
// To approximate this, we use the cluster token issuer as defined by | ||
// the controller's default service account token. | ||
// See https://kubernetes.io/docs/tasks/run-application/access-api-from-pod/#directly-accessing-the-rest-api for more details. | ||
// This will be change depending on where/how the cluster is running. | ||
// | ||
// Some examples: | ||
// - GKE: https://containers.googleapis.com/v1/projects/123456789012/locations/us-east1/clusters/cluster-1 | ||
// - EKS: https://oidc.eks.us-east-1.amazonaws.com/id/12345678901234567890123456789012 | ||
// - AKS: https://eastus.oic.prod-aks.azure.com/00000000-0000-0000-0000-000000000000/00000000-0000-0000-0000-000000000000/ | ||
// - Kind/Local: https://kubernetes.default.svc (NOTE: this isn't a real URL and won't give you much useful information) | ||
func BuilderID(ctx context.Context) string { | ||
return provider.Issuer(ctx) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package cluster | ||
|
||
import ( | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
"time" | ||
|
||
"github.com/golang-jwt/jwt/v5" | ||
logtesting "knative.dev/pkg/logging/testing" | ||
) | ||
|
||
func TestBuilderID(t *testing.T) { | ||
// Create the Claims | ||
claims := &jwt.RegisteredClaims{ | ||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(1 * time.Hour)), | ||
Issuer: "example.com", | ||
} | ||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) | ||
|
||
pk, err := rsa.GenerateKey(rand.Reader, 2048) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
ss, err := token.SignedString(pk) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
// This is a real token taken from a local kind cluster. | ||
realToken, err := os.ReadFile("testdata/token.txt") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
for _, tc := range []struct { | ||
token []byte | ||
want string | ||
}{ | ||
{ | ||
token: []byte(ss), | ||
want: claims.Issuer, | ||
}, | ||
{ | ||
token: realToken, | ||
want: "https://kubernetes.default.svc", | ||
}, | ||
} { | ||
t.Run(tc.want, func(t *testing.T) { | ||
path := filepath.Join(t.TempDir(), "token") | ||
if err := os.WriteFile(path, []byte(ss), 0600); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
provider = &tokenSource{ | ||
path: path, | ||
} | ||
|
||
ctx := logtesting.TestContextWithLogger(t) | ||
got := BuilderID(ctx) | ||
if got != claims.Issuer { | ||
t.Errorf("BuilderID() = %s, want %s", got, claims.Issuer) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
eyJhbGciOiJSUzI1NiIsImtpZCI6IjR2b0s2RFJKZ1EybG04TmVMR2wyWFVUQ2xmNjFFV1E0cU9Fc2UxT0d4X1kifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzMyMzIzMTY3LCJpYXQiOjE3MDA3ODcxNjcsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJoZWxsby1weDJqai1wb2QiLCJ1aWQiOiI0MDUyZDY2NS1iMThjLTQ2OTYtOGM3MS0wNGI2MjY2OTNhN2EifSwic2VydmljZWFjY291bnQiOnsibmFtZSI6ImRlZmF1bHQiLCJ1aWQiOiJmODg3NDBiMy1jNzRmLTQyNmYtYTllNi0xMDIyMjk2OWI2NjUifSwid2FybmFmdGVyIjoxNzAwNzkwNzc0fSwibmJmIjoxNzAwNzg3MTY3LCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpkZWZhdWx0In0.<omitted> |