diff --git a/pom.xml b/pom.xml index 1434c68..026f6a3 100644 --- a/pom.xml +++ b/pom.xml @@ -74,19 +74,15 @@ io.quarkus quarkus-smallrye-openapi - info.picocli picocli 4.7.4 + + io.quarkus + quarkus-vertx + io.quarkus quarkus-junit5 diff --git a/src/main/java/com/redhat/pctsec/model/RequestType.java b/src/main/java/com/redhat/pctsec/model/RequestType.java new file mode 100644 index 0000000..a589a78 --- /dev/null +++ b/src/main/java/com/redhat/pctsec/model/RequestType.java @@ -0,0 +1,3 @@ +package com.redhat.pctsec.model; + +public enum RequestType {BREW, PNC, GIT} diff --git a/src/main/java/com/redhat/pctsec/model/ScanRequest.java b/src/main/java/com/redhat/pctsec/model/ScanRequest.java index 16533db..04da19a 100644 --- a/src/main/java/com/redhat/pctsec/model/ScanRequest.java +++ b/src/main/java/com/redhat/pctsec/model/ScanRequest.java @@ -12,14 +12,11 @@ import jakarta.persistence.*; import java.util.HashMap; import java.util.UUID; -enum RequestType{BREW, PNC, GIT} @ApplicationScoped @Entity public class ScanRequest { - @Transient - @Inject - EventBus bus; + @Id @GeneratedValue @@ -27,8 +24,23 @@ public class ScanRequest { private String metadata; private String oshScanOptions; + public EventBus getBus() { + return bus; + } + + public void setBus(EventBus bus) { + this.bus = bus; + } + + @Transient + @Inject + EventBus bus; + + public RequestType getType() { + return type; + } - RequestType type; + private RequestType type; @OneToOne @JoinColumn(name = "brew_build_id", referencedColumnName = "id") @@ -65,8 +77,10 @@ public class ScanRequest { { this.git = new Git(repo, ref); } - public void executeScan(){ - //Drop self on event bus for tekton handler + public ScanTask executeScan(){ + ScanTask st = new ScanTask(this); + st.execute(); + return st; } } diff --git a/src/main/java/com/redhat/pctsec/model/ScanRequests.java b/src/main/java/com/redhat/pctsec/model/ScanRequests.java index df8ad3e..4d52f3d 100644 --- a/src/main/java/com/redhat/pctsec/model/ScanRequests.java +++ b/src/main/java/com/redhat/pctsec/model/ScanRequests.java @@ -2,9 +2,11 @@ package com.redhat.pctsec.model; import com.redhat.pctsec.model.api.request.pssaas; import com.redhat.pctsec.model.api.request.scanChain; +import io.vertx.mutiny.core.eventbus.EventBus; import jakarta.enterprise.context.ApplicationScoped; import java.util.*; +import java.util.stream.Collectors; import jakarta.persistence.*; @@ -71,10 +73,14 @@ public class ScanRequests { } //Create tekton pipeline/taskrun - public void execute(){ + public List execute(EventBus eventBus){ + scanRequests.stream().forEach(s -> s.setBus(eventBus)); + return scanRequests.stream().map(s -> s.executeScan()).collect(Collectors.toList()); + /* for(ScanRequest s : scanRequests){ s.executeScan(); } + */ } public Set getScanRequests() { diff --git a/src/main/java/com/redhat/pctsec/model/ScanResult.java b/src/main/java/com/redhat/pctsec/model/ScanResult.java new file mode 100644 index 0000000..64c56ba --- /dev/null +++ b/src/main/java/com/redhat/pctsec/model/ScanResult.java @@ -0,0 +1,19 @@ +package com.redhat.pctsec.model; + +import java.net.URI; +import java.net.URL; + +public class ScanResult { + + public URL covScanTask; + + + //Store files in document store + private void storeResults(){ + + } + + private void fetchResults(){ + + } +} diff --git a/src/main/java/com/redhat/pctsec/model/ScanTask.java b/src/main/java/com/redhat/pctsec/model/ScanTask.java new file mode 100644 index 0000000..3c1cddb --- /dev/null +++ b/src/main/java/com/redhat/pctsec/model/ScanTask.java @@ -0,0 +1,64 @@ +package com.redhat.pctsec.model; + + +import io.vertx.mutiny.core.eventbus.EventBus; +import jakarta.enterprise.context.Dependent; +import jakarta.inject.Inject; + +//@ApplicationScoped +@Dependent +public class ScanTask { + + + @Inject + EventBus bus; + public ScanTaskState state; + + public void setTektonRunId(String tektonRunId) { + this.tektonRunId = tektonRunId; + } + + public String tektonRunId; + + public ScanRequest scanRequest; + + + public ScanTask(ScanRequest scanRequest) { + this(); + this.scanRequest = scanRequest; + this.bus = scanRequest.getBus(); + } + + + public ScanTask(){ + } + + + /* + public ScanTask(ScanRequest scanRequest) + { + this( + this.scanRequest = scanRequest; + } + + */ + public void execute(){ + bus.publish("tekton", this); + } + + public ScanTaskState getState() { + return state; + } + + public void setState(ScanTaskState state) { + this.state = state; + } + + public ScanRequest getScanRequest() { + return scanRequest; + } + + public void setScanRequest(ScanRequest scanRequest) { + this.scanRequest = scanRequest; + } +} diff --git a/src/main/java/com/redhat/pctsec/model/ScanTaskState.java b/src/main/java/com/redhat/pctsec/model/ScanTaskState.java new file mode 100644 index 0000000..6f4fa1a --- /dev/null +++ b/src/main/java/com/redhat/pctsec/model/ScanTaskState.java @@ -0,0 +1,3 @@ +package com.redhat.pctsec.model; + +public enum ScanTaskState {AWAIT, TRIGGERED, RUNNING, SUCCESS, FAULURE} diff --git a/src/main/java/com/redhat/pctsec/rest/v1alpha1/ScanResource.java b/src/main/java/com/redhat/pctsec/rest/v1alpha1/ScanResource.java index 4889806..f160b18 100644 --- a/src/main/java/com/redhat/pctsec/rest/v1alpha1/ScanResource.java +++ b/src/main/java/com/redhat/pctsec/rest/v1alpha1/ScanResource.java @@ -1,11 +1,9 @@ package com.redhat.pctsec.rest.v1alpha1; -import com.redhat.pctsec.model.Git; -import com.redhat.pctsec.model.Scan; -import com.redhat.pctsec.model.ScanRequest; -import com.redhat.pctsec.model.ScanRequests; +import com.redhat.pctsec.model.*; import com.redhat.pctsec.model.api.request.pssaas; import com.redhat.pctsec.model.jpa.ScanRepository; +import io.vertx.mutiny.core.eventbus.EventBus; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; @@ -25,6 +23,9 @@ public class ScanResource { @Inject ScanRepository sr; + @Inject + EventBus bus; + @POST @Path("PSSaaS") @Consumes({ "application/json" }) @@ -55,8 +56,11 @@ public class ScanResource { @GET @Path("{id}/run") - public String scanRequestExe(String id){ - return "We'd normally have a json payload here, with pipeline UID"; + public List scanRequestExe(String id) + { + Scan s = sr.findById(UUID.fromString(id)); + return s.scanRequests.execute(bus); + //return "We'd normally have a json payload here, with pipeline UID"; } diff --git a/src/main/java/com/redhat/pctsec/tekton/TaskHandler.java b/src/main/java/com/redhat/pctsec/tekton/TaskHandler.java new file mode 100644 index 0000000..b76dbe3 --- /dev/null +++ b/src/main/java/com/redhat/pctsec/tekton/TaskHandler.java @@ -0,0 +1,136 @@ +package com.redhat.pctsec.tekton; +import com.redhat.pctsec.model.RequestType; +import com.redhat.pctsec.model.ScanTask; +import com.redhat.pctsec.model.ScanTaskState; +import io.fabric8.kubernetes.api.model.ConfigMapVolumeSource; +import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSource; +import io.fabric8.kubernetes.api.model.PodSecurityContext; +import io.fabric8.kubernetes.api.model.PodSecurityContextBuilder; +import io.fabric8.tekton.client.DefaultTektonClient; +import io.fabric8.tekton.client.TektonClient; +import io.fabric8.tekton.client.DefaultTektonClient; +import io.fabric8.tekton.client.TektonClient; +import io.fabric8.tekton.pipeline.v1beta1.*; + +import io.quarkus.vertx.ConsumeEvent; + +import jakarta.inject.Inject; + +import org.apache.commons.lang3.RandomStringUtils; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import java.util.ArrayList; +import java.util.List; + +public class TaskHandler { + + @ConfigProperty(name = "quarkus.openshift.namespace") + String NAMESPACE; + @ConfigProperty(name = "tekton.pipeline.ref") + String PIPELINE_REFERENCE; + @ConfigProperty(name = "tekton.service-account") + String SERVICE_ACCOUNT; + + @ConfigProperty(name = "tekton.task.ref") + String TASK_REFERENCE; + + @Inject TektonClient tektonClient; + + @ConsumeEvent("tekton") + private ScanTask consume(ScanTask scanTask) + { + + switch(scanTask.getScanRequest().getType()) + { + case BREW: + scanTask.setTektonRunId(invokeScanTask(scanTask.getScanRequest().brewBuild.buildRef)); + scanTask.setState(ScanTaskState.RUNNING); + break; + + case PNC: + String repo = scanTask.getScanRequest().pncBuild.SCMURL().toString(); + String ref = scanTask.getScanRequest().pncBuild.revision(); + scanTask.setTektonRunId(invokeOshScmScanPipeline(repo, ref)); + scanTask.setState(ScanTaskState.RUNNING); + break; + + case GIT: + scanTask.setTektonRunId(invokeOshScmScanPipeline(scanTask.getScanRequest().git.repo.toString(), scanTask.getScanRequest().git.ref)); + scanTask.setState(ScanTaskState.RUNNING); + break; + } + + return scanTask; + } + + public String invokeScanTask(String buildId) { + // String buildId = "xterm-366-8.el9"; + String scanProfile = "snyk-only-unstable"; + + // random taskrun name generating for now + TaskRun taskRun = new TaskRunBuilder().withNewMetadata().withName("osh-scan-taskrun-" + RandomStringUtils.randomAlphanumeric(8).toLowerCase()) + .endMetadata() + .withNewSpec() + .withServiceAccountName(SERVICE_ACCOUNT) + .withNewTaskRef() + .withName(TASK_REFERENCE) + .endTaskRef() + .withParams( + new Param("buildId", new ArrayOrString(buildId)), + new Param("scanProfile", new ArrayOrString(scanProfile))) + .endSpec() + .build(); + + tektonClient.v1beta1().taskRuns().inNamespace(NAMESPACE).resource(taskRun).create(); + + return taskRun.getMetadata().getName(); + } + + public String invokeOshScmScanPipeline(String repo, String ref) { + + PodSecurityContext securityContext = new PodSecurityContextBuilder() + .withRunAsNonRoot(true) + .withRunAsUser(65532L) + .build(); + + WorkspaceBinding sourcesWorkspaceBinding = new WorkspaceBindingBuilder() + .withName("sources") + .withPersistentVolumeClaim(new PersistentVolumeClaimVolumeSource("osh-client-sources", null)) + .build(); + + WorkspaceBinding sourceTarsWorkspaceBinding = new WorkspaceBindingBuilder() + .withName("source-tars") + .withPersistentVolumeClaim(new PersistentVolumeClaimVolumeSource("osh-client-source-tars", null)) + .build(); + + WorkspaceBinding sslCaDirectoryWorkspaceBinding = new WorkspaceBindingBuilder() + .withName("ssl-ca-directory") + .withConfigMap(new ConfigMapVolumeSource(null, null, "config-trusted-cabundle", null)) + .build(); + + List workspaceBindings = new ArrayList<>(); + workspaceBindings.add(sourcesWorkspaceBinding); + workspaceBindings.add(sourceTarsWorkspaceBinding); + workspaceBindings.add(sslCaDirectoryWorkspaceBinding); + + PipelineRun pipelineRun = new PipelineRunBuilder() + .withNewMetadata().withName("osh-scm-scan-" + RandomStringUtils.randomAlphanumeric(8).toLowerCase()).endMetadata() + .withNewSpec() + .withNewPodTemplate() + .withSecurityContext(securityContext) + .endPodTemplate() + .withServiceAccountName(SERVICE_ACCOUNT) + .withNewPipelineRef().withName(PIPELINE_REFERENCE).endPipelineRef() + .addNewParam().withName("repo-url").withNewValue(repo).endParam() + .addNewParam().withName("revision").withNewValue(ref).endParam() + .withWorkspaces(workspaceBindings) + .endSpec() + .build(); + + tektonClient.v1beta1().pipelineRuns().inNamespace(NAMESPACE).resource(pipelineRun).create(); + + return pipelineRun.getMetadata().getName(); + } + + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1115fea..23de497 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -29,6 +29,8 @@ %dev.quarkus.hibernate-orm.database.generation=drop-and-create #Always provide swagger ui %dev.quarkus.swagger-ui.always-include=true +%dev.quarkus.openshift.service-account=osh-wrapper-client-sa +%dev.quarkus.openshift.namespace=pct-security-tooling %stage.quarkus.openshift.name=osh %stage.quarkus.openshift.service-account=osh-wrapper-client-sa @@ -42,6 +44,7 @@ quarkus.arc.remove-unused-beans=false %stage.quarkus.openshift.route.expose=false %stage.quarkus.openshift.route.target-port=https %stage.quarkus.openshift.route.tls.insecure-edge-termination-policy=redirect +%stage.quarkus.openshift.namespace=pct-security-tooling ########################################## # Kerberos Specifics # @@ -58,5 +61,9 @@ quarkus.arc.remove-unused-beans=false %stage.quarkus.openshift.config-map-volumes.osh-wrapper-config-vol.items."linux-krb5.conf".path=linux-krb5.conf %stage.quarkus.openshift.mounts.osh-wrapper-config-vol.read-only=true +tekton.pipeline.ref=osh-client-from-source +tekton.task.ref=osh-scan-task +tekton.service-account=${%stage.quarkus.openshift.service-account} +