1- #pylint: disable=too-many-lines,too-few-public-methods,too-many-locals,too-many-statements,too-many-branches
1+ #pylint: disable=too-many-lines,too-few-public-methods,too-many-locals,too-many-statements,too-many-branches
22import os
33import json
44import time
@@ -43,117 +43,34 @@ def delete_file(path):
4343 except Exception as error :
4444 logger .warning ("Failed to delete file: %s" , error )
4545
46- class Vault ():
47- def __init__ (self , base_url , namespace , version , role_id , secret_id ):
48- self .base_url = base_url
49- self .namespace = namespace
50- self .version = version
51- self .role_id = role_id
52- self .secret_id = secret_id
53- self .token = None
54- self .policies = None
55-
56- def get_token_by_app_role (self ):
57- self .policies = self .get_policies (self .token )
58- if self .token and self .policies :
59- return self .token
60- app_role = {'role_id' : self .role_id , 'secret_id' : self .secret_id }
61- app_role_url = self .base_url + '/v1/' + self .namespace + '/auth/approle/login' if self .namespace else self .base_url + '/v1/auth/approle/login'
62- json_data = json .dumps (app_role )
63- for i in range (0 , 10 ):
64- res = requests .post (url = app_role_url , data = json_data , verify = False )
65- if res .status_code == 200 :
66- json_res = json .loads (res .content )
67- self .token = json_res ['auth' ]['client_token' ]
68- self .policies = self .get_policies (self .token )
69- return self .token
70- time .sleep (5 )
71- err_msg = "Getting token from Vault error even tried 10 times, url is {}, API response is {}:{}" .format (app_role_url , res .status_code , res .text )
72- abort (400 , err_msg )
73-
74- def get_policies (self , token ):
75- """Return the policies associated with the provided token."""
76- if not token :
77- return None
78- try :
79- lookup_url = self .base_url + '/v1/' + self .namespace + '/auth/token/lookup-self' if self .namespace else self .base_url + 'v1/auth/token/lookup-self'
80- res = requests .get (url = lookup_url , headers = {"X-Vault-Token" : token }, verify = False )
81- if res .status_code == 200 :
82- json_res = json .loads (res .content )
83- policies = json_res ['data' ]['policies' ]
84- return policies
85- return None
86- except Exception as e :
87- logger .debug ("Token validation failed: %s" , str (e ))
88- return None
89-
90- def generate_batch_token (self , service_token , ttl = "1h" ):
91- """
92- Generate a batch token using AppRole credentials.
93- :param service_token: A service token generated when logging in with AppRole.
94- :param ttl: Time-to-live for the batch token.
95- :return: The generated batch token.
96- """
97- try :
98- if self .policies is None :
99- self .policies = self .get_policies (service_token ) or ['default' ]
100- if self .policies and 'token-creator' in self .policies :
101- self .policies .remove ('token-creator' )
102-
103- batch_payload = {
104- "type" : "batch" ,
105- "policies" : self .policies or ['default' ],
106- "ttl" : ttl
107- }
108- url = self .base_url + '/v1/' + self .namespace + '/auth/token/create' if self .namespace else self .base_url + '/v1/auth/token/create'
109-
110- for i in range (0 , 10 ):
111- res = requests .post (url = url , json = batch_payload , headers = {"X-Vault-Token" : service_token }, verify = False )
112- if res .status_code == 200 :
113- json_res = json .loads (res .content )
114- token = json_res ['auth' ]['client_token' ]
115- return token
116- elif res .status_code == 403 :
117- logger .info ("Getting batch token from Vault forbidden: {}" .format (res .text ))
118- return None
119- time .sleep (5 )
120- msg = "Getting batch token from Vault failed even tried 10 times, url is {}, API response is {}:{}" .format (
121- url , res .status_code , res .text )
122- logger .info (msg )
123- return None
124- except Exception as e :
125- logger .info ("Exception when getting batch token from Vault: {}" .format (e ))
126- return None
127-
128- def _get_api_url (self , secret_path ):
129- url = self .base_url
130- if not self .namespace :
131- self .namespace = ''
132- if self .version == 'v1' :
133- url += '/v1/' + self .namespace + '/' + secret_path if self .namespace else '/v1/' + secret_path
134- elif self .version == 'v2' :
135- paths = secret_path .split ('/' )
136- url += '/v1/' + self .namespace + '/' + paths [0 ] + '/data/' + '/' .join (paths [1 :]) if self .namespace else '/v1/' + paths [0 ] + '/data/' + '/' .join (paths [1 :])
137- return url
138-
139- def get_value_from_vault (self , token , secret_path , secret_key , verify ):
140- try :
141- url = self ._get_api_url (secret_path )
142- for i in range (0 , 10 ):
143- response = requests .get (url = url , headers = {'X-Vault-Token' : token }, verify = verify , timeout = 30 )
144- if response .status_code == 200 :
145- json_res = json .loads (response .content )
146- if json_res ['data' ].get ('data' ) and isinstance (json_res ['data' ].get ('data' ), dict ):
147- value = json_res ['data' ].get ('data' ).get (secret_key )
148- else :
149- value = json_res ['data' ].get (secret_key )
150- return value
151- time .sleep (5 )
152- err_msg = "Getting value from Vault error even tried 10 times, url is {}, API response is {}:{}" .format (url , response .status_code , response .text )
153- abort (400 , err_msg )
154- except Exception as e :
155- err_msg = "Getting value from Vault exception: {}, url is {}" .format (str (e ), url )
156- abort (400 , err_msg )
46+
47+ def get_token_by_app_role (app_role_url , role_id , secret_id ):
48+ app_role = {'role_id' : role_id , 'secret_id' : secret_id }
49+ json_data = json .dumps (app_role )
50+ for i in range (0 , 10 ):
51+ res = requests .post (url = app_role_url , data = json_data , verify = False )
52+ if res .status_code == 200 :
53+ json_res = json .loads (res .content )
54+ token = json_res ['auth' ]['client_token' ]
55+ return token
56+ time .sleep (5 )
57+ err_msg = "Getting token from Vault error even tried 10 times, url is {}, API response is {}:{}" .format (app_role_url , res .status_code , res .text )
58+ abort (400 , err_msg )
59+
60+
61+ def get_value_from_vault (url , token , secret_key , verify ):
62+ for i in range (0 , 10 ):
63+ response = requests .get (url = url , headers = {'X-Vault-Token' : token }, verify = verify )
64+ if response .status_code == 200 :
65+ json_res = json .loads (response .content )
66+ if json_res ['data' ].get ('data' ) and isinstance (json_res ['data' ].get ('data' ), dict ):
67+ value = json_res ['data' ].get ('data' ).get (secret_key )
68+ else :
69+ value = json_res ['data' ].get (secret_key )
70+ return value
71+ time .sleep (5 )
72+ err_msg = "Getting value from Vault error even tried 10 times, url is {}, API response is {}:{}" .format (url , response .status_code , response .text )
73+ abort (400 , err_msg )
15774
15875
15976@api .route ("/api/job/job" , doc = False )
@@ -361,8 +278,6 @@ def get(self):
361278 ''' , [data ['project' ]['id' ]])
362279
363280 is_fork = data ['job' ].get ('fork' , False )
364- # Cache Vault instances per vault name+project so we create them only once
365- vault_cache = {}
366281
367282 def get_secret_type (name ):
368283 try :
@@ -385,10 +300,10 @@ def get_auth_type(res):
385300 def get_secret (name ):
386301 secret_type = get_secret_type (name )
387302 if secret_type == 'vault' :
388- vault_cfg = json .loads (name )
389- vault_name = vault_cfg ['$vault' ]
390- secret_path = vault_cfg ['$vault_secret_path' ]
391- secret_key = vault_cfg ['$vault_secret_key' ]
303+ vault = json .loads (name )
304+ vault_name = vault ['$vault' ]
305+ secret_path = vault ['$vault_secret_path' ]
306+ secret_key = vault ['$vault_secret_key' ]
392307
393308 result = g .db .execute_one ("""
394309 SELECT url, version, token, ca, namespace, role_id, secret_id FROM vault WHERE name = %s and project_id = %s
@@ -398,36 +313,30 @@ def get_secret(name):
398313 abort (400 , "Cannot get Vault '%s' in project '%s' " % (vault_name , data ['project' ]['id' ]))
399314
400315 url , version , token , ca , namespace , role_id , secret_id = result [0 ], result [1 ], result [2 ], result [3 ], result [4 ], result [5 ], result [6 ]
316+ if not namespace :
317+ namespace = ''
318+ if version == 'v1' :
319+ url += '/v1/' + namespace + '/' + secret_path if namespace else '/v1/' + secret_path
320+ elif version == 'v2' :
321+ paths = secret_path .split ('/' )
322+ url += '/v1/' + namespace + '/' + paths [0 ] + '/data/' + '/' .join (paths [1 :]) if namespace else '/v1/' + paths [0 ] + '/data/' + '/' .join (paths [1 :])
401323 # choose validate way
402324 validate_res = get_auth_type (result )
403-
404- # key vault per project to avoid collisions
405- vault_key = f"{ vault_name } -{ data ['project' ]['id' ]} "
406- vault_client = vault_cache .get (vault_key )
407- if not vault_client :
408- vault_client = Vault (url , namespace , version , role_id , secret_id )
409- vault_cache [vault_key ] = vault_client
410-
411325 if validate_res == 'token' :
412326 logger .info ('validate way is token' )
413- token_to_use = token
414327 elif validate_res == 'appRole' :
415- logger .info ('validate way is appRole' )
416- token_to_use = vault_client .get_token_by_app_role ()
417- batch_token = vault_client .generate_batch_token (token_to_use )
418- if batch_token :
419- token_to_use = batch_token
328+ app_role_url = result [0 ] + '/v1/' + namespace + '/auth/approle/login' if namespace else result [0 ] + '/v1/auth/approle/login'
329+ token = get_token_by_app_role (app_role_url , role_id , secret_id )
420330 else :
421331 abort (400 , "Validate way is '%s' ! result is '%s' " % (validate_res , result ))
422332
423333 if not ca :
424- logger .info ('Start to get value fron vault %s %s %s' % (token_to_use , secret_path , secret_key ))
425- return vault_client .get_value_from_vault (token_to_use , secret_path , secret_key , False )
334+ return get_value_from_vault (url , token , secret_key , False )
426335 else :
427336 with tempfile .NamedTemporaryFile (delete = False ) as f :
428337 f .write (ca )
429338 f .flush () # ensure all data written
430- return vault_client . get_value_from_vault (token_to_use , secret_path , secret_key , f .name )
339+ return get_value_from_vault (url , token , secret_key , f .name )
431340 else :
432341 if is_fork :
433342 abort (400 , 'Access to secret %s is not allowed from a fork' % name )
0 commit comments