From 8060d363b9b37f2b22920f549253c98b7932f253 Mon Sep 17 00:00:00 2001 From: Diego Ciangottini Date: Tue, 2 Jul 2024 21:28:19 +0200 Subject: [PATCH] Fix installation script and client credentials support in refresh token (#247) * add client_credentials flow and fix deployment script and refresh.py --------- Signed-off-by: Diego Ciangottini --- cmd/installer/main.go | 20 +-- cmd/installer/templates/deployment.yaml | 8 +- cmd/installer/templates/interlink-install.sh | 6 +- cmd/installer/templates/service-account.yaml | 2 +- docker/scripts/refresh.py | 128 +++++++++++++----- .../tutorial-admins/01-deploy-interlink.mdx | 1 + docs/docs/tutorial-admins/04-oidc-IAM.md | 2 + 7 files changed, 109 insertions(+), 58 deletions(-) diff --git a/cmd/installer/main.go b/cmd/installer/main.go index 7d3a5eca..33d49302 100644 --- a/cmd/installer/main.go +++ b/cmd/installer/main.go @@ -12,7 +12,6 @@ import ( "github.com/spf13/cobra" "golang.org/x/oauth2" - "golang.org/x/oauth2/clientcredentials" "gopkg.in/yaml.v3" ) @@ -45,7 +44,7 @@ type oauthStruct struct { RefreshToken string `yaml:"refresh_token,omitempty"` Audience string `yaml:"audience,omitempty"` Group string `yaml:"group,omitempty"` - GroupClaim string `yaml:"groupClaim,omitempty"` + GroupClaim string `default:"groups" yaml:"group_claim"` Scopes []string `yaml:"scopes"` GitHUBUser string `yaml:"github_user"` TokenURL string `yaml:"token_url"` @@ -202,28 +201,15 @@ func root(cmd *cobra.Command, args []string) error { //fmt.Println(token.RefreshToken) //fmt.Println(token.Expiry) //fmt.Println(token.TokenType) + configCLI.OAUTH.RefreshToken = token.RefreshToken } else if configCLI.OAUTH.GrantType == "client_credentials" { - cfg := clientcredentials.Config{ - ClientID: configCLI.OAUTH.ClientID, - ClientSecret: configCLI.OAUTH.ClientSecret, - TokenURL: configCLI.OAUTH.TokenURL, - Scopes: configCLI.OAUTH.Scopes, - } - token, err = cfg.Token(ctx) - if err != nil { - panic(err) - } - //fmt.Println(token.AccessToken) - //fmt.Println(token.RefreshToken) - //fmt.Println(token.Expiry) - //fmt.Println(token.TokenType) + fmt.Println("Client_credentials set, I won't try to get any refresh token.") } else { panic(fmt.Errorf("wrong grant type specified in the configuration. Only client_credentials and authorization_code are supported")) } - configCLI.OAUTH.RefreshToken = token.RefreshToken namespaceYAML, err := evalManifest("templates/namespace.yaml", configCLI) if err != nil { diff --git a/cmd/installer/templates/deployment.yaml b/cmd/installer/templates/deployment.yaml index b186dd9a..e2b26229 100644 --- a/cmd/installer/templates/deployment.yaml +++ b/cmd/installer/templates/deployment.yaml @@ -20,7 +20,7 @@ spec: - name: jaeger image: jaegertracing/all-in-one:1.51 - name: inttw-vk - image: ghcr.io/intertwin-eu/interlink/virtual-kubelet-inttw:latest + image: ghcr.io/intertwin-eu/interlink/virtual-kubelet-inttw:{{.InterLinkVersion}} imagePullPolicy: Always env: - name: NODENAME @@ -49,7 +49,7 @@ spec: cpu: 100m memory: 200Mi - name: refresh-token - image: ghcr.io/intertwin-eu/virtual-kubelet-inttw-refresh:latest + image: ghcr.io/intertwin-eu/virtual-kubelet-inttw-refresh:{{.InterLinkVersion}} imagePullPolicy: Always env: - name: IAM_TOKEN_ENDPOINT @@ -58,7 +58,9 @@ spec: - name: IAM_CLIENT_ID value: {{.OAUTH.ClientID}} - name: IAM_CLIENT_SECRET - value: {{.OAUTH.ClientSecret}} + value: "{{.OAUTH.ClientSecret}}" + - name: IAM_GRANT_TYPE + value: {{.OAUTH.GrantType}} - name: IAM_REFRESH_TOKEN value: {{.OAUTH.RefreshToken}} - name: IAM_VK_AUD diff --git a/cmd/installer/templates/interlink-install.sh b/cmd/installer/templates/interlink-install.sh index 88c883fc..97bb8f66 100644 --- a/cmd/installer/templates/interlink-install.sh +++ b/cmd/installer/templates/interlink-install.sh @@ -87,10 +87,10 @@ start() { case "{{.OAUTH.Provider}}" in oidc) $HOME/.interlink/bin/oauth2-proxy \ - --client-id DUMMY \ - --client-secret DUMMY \ + --client-id "{{.OAUTH.ClientID}}" \ + --client-secret "\"{{.OAUTH.ClientSecret}}\"" \ --http-address 0.0.0.0:{{.InterLinkPort}} \ - --oidc-issuer-url {{.OAUTH.Issuer}} \ + --oidc-issuer-url "{{.OAUTH.Issuer}}" \ --pass-authorization-header true \ --provider oidc \ --redirect-url http://localhost:8081 \ diff --git a/cmd/installer/templates/service-account.yaml b/cmd/installer/templates/service-account.yaml index 3ffdd0f9..4a086a73 100644 --- a/cmd/installer/templates/service-account.yaml +++ b/cmd/installer/templates/service-account.yaml @@ -7,7 +7,7 @@ metadata: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: virtual-kubelet + name: {{.VKName}} namespace: interlink rules: - apiGroups: diff --git a/docker/scripts/refresh.py b/docker/scripts/refresh.py index 1bf444dc..bf051901 100644 --- a/docker/scripts/refresh.py +++ b/docker/scripts/refresh.py @@ -15,6 +15,7 @@ sync OIDC identities on user accounts """ try: + iam_grant_type = os.environ.get("IAM_GRANT_TYPE") iam_server = os.environ.get( "IAM_TOKEN_ENDPOINT", "https://cms-auth.web.cern.ch/token") iam_client_id = os.environ.get("IAM_CLIENT_ID") @@ -38,47 +39,106 @@ token = None while True: - try: - request_data = { - "audience": audience, - "grant_type": "refresh_token", - "refresh_token": iam_refresh_token, - #"scope": "openid profile email address phone offline_access" - } - - from requests.auth import HTTPBasicAuth - auth = HTTPBasicAuth(iam_client_id, iam_client_secret) - - r = requests.post(iam_server, data=request_data, auth=auth) - print(r.text) + if iam_grant_type == "client_credentials": try: - response = json.loads(r.text) - except: + request_data = { + "audience": audience, + "grant_type": iam_grant_type, + "client_id" : iam_client_id, + "client_secret": iam_client_secret + #"scope": "openid profile email address phone offline_access" + } + + from requests.auth import HTTPBasicAuth + auth = HTTPBasicAuth(iam_client_id, iam_client_secret) + headers = {'Content-Type': 'application/x-www-form-urlencoded'} + r = requests.post(iam_server, data=request_data, auth=auth, headers=headers) + print(r.text) + try: + response = json.loads(r.text) + except: + try: + response = dict(parse.parse_qsl(r.text)) + print(response) + except: + exit(1) + + + print(iam_client_id, iam_client_secret, response) + + token = response['access_token'] + try: + refresh_token = response['refresh_token'] + except: + refresh_token = iam_refresh_token + + + print("Token retrieved") + + ## TODO: collect new refresh token and store it somewhere + with open(output_file+"-refresh", "w") as text_file: + text_file.write(refresh_token) + + print(f"Refresh token written in {output_file+'-refresh'}") + + with open(output_file, "w") as text_file: + text_file.write(token) + + print(f"Token written in {output_file}") + + except Exception as e: + logging.warn("ERROR oidc get token: {}".format(e)) + + elif iam_grant_type == "authorization_code": + + try: + request_data = { + "audience": audience, + "grant_type": "refresh_token", + "refresh_token": iam_refresh_token, + #"scope": "openid profile email address phone offline_access" + } + + from requests.auth import HTTPBasicAuth + auth = HTTPBasicAuth(iam_client_id, iam_client_secret) + + r = requests.post(iam_server, data=request_data, auth=auth) + print(r.text) + try: + response = json.loads(r.text) + except: + try: + response = dict(parse.parse_qsl(r.text)) + print(response) + except: + exit(1) + + + print(iam_client_id, iam_client_secret, response) + + token = response['access_token'] try: - response = dict(parse.parse_qsl(r.text)) - print(response) + refresh_token = response['refresh_token'] except: - exit(1) - + refresh_token = iam_refresh_token - print(iam_client_id, iam_client_secret, response) - token = response['access_token'] - refresh_token = response['refresh_token'] - print("Token retrieved") + print("Token retrieved") - ## TODO: collect new refresh token and store it somewhere - with open(output_file+"-refresh", "w") as text_file: - text_file.write(refresh_token) + ## TODO: collect new refresh token and store it somewhere + with open(output_file+"-refresh", "w") as text_file: + text_file.write(refresh_token) - print(f"Refresh token written in {output_file+'-refresh'}") + print(f"Refresh token written in {output_file+'-refresh'}") - with open(output_file, "w") as text_file: - text_file.write(token) + with open(output_file, "w") as text_file: + text_file.write(token) - print(f"Token written in {output_file}") + print(f"Token written in {output_file}") - except Exception as e: - logging.warn("ERROR oidc get token: {}".format(e)) - - time.sleep(1000) + except Exception as e: + logging.warn("ERROR oidc get token: {}".format(e)) + else: + logging.error(f"Invalid grant type {iam_grant_type}" ) + exit(1) + time.sleep(200) diff --git a/docs/docs/tutorial-admins/01-deploy-interlink.mdx b/docs/docs/tutorial-admins/01-deploy-interlink.mdx index 80277e42..8522a257 100644 --- a/docs/docs/tutorial-admins/01-deploy-interlink.mdx +++ b/docs/docs/tutorial-admins/01-deploy-interlink.mdx @@ -103,6 +103,7 @@ node_limits: oauth: provider: github issuer: https://github.com/oauth + grant_type: authorization_code scopes: - "read:user" github_user: "dciangot" diff --git a/docs/docs/tutorial-admins/04-oidc-IAM.md b/docs/docs/tutorial-admins/04-oidc-IAM.md index de6758ca..39c9cc7c 100644 --- a/docs/docs/tutorial-admins/04-oidc-IAM.md +++ b/docs/docs/tutorial-admins/04-oidc-IAM.md @@ -23,6 +23,7 @@ oauth: - "offline_access" - "profile" audience: interlink + grant_type: authorization_code group_claim: email group: "YOUR EMAIL HERE" token_url: "https://aai.egi.eu/auth/realms/egi/protocol/openid-connect/token" @@ -72,6 +73,7 @@ oauth: - "offline_access" - "profile" audience: users + grant_type: authorization_code group_claim: email group: "YOUR EMAIL HERE" token_url: "https://iam.cloud.infn.it/token"