worker: Support client_credentials grant type in client
This will allow us to use the service accounts which work against identity.api.openshift.com. These are much easier to manage, especially with the new multi-tenancy, as there's a single page to create/expire them across an account. They also have the added benefit of not expiring automatically when they're not used like offline tokens, and immediate expiration when desired.
This commit is contained in:
parent
8900bcec40
commit
2023f7731d
8 changed files with 84 additions and 19 deletions
|
|
@ -142,6 +142,8 @@ func main() {
|
|||
Authentication *struct {
|
||||
OAuthURL string `toml:"oauth_url"`
|
||||
OfflineTokenPath string `toml:"offline_token"`
|
||||
ClientId string `toml:"client_id"`
|
||||
ClientSecretPath string `toml:"client_secret"`
|
||||
} `toml:"authentication"`
|
||||
RelaxTimeoutFactor uint `toml:"RelaxTimeoutFactor"`
|
||||
BasePath string `toml:"base_path"`
|
||||
|
|
@ -205,7 +207,7 @@ func main() {
|
|||
BaseURL: address,
|
||||
BasePath: config.BasePath,
|
||||
})
|
||||
} else if config.Authentication != nil && config.Authentication.OfflineTokenPath != "" {
|
||||
} else if config.Authentication != nil {
|
||||
var conf *tls.Config
|
||||
conConf := &connectionConfig{
|
||||
CACertFile: "/etc/osbuild-composer/ca-crt.pem",
|
||||
|
|
@ -217,13 +219,22 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
t, err := ioutil.ReadFile(config.Authentication.OfflineTokenPath)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Could not read offline token: %v", err)
|
||||
token := ""
|
||||
if config.Authentication.OfflineTokenPath != "" {
|
||||
t, err := ioutil.ReadFile(config.Authentication.OfflineTokenPath)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Could not read offline token: %v", err)
|
||||
}
|
||||
token = strings.TrimSpace(string(t))
|
||||
}
|
||||
token := strings.TrimSpace(string(t))
|
||||
if token != "" && config.Authentication.OAuthURL == "" {
|
||||
logrus.Fatal("OAuth URL should be specified together with the offline token")
|
||||
|
||||
clientSecret := ""
|
||||
if config.Authentication.ClientSecretPath != "" {
|
||||
cs, err := ioutil.ReadFile(config.Authentication.ClientSecretPath)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Could not read client secret: %v", err)
|
||||
}
|
||||
clientSecret = strings.TrimSpace(string(cs))
|
||||
}
|
||||
|
||||
client, err = worker.NewClient(worker.ClientConfig{
|
||||
|
|
@ -231,6 +242,8 @@ func main() {
|
|||
TlsConfig: conf,
|
||||
OfflineToken: token,
|
||||
OAuthURL: config.Authentication.OAuthURL,
|
||||
ClientId: config.Authentication.ClientId,
|
||||
ClientSecret: clientSecret,
|
||||
BasePath: config.BasePath,
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ type Client struct {
|
|||
offlineToken string
|
||||
oAuthURL string
|
||||
accessToken string
|
||||
clientId string
|
||||
clientSecret string
|
||||
|
||||
tokenMu sync.RWMutex
|
||||
}
|
||||
|
|
@ -33,6 +35,8 @@ type ClientConfig struct {
|
|||
TlsConfig *tls.Config
|
||||
OfflineToken string
|
||||
OAuthURL string
|
||||
ClientId string
|
||||
ClientSecret string
|
||||
BasePath string
|
||||
}
|
||||
|
||||
|
|
@ -76,6 +80,15 @@ func NewClient(conf ClientConfig) (*Client, error) {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
if conf.OAuthURL != "" {
|
||||
if conf.ClientId == "" {
|
||||
return nil, fmt.Errorf("OAuthURL token url specified but no client id")
|
||||
}
|
||||
if conf.OfflineToken == "" && conf.ClientSecret == "" {
|
||||
return nil, fmt.Errorf("OAuthURL token url specified but no client secret or offline token")
|
||||
}
|
||||
}
|
||||
|
||||
requester := &http.Client{}
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
if conf.TlsConfig != nil {
|
||||
|
|
@ -88,6 +101,8 @@ func NewClient(conf ClientConfig) (*Client, error) {
|
|||
requester: requester,
|
||||
offlineToken: conf.OfflineToken,
|
||||
oAuthURL: conf.OAuthURL,
|
||||
clientId: conf.ClientId,
|
||||
clientSecret: conf.ClientSecret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -118,19 +133,21 @@ func NewClientUnix(conf ClientConfig) *Client {
|
|||
}
|
||||
}
|
||||
|
||||
// Note: Only call this function with Client.tokenMu locked!
|
||||
func (c *Client) refreshAccessToken() error {
|
||||
c.tokenMu.Lock()
|
||||
defer c.tokenMu.Unlock()
|
||||
|
||||
if c.offlineToken == "" || c.oAuthURL == "" {
|
||||
return fmt.Errorf("No offline token or oauth url available")
|
||||
}
|
||||
|
||||
data := url.Values{}
|
||||
data.Set("grant_type", "refresh_token")
|
||||
data.Set("client_id", "rhsm-api")
|
||||
data.Set("refresh_token", c.offlineToken)
|
||||
if c.offlineToken != "" {
|
||||
data.Set("grant_type", "refresh_token")
|
||||
data.Set("client_id", c.clientId)
|
||||
data.Set("refresh_token", c.offlineToken)
|
||||
}
|
||||
if c.clientSecret != "" {
|
||||
data.Set("grant_type", "client_credentials")
|
||||
data.Set("client_id", c.clientId)
|
||||
data.Set("client_secret", c.clientSecret)
|
||||
}
|
||||
|
||||
resp, err := http.PostForm(c.oAuthURL, data)
|
||||
if err != nil {
|
||||
|
|
@ -159,8 +176,7 @@ func (c *Client) NewRequest(method, url string, headers map[string]string, body
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// If we're using OAUTH, add the Bearer token
|
||||
if c.offlineToken != "" {
|
||||
if c.oAuthURL != "" {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token()))
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +189,7 @@ func (c *Client) NewRequest(method, url string, headers map[string]string, body
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusUnauthorized {
|
||||
if resp.StatusCode == http.StatusUnauthorized && c.oAuthURL != "" {
|
||||
err = c.refreshAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -399,6 +399,7 @@ func TestOAuth(t *testing.T) {
|
|||
client, err := worker.NewClient(worker.ClientConfig{
|
||||
BaseURL: proxySrv.URL,
|
||||
TlsConfig: nil,
|
||||
ClientId: "rhsm-api",
|
||||
OfflineToken: offlineToken,
|
||||
OAuthURL: oauthSrv.URL,
|
||||
BasePath: "/api/image-builder-worker/v1",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
source /tmp/cloud_init_vars
|
||||
|
||||
echo "Writing client credentials."
|
||||
|
||||
if [[ -z "$CLIENT_CREDENTIALS_ARN" ]]; then
|
||||
echo "CLIENT_CREDENTIALS_ARN not defined, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# get client credentials
|
||||
/usr/local/bin/aws secretsmanager get-secret-value \
|
||||
--endpoint-url "${SECRETS_MANAGER_ENDPOINT_URL}" \
|
||||
--secret-id "${CLIENT_CREDENTIALS_ARN}" | jq -r ".SecretString" > /tmp/client-credentials.json
|
||||
|
||||
CLIENT_ID=$(jq -r ".client_id" /tmp/client-credentials.json)
|
||||
jq -r ".client_secret" /tmp/client-credentials.json > /etc/osbuild-worker/client-secret
|
||||
rm -f /tmp/client-credentials.json
|
||||
|
||||
sudo tee -a /etc/osbuild-worker/osbuild-worker.toml > /dev/null << EOF
|
||||
[authentication]
|
||||
oauth_url = "https://identity.api.openshift.com/auth/realms/rhoas/protocol/openid-connect/token"
|
||||
client_id = "${CLIENT_ID}"
|
||||
client_secret = "/etc/osbuild-worker/client-secret"
|
||||
EOF
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
set -eo pipefail
|
||||
source /tmp/cloud_init_vars
|
||||
|
||||
echo "Writing offline token."
|
||||
|
||||
if [[ -z "$OFFLINE_TOKEN_ARN" ]]; then
|
||||
echo "OFFLINE_TOKEN_ARN not defined, skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# get offline token
|
||||
/usr/local/bin/aws secretsmanager get-secret-value \
|
||||
--endpoint-url "${SECRETS_MANAGER_ENDPOINT_URL}" \
|
||||
|
|
@ -15,5 +20,6 @@ rm -f /tmp/offline-token.json
|
|||
sudo tee -a /etc/osbuild-worker/osbuild-worker.toml > /dev/null << EOF
|
||||
[authentication]
|
||||
oauth_url = "https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token"
|
||||
client_id = "rhsm-api"
|
||||
offline_token = "/etc/osbuild-worker/offline-token"
|
||||
EOF
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ ExecStart=touch /etc/worker-first-boot
|
|||
ExecStart=/usr/local/libexec/worker-initialization-scripts/set_hostname.sh
|
||||
ExecStart=/usr/local/libexec/worker-initialization-scripts/vector.sh
|
||||
ExecStart=/usr/local/libexec/worker-initialization-scripts/offline_token.sh
|
||||
ExecStart=/usr/local/libexec/worker-initialization-scripts/client_credentials.sh
|
||||
ExecStart=/usr/local/libexec/worker-initialization-scripts/subscription_manager.sh
|
||||
ExecStart=/usr/local/libexec/worker-initialization-scripts/get_aws_creds.sh
|
||||
ExecStart=/usr/local/libexec/worker-initialization-scripts/get_azure_creds.sh
|
||||
|
|
|
|||
|
|
@ -1294,6 +1294,7 @@ EOF
|
|||
cat <<EOF | sudo tee "/etc/osbuild-worker/osbuild-worker.toml"
|
||||
[authentication]
|
||||
oauth_url = http://localhost:8081/token
|
||||
client_id = "rhsm-api"
|
||||
offline_token = "/etc/osbuild-worker/token"
|
||||
EOF
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ EOF
|
|||
sudo tee "/etc/osbuild-worker/osbuild-worker.toml" >/dev/null <<EOF
|
||||
[authentication]
|
||||
oauth_url = "http://localhost:8081/token"
|
||||
client_id = "rhsm-api"
|
||||
offline_token = "/etc/osbuild-worker/token"
|
||||
|
||||
[koji.localhost.kerberos]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue