mirror of
https://github.com/morten-olsen/homelab-nuclei-operator.git
synced 2026-02-08 02:16:23 +01:00
init
This commit is contained in:
36
api/v1alpha1/groupversion_info.go
Normal file
36
api/v1alpha1/groupversion_info.go
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package v1alpha1 contains API Schema definitions for the nuclei v1alpha1 API group.
|
||||
// +kubebuilder:object:generate=true
|
||||
// +groupName=nuclei.homelab.mortenolsen.pro
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
var (
|
||||
// GroupVersion is group version used to register these objects.
|
||||
GroupVersion = schema.GroupVersion{Group: "nuclei.homelab.mortenolsen.pro", Version: "v1alpha1"}
|
||||
|
||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
|
||||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
||||
|
||||
// AddToScheme adds the types in this group-version to the given scheme.
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
224
api/v1alpha1/nucleiscan_types.go
Normal file
224
api/v1alpha1/nucleiscan_types.go
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// SourceReference identifies the Ingress or VirtualService that triggered this scan
|
||||
type SourceReference struct {
|
||||
// APIVersion of the source resource
|
||||
// +kubebuilder:validation:Required
|
||||
APIVersion string `json:"apiVersion"`
|
||||
|
||||
// Kind of the source resource - Ingress or VirtualService
|
||||
// +kubebuilder:validation:Enum=Ingress;VirtualService
|
||||
Kind string `json:"kind"`
|
||||
|
||||
// Name of the source resource
|
||||
// +kubebuilder:validation:Required
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace of the source resource
|
||||
// +kubebuilder:validation:Required
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// UID of the source resource for owner reference
|
||||
// +kubebuilder:validation:Required
|
||||
UID string `json:"uid"`
|
||||
}
|
||||
|
||||
// NucleiScanSpec defines the desired state of NucleiScan
|
||||
type NucleiScanSpec struct {
|
||||
// SourceRef references the Ingress or VirtualService being scanned
|
||||
// +kubebuilder:validation:Required
|
||||
SourceRef SourceReference `json:"sourceRef"`
|
||||
|
||||
// Targets is the list of URLs to scan, extracted from the source resource
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:MinItems=1
|
||||
Targets []string `json:"targets"`
|
||||
|
||||
// Templates specifies which Nuclei templates to use
|
||||
// If empty, uses default templates
|
||||
// +optional
|
||||
Templates []string `json:"templates,omitempty"`
|
||||
|
||||
// Severity filters scan results by severity level
|
||||
// +kubebuilder:validation:Enum=info;low;medium;high;critical
|
||||
// +optional
|
||||
Severity []string `json:"severity,omitempty"`
|
||||
|
||||
// Schedule for periodic rescanning in cron format
|
||||
// If empty, scan runs once
|
||||
// +optional
|
||||
Schedule string `json:"schedule,omitempty"`
|
||||
|
||||
// Suspend prevents scheduled scans from running
|
||||
// +optional
|
||||
Suspend bool `json:"suspend,omitempty"`
|
||||
}
|
||||
|
||||
// ScanPhase represents the current phase of the scan
|
||||
// +kubebuilder:validation:Enum=Pending;Running;Completed;Failed
|
||||
type ScanPhase string
|
||||
|
||||
const (
|
||||
ScanPhasePending ScanPhase = "Pending"
|
||||
ScanPhaseRunning ScanPhase = "Running"
|
||||
ScanPhaseCompleted ScanPhase = "Completed"
|
||||
ScanPhaseFailed ScanPhase = "Failed"
|
||||
)
|
||||
|
||||
// Finding represents a single Nuclei scan finding
|
||||
type Finding struct {
|
||||
// TemplateID is the Nuclei template identifier
|
||||
TemplateID string `json:"templateId"`
|
||||
|
||||
// TemplateName is the human-readable template name
|
||||
// +optional
|
||||
TemplateName string `json:"templateName,omitempty"`
|
||||
|
||||
// Severity of the finding
|
||||
Severity string `json:"severity"`
|
||||
|
||||
// Type of the finding - http, dns, ssl, etc.
|
||||
// +optional
|
||||
Type string `json:"type,omitempty"`
|
||||
|
||||
// Host that was scanned
|
||||
Host string `json:"host"`
|
||||
|
||||
// MatchedAt is the specific URL or endpoint where the issue was found
|
||||
// +optional
|
||||
MatchedAt string `json:"matchedAt,omitempty"`
|
||||
|
||||
// ExtractedResults contains any data extracted by the template
|
||||
// +optional
|
||||
ExtractedResults []string `json:"extractedResults,omitempty"`
|
||||
|
||||
// Description provides details about the finding
|
||||
// +optional
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
// Reference contains URLs to additional information about the finding
|
||||
// +optional
|
||||
Reference []string `json:"reference,omitempty"`
|
||||
|
||||
// Tags associated with the finding
|
||||
// +optional
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
|
||||
// Timestamp when the finding was discovered
|
||||
Timestamp metav1.Time `json:"timestamp"`
|
||||
|
||||
// Metadata contains additional template metadata
|
||||
// +kubebuilder:pruning:PreserveUnknownFields
|
||||
// +optional
|
||||
Metadata *runtime.RawExtension `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// ScanSummary provides aggregated statistics about the scan
|
||||
type ScanSummary struct {
|
||||
// TotalFindings is the total number of findings
|
||||
TotalFindings int `json:"totalFindings"`
|
||||
|
||||
// FindingsBySeverity breaks down findings by severity level
|
||||
// +optional
|
||||
FindingsBySeverity map[string]int `json:"findingsBySeverity,omitempty"`
|
||||
|
||||
// TargetsScanned is the number of targets that were scanned
|
||||
TargetsScanned int `json:"targetsScanned"`
|
||||
|
||||
// DurationSeconds is the duration of the scan in seconds
|
||||
// +optional
|
||||
DurationSeconds int64 `json:"durationSeconds,omitempty"`
|
||||
}
|
||||
|
||||
// NucleiScanStatus defines the observed state of NucleiScan
|
||||
type NucleiScanStatus struct {
|
||||
// Phase represents the current scan phase
|
||||
// +optional
|
||||
Phase ScanPhase `json:"phase,omitempty"`
|
||||
|
||||
// Conditions represent the latest available observations
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
// +optional
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
|
||||
// LastScanTime is when the last scan was initiated
|
||||
// +optional
|
||||
LastScanTime *metav1.Time `json:"lastScanTime,omitempty"`
|
||||
|
||||
// CompletionTime is when the last scan completed
|
||||
// +optional
|
||||
CompletionTime *metav1.Time `json:"completionTime,omitempty"`
|
||||
|
||||
// NextScheduledTime is when the next scheduled scan will run
|
||||
// +optional
|
||||
NextScheduledTime *metav1.Time `json:"nextScheduledTime,omitempty"`
|
||||
|
||||
// Summary provides aggregated scan statistics
|
||||
// +optional
|
||||
Summary *ScanSummary `json:"summary,omitempty"`
|
||||
|
||||
// Findings contains the array of scan results from Nuclei JSONL output
|
||||
// Each element is a parsed JSON object from Nuclei output
|
||||
// +optional
|
||||
Findings []Finding `json:"findings,omitempty"`
|
||||
|
||||
// LastError contains the error message if the scan failed
|
||||
// +optional
|
||||
LastError string `json:"lastError,omitempty"`
|
||||
|
||||
// ObservedGeneration is the generation observed by the controller
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:resource:shortName=ns;nscan
|
||||
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
|
||||
// +kubebuilder:printcolumn:name="Findings",type=integer,JSONPath=`.status.summary.totalFindings`
|
||||
// +kubebuilder:printcolumn:name="Source",type=string,JSONPath=`.spec.sourceRef.kind`
|
||||
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
|
||||
|
||||
// NucleiScan is the Schema for the nucleiscans API
|
||||
type NucleiScan struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec NucleiScanSpec `json:"spec,omitempty"`
|
||||
Status NucleiScanStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// NucleiScanList contains a list of NucleiScan
|
||||
type NucleiScanList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []NucleiScan `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&NucleiScan{}, &NucleiScanList{})
|
||||
}
|
||||
235
api/v1alpha1/zz_generated.deepcopy.go
Normal file
235
api/v1alpha1/zz_generated.deepcopy.go
Normal file
@@ -0,0 +1,235 @@
|
||||
//go:build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Finding) DeepCopyInto(out *Finding) {
|
||||
*out = *in
|
||||
if in.ExtractedResults != nil {
|
||||
in, out := &in.ExtractedResults, &out.ExtractedResults
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Reference != nil {
|
||||
in, out := &in.Reference, &out.Reference
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Tags != nil {
|
||||
in, out := &in.Tags, &out.Tags
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
in.Timestamp.DeepCopyInto(&out.Timestamp)
|
||||
if in.Metadata != nil {
|
||||
in, out := &in.Metadata, &out.Metadata
|
||||
*out = new(runtime.RawExtension)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Finding.
|
||||
func (in *Finding) DeepCopy() *Finding {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Finding)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NucleiScan) DeepCopyInto(out *NucleiScan) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NucleiScan.
|
||||
func (in *NucleiScan) DeepCopy() *NucleiScan {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NucleiScan)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *NucleiScan) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NucleiScanList) DeepCopyInto(out *NucleiScanList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]NucleiScan, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NucleiScanList.
|
||||
func (in *NucleiScanList) DeepCopy() *NucleiScanList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NucleiScanList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *NucleiScanList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NucleiScanSpec) DeepCopyInto(out *NucleiScanSpec) {
|
||||
*out = *in
|
||||
out.SourceRef = in.SourceRef
|
||||
if in.Targets != nil {
|
||||
in, out := &in.Targets, &out.Targets
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Templates != nil {
|
||||
in, out := &in.Templates, &out.Templates
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Severity != nil {
|
||||
in, out := &in.Severity, &out.Severity
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NucleiScanSpec.
|
||||
func (in *NucleiScanSpec) DeepCopy() *NucleiScanSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NucleiScanSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NucleiScanStatus) DeepCopyInto(out *NucleiScanStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]v1.Condition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.LastScanTime != nil {
|
||||
in, out := &in.LastScanTime, &out.LastScanTime
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
if in.CompletionTime != nil {
|
||||
in, out := &in.CompletionTime, &out.CompletionTime
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
if in.NextScheduledTime != nil {
|
||||
in, out := &in.NextScheduledTime, &out.NextScheduledTime
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
if in.Summary != nil {
|
||||
in, out := &in.Summary, &out.Summary
|
||||
*out = new(ScanSummary)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Findings != nil {
|
||||
in, out := &in.Findings, &out.Findings
|
||||
*out = make([]Finding, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NucleiScanStatus.
|
||||
func (in *NucleiScanStatus) DeepCopy() *NucleiScanStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NucleiScanStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ScanSummary) DeepCopyInto(out *ScanSummary) {
|
||||
*out = *in
|
||||
if in.FindingsBySeverity != nil {
|
||||
in, out := &in.FindingsBySeverity, &out.FindingsBySeverity
|
||||
*out = make(map[string]int, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ScanSummary.
|
||||
func (in *ScanSummary) DeepCopy() *ScanSummary {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ScanSummary)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SourceReference) DeepCopyInto(out *SourceReference) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceReference.
|
||||
func (in *SourceReference) DeepCopy() *SourceReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SourceReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user