def test_docker_pull_config_secret(): pull_config_str = '{"auths":{"example.com":{"username":"******","password":"******",'\ '"email":"*****@*****.**","auth":"f00BA7"}}}' migrated_dcos_secret = V1Secret( kind='Secret', api_version='v1', metadata=V1ObjectMeta(name='nothing-depends-on-this-name'), data={'nothing-depends-on-the-name-of-this-key': pull_config_str}) input_manifest_list = ManifestList() input_manifest_list.append( Manifest(pluginName="secret", manifestName="foo.docker-c_nfig", data=[migrated_dcos_secret])) app = { "id": "/foo/barify", "container": { "docker": { "pullConfig": { "secret": "pull-config" } } }, "env": { "BAR": { "secret": "pull-config" } }, # See the NOTE below "secrets": { "pull-config": { "source": "/foo/docker-c@nfig" }, "unused": { "source": "unused" }, }, } migrator = MarathonMigrator(object=app, manifest_list=input_manifest_list) manifest = migrator.migrate() # NOTE: Thit test expects that two secrets will be created: # one for the image pull config and another for everything else. # This might be not the optimal migration startegy. [deployment] = [m for m in manifest if isinstance(m, V1Deployment)] [pull_secret] = [m for m in manifest \ if isinstance(m, V1Secret) and m.type == "kubernetes.io/dockerconfigjson"] [generic_secret] = [m for m in manifest \ if isinstance(m, V1Secret) and m.type != "kubernetes.io/dockerconfigjson"] assert deployment.spec.template.spec.image_pull_secrets[ 0].name == pull_secret.metadata.name assert pull_secret.data[".dockerconfigjson"] == pull_config_str assert generic_secret.data["foo.docker-c_nfig"] == pull_config_str
def migrate(self, backupList: BackupList, manifestList: ManifestList, **kwargs: Any) -> ManifestList: ml = ManifestList() for ba in backupList.backups(pluginName='secret'): assert isinstance(ba, Backup) metadata = V1ObjectMeta() metadata.annotations = {} clusterMeta = manifestList.clusterMeta() if clusterMeta: metadata.annotations = clusterMeta.annotations logging.debug("Found backup {}".format(ba)) b = ba.data fullPath = "/".join(filter(None, [b["path"], b["key"]])) name = b["key"] metadata.annotations[utils.namespace_path( "secret-path")] = fullPath metadata.name = utils.make_subdomain(name.split('/')) sec = V1Secret(metadata=metadata) sec.api_version = 'v1' sec.kind = 'Secret' # K8s requires secret values to be base64-encoded. The secret value # is base64-encoded during backup so it can be passed as-is here. sec.data = {utils.dnsify(name): b['value']} manifest = Manifest(pluginName=self.plugin_name, manifestName=utils.dnsify(fullPath)) manifest.append(sec) ml.append(manifest) return ml
def _create_remapped_secret( manifest_list: Optional[ManifestList], remapping: SecretRemapping, app_id: str, ) -> Optional[V1Secret]: if not remapping.key_mapping: return None assert manifest_list is not None clusterMeta: Optional[V1ObjectMeta] = manifest_list.clusterMeta() metadata = V1ObjectMeta(annotations={}) if clusterMeta is not None: metadata.annotations = clusterMeta.annotations metadata.annotations[utils.namespace_path("marathon-appid")] = app_id metadata.name = utils.dnsify(remapping.dest_name) secret = V1Secret(metadata=metadata, data={}) secret.api_version = 'v1' secret.kind = 'Secret' if remapping.dest_type is not None: secret.type = remapping.dest_type for source_key, destination_key in remapping.key_mapping.items(): sourceSecret = manifest_list.manifest(pluginName='secret', manifestName=source_key) if not sourceSecret: raise NoMigratedSecretFound('No migrated secret "{}" found'.format(source_key)) [value] = sourceSecret[0].data.values() secret.data[destination_key] = value return secret
def make_secret( name, username, cert_paths, hub_ca, owner_references, labels=None, annotations=None, ): """ Make a k8s secret specification using pre-existing ssl credentials for a given user. Parameters ---------- name: Name of the secret. Must be unique within the namespace the object is going to be created in. username: The name of the user notebook. cert_paths: JupyterHub spawners cert_paths dictionary container certificate path references hub_ca: Path to the hub certificate authority labels: Labels to add to the secret. annotations: Annotations to add to the secret. """ secret = V1Secret() secret.kind = "Secret" secret.api_version = "v1" secret.metadata = V1ObjectMeta() secret.metadata.name = name secret.metadata.annotations = (annotations or {}).copy() secret.metadata.labels = (labels or {}).copy() secret.metadata.owner_references = owner_references secret.data = {} with open(cert_paths['keyfile'], 'r') as file: encoded = base64.b64encode(file.read().encode("utf-8")) secret.data['ssl.key'] = encoded.decode("utf-8") with open(cert_paths['certfile'], 'r') as file: encoded = base64.b64encode(file.read().encode("utf-8")) secret.data['ssl.crt'] = encoded.decode("utf-8") with open(cert_paths['cafile'], 'r') as file: encoded = base64.b64encode(file.read().encode("utf-8")) secret.data["notebooks-ca_trust.crt"] = encoded.decode("utf-8") with open(hub_ca, 'r') as file: encoded = base64.b64encode(file.read().encode("utf-8")) secret.data["notebooks-ca_trust.crt"] = secret.data[ "notebooks-ca_trust.crt" ] + encoded.decode("utf-8") return secret
def mocked_k8s_CoreV1Api(mocker): mocked_coreV1Api_class = mocker.patch('kubernetes.client.CoreV1Api') mocker.patch('kubernetes.client.ApiClient') coreV1API_instance = mocked_coreV1Api_class.return_value pods_mock = MagicMock() pods_mock.items = [ MagicMock(spec=V1Pod), MagicMock(spec=V1Pod), MagicMock(spec=V1Pod) ] coreV1API_instance.list_pod_for_all_namespaces.return_value = pods_mock services_mock = MagicMock() services_mock.items = [ MagicMock(spec=V1Service), MagicMock(spec=V1Service), MagicMock(spec=V1Service) ] coreV1API_instance.list_service_for_all_namespaces.return_value = services_mock v1_namespace = V1Namespace() v1_metadata_namespace = V1ObjectMeta(name=test_namespace) v1_namespace.metadata = v1_metadata_namespace v1_namespace_status = V1NamespaceStatus(phase=NamespaceStatus.ACTIVE.value) v1_namespace.status = v1_namespace_status coreV1API_instance.read_namespace.return_value = v1_namespace coreV1API_instance.delete_namespace.return_value = V1Status( status="{'phase': 'Terminating'}") v1_config_map = V1ConfigMap(data=test_config_map_data()) coreV1API_instance.read_namespaced_config_map.return_value = v1_config_map secret_data = {"token": TEST_TOKEN} v1_metadata_secret = V1ObjectMeta(name="default-token") v1_secret = V1Secret(metadata=v1_metadata_secret, data=secret_data) v1_secret_list = V1SecretList(items=[v1_secret]) coreV1API_instance.list_namespaced_secret.return_value = v1_secret_list v1_pod_status = V1PodStatus(phase=K8S_RUNNING_POD_STATUS) v1_pod = V1Pod(status=v1_pod_status) v1_pod_lists = V1PodList(items=[v1_pod]) coreV1API_instance.list_namespaced_pod.return_value = v1_pod_lists v1_metadata_event = V1ObjectMeta(name="default-name") v1_object = V1ObjectReference(name="pod_name") v1_event = V1Event(message="Insufficient cpu", involved_object=v1_object, metadata=v1_metadata_event) v1_event_list = V1EventList(items=[v1_event]) coreV1API_instance.list_namespaced_event.return_value = v1_event_list return coreV1API_instance
def create_example_list_manifest(dir: str) -> ManifestList: list = ManifestList(path=str(dir)) p = "testPlugin" b = "foobar" metadata = V1ObjectMeta(name="secret1") sec = V1Secret(metadata=metadata, kind="Secret", api_version="v1") sec.data = {"secret1": "Zm9vYmFy"} d = [sec] list.append(Manifest(pluginName=p, manifestName=b, data=d)) list.store() return list, p, b, d
def generate_drive_config(self, url, username, token, **kwargs): base = DEFAULT_DRIVE_CONFIG.copy() base.update(**kwargs) cp = configparser.ConfigParser() cp.read_dict(base) cp['account'].update(dict(server=url, username=username, token=token)) cfg = io.StringIO() cp.write(cfg) cfg.seek(0) data = {'seadrive.conf': cfg.read()} metadata = V1ObjectMeta( name='seadrive-{username}-conf'.format(username=username), annotations={'username': username}) secret = V1Secret(string_data=data, metadata=metadata) return secret
def make_secret_spec(self): name = "dask-gateway-tls-%s" % self.cluster_name labels = self.get_labels_for("dask-gateway-tls") annotations = self.common_annotations secret = V1Secret( kind="Secret", api_version="v1", string_data={ "dask.crt": self.tls_cert.decode(), "dask.pem": self.tls_key.decode(), }, metadata=V1ObjectMeta(name=name, labels=labels, annotations=annotations), ) return secret
def test_manifest_comments_deserialization(): @with_comment class V1beta1CronJobWithComment(V1beta1CronJob): pass obj_with_comment = V1beta1CronJobWithComment(api_version='v999', kind='CronJob') obj_with_comment.set_comment([ "Lorem ipsum dolor sit amet,", "consectetur\n adipiscing\n elit", ]) obj_without_comment = V1Secret(api_version='v678', kind='Secret') # No stray newlines should be dumped if the comment is empty. obj_with_empty_comment = V1beta1CronJobWithComment(api_version='v1234', kind='CronJob') obj_with_empty_comment.set_comment([]) manifest = Manifest(pluginName='foo', manifestName='bar', data=[obj_with_comment, obj_without_comment, obj_with_empty_comment]) dump = manifest.dumps(None) assert dump == textwrap.dedent("""\ --- # Lorem ipsum dolor sit amet, # consectetur # adipiscing # elit apiVersion: v999 kind: CronJob --- apiVersion: v678 kind: Secret --- apiVersion: v1234 kind: CronJob """)
def create_manifest_list_cluster() -> ManifestList: clusterID = "test-1234-test-test" ml = ManifestList() metadata = V1ObjectMeta(name="dcos-{}".format(clusterID)) metadata.annotations = { "migration.dcos.d2iq.com/cluster-id": clusterID, "migration.dcos.d2iq.com/cluster-name": "testcluster", "migration.dcos.d2iq.com/backup-date": "2021-01-25", } cfgmap = V1ConfigMap(metadata=metadata) # models do not set defaults -.- cfgmap.kind = "ConfigMap" cfgmap.api_version = "v1" cfgmap.data = { "MESOS_MASTER_STATE_SUMMARY_BASE64": b64encode(json.dumps({ "foo": "bar" }).encode("ascii")) } manifest = Manifest(pluginName="cluster", manifestName="dcos-cluster") manifest.append(cfgmap) ml.append(manifest) secret = Manifest(pluginName="secret", manifestName="hello-world.secret") sec = V1Secret(metadata=V1ObjectMeta(name="hello-world.secret", annotations=metadata.annotations)) sec.api_version = 'v1' sec.kind = 'Secret' sec.data = {"hello-world.secret": "Zm9vYmFy"} secret.append(sec) ml.append(secret) return ml
def generate(self, user_id, access_token, refresh_token, user_project_id, project_id): self.info("GenIdentity U=%s, P=%s" % (user_id, user_project_id)) pki_role = "pki-backend-role-%s" % project_id jwt_role = "sae-issue-cert-%s" % project_id access_token, vault_token = self.vault_login(os.environ['VAULT_ADDR'], jwt_role, access_token, refresh_token, 1) url = "%s/v1/pki_int/issue/%s" % (os.environ['VAULT_ADDR'], pki_role) self.info("issue url %s" % url) payload = {'common_name': user_project_id} headers = {'X-Vault-Token': vault_token} x = requests.post(url, data=json.dumps(payload), headers=headers) self.info("status_code %s" % x.status_code) if x.status_code != 200: self.info("text %s" % x.text) raise Exception("Failed to issue certificate") j = x.json() self.info("issue_cert SERIAL NUMBER " + j['data']['serial_number']) dirpath = tempfile.mkdtemp() self.info("Building creds in %s" % dirpath) f = open("%s/crt" % dirpath, "w") f.write(j['data']['certificate']) f.close() f = open("%s/key" % dirpath, "w") f.write(j['data']['private_key']) f.close() os.system( "(cd %s; openssl pkcs12 -export -out private.pfx -inkey key -in crt -password pass:password)" % dirpath) os.system( 'mkdir %s/nssdb && certutil -d %s/nssdb -N --empty-password' % (dirpath, dirpath)) print("Created new DB") os.system( 'pk12util -v -d sql:%s/nssdb -K password -W password -i %s/private.pfx' % (dirpath, dirpath)) print("Added private key") #os.system('echo "password" > pass') os.system( 'certutil -A -n "ca-vaultpki-root" -t TC -i /cacerts/ca-vaultpki-root.crt -d sql:%s/nssdb' % dirpath) os.system( 'certutil -A -n "ca-vaultpki-inter" -t TC -i /cacerts/ca-vaultpki-inter.crt -d sql:%s/nssdb' % dirpath) os.system('certutil -L -d sql:%s/nssdb' % dirpath) os.system('ls -la %s/nssdb' % dirpath) secret_data = {} secret_data['refresh_token'] = base64.b64encode( refresh_token.encode('utf-8')).decode('utf-8') secret_data['postgresql.crt'] = base64.b64encode( j['data']['certificate'].encode('utf-8')).decode('utf-8') secret_data['postgresql.key'] = base64.b64encode( j['data']['private_key'].encode('utf-8')).decode('utf-8') for f in listdir('%s/nssdb' % dirpath): data = open("%s/nssdb/%s" % (dirpath, f), "rb").read() b64 = base64.b64encode(data) secret_data[f] = b64.decode('utf-8') self.info("Deleting temp folder %s" % dirpath) shutil.rmtree(dirpath) namespace = 'vdi' metadata = { 'name': "%s-cert" % user_project_id, 'namespace': namespace } secret = V1Secret('v1', secret_data, 'Secret', metadata, type='Opaque') print("Secret name = %s" % secret.metadata['name']) print(secret_data.keys()) return secret