diff --git a/security/step-certificates/Makefile b/security/step-certificates/Makefile new file mode 100644 index 0000000000..b1948dadfb --- /dev/null +++ b/security/step-certificates/Makefile @@ -0,0 +1,10 @@ +PLUGIN_NAME= step-certificates +PLUGIN_VERSION= 1.0 +PLUGIN_COMMENT= StepCA Certificate Authority for OPNSense +PLUGIN_DEPENDS= security/step-certificates \ + security/step-kms \ + devel/pcsc-lite + # security/py-yubikey-manager +PLUGIN_MAINTAINER= step-opn@papro.ca + +.include "../../Mk/plugins.mk" diff --git a/security/step-certificates/pkg-descr b/security/step-certificates/pkg-descr new file mode 100644 index 0000000000..53e42911bf --- /dev/null +++ b/security/step-certificates/pkg-descr @@ -0,0 +1,8 @@ +StepCA Certificates for OPNSense + +Plugin Changelog +================ + +1.0 + +* Initial release diff --git a/security/step-certificates/src/etc/inc/plugins.inc.d/stepca.inc b/security/step-certificates/src/etc/inc/plugins.inc.d/stepca.inc new file mode 100644 index 0000000000..1852c73057 --- /dev/null +++ b/security/step-certificates/src/etc/inc/plugins.inc.d/stepca.inc @@ -0,0 +1,70 @@ + gettext('StepCA'), + 'pidfile' => '/var/run/step_ca.pid', + 'configd' => array( + 'restart' => array('stepca restart'), + 'start' => array('stepca start'), + 'stop' => array('stepca stop'), + ), + 'name' => 'stepca', + ); + + return $services; +} + +function stepca_xmlrpc_sync() +{ + $result = array(); + $result['id'] = 'stepca'; + $result['section'] = 'OPNsense.stepca'; + $result['description'] = gettext('StepCA'); + return array($result); +} diff --git a/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/Api/InitializeController.php b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/Api/InitializeController.php new file mode 100644 index 0000000000..3b3472f1f9 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/Api/InitializeController.php @@ -0,0 +1,43 @@ +request->isPost()) { + $bckresult = json_decode(trim((new Backend())->configdRun("stepca initca")), true); + if ($bckresult !== null) { + // only return valid json type responses + return $bckresult; + } + } + return ["message" => "unable to run config action"]; + } + + // Copy of original reconfigure, + // added failure detection + public function reconfigureAction() + { + if (true || $this->request->isPost()) { + $this->sessionClose(); + + $backend = new Backend(); + + if (!$this->serviceEnabled() || $this->reconfigureForceRestart()) { + $backend->configdRun(escapeshellarg(static::$internalServiceName) . ' stop'); + } + + if ($this->invokeInterfaceRegistration()) { + $backend->configdRun('interface invoke registration'); + } + + if (!empty(static::$internalServiceTemplate)) { + $result = trim($backend->configdpRun('template reload', [static::$internalServiceTemplate]) ?? ''); + if ($result !== 'OK') { + throw new UserException(sprintf( + gettext('Template generation failed for internal service "%s". See backend log for details.'), + static::$internalServiceName + ), gettext('Configuration exception')); + } + } + + $status = 'ok'; + if ($this->serviceEnabled()) { + $runStatus = $this->statusAction(); + if ($runStatus['status'] != 'running') { + $response = $backend->configdRun(escapeshellarg(static::$internalServiceName) . ' start'); + } else { + $response = $backend->configdRun(escapeshellarg(static::$internalServiceName) . ' reload'); + } + if (trim($response) !== 'OK') { + $status = 'failed'; + } + } + + return array('status' => $status); + } else { + return array('status' => 'failed'); + } + } +} diff --git a/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/Api/SettingsController.php b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/Api/SettingsController.php new file mode 100644 index 0000000000..8028c28d5f --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/Api/SettingsController.php @@ -0,0 +1,73 @@ +searchBase("provisioners.provisioner", array('Enabled', 'Name', 'Provisioner'), "Name"); + } + + public function setItemAction($uuid) + { + return $this->setBase("provisioner", "provisioners.provisioner", $uuid); + } + + public function addItemAction() + { + return $this->addBase("provisioner", "provisioners.provisioner"); + } + + public function getItemAction($uuid = null) + { + return $this->getBase("provisioner", "provisioners.provisioner", $uuid); + } + + public function delItemAction($uuid) + { + return $this->delBase("provisioners.provisioner", $uuid); + } + + public function toggleItemAction($uuid, $enabled = null) + { + return $this->toggleBase("provisioners.provisioner", $uuid, $enabled); + } +} diff --git a/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/GeneralController.php b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/GeneralController.php new file mode 100644 index 0000000000..5b65fe297a --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/GeneralController.php @@ -0,0 +1,46 @@ +view->pick('OPNsense/StepCA/general'); + // fetch form data "general" in + $this->view->generalForm = $this->getForm("general"); + } +} diff --git a/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/InitializeController.php b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/InitializeController.php new file mode 100644 index 0000000000..e21b82c7b0 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/InitializeController.php @@ -0,0 +1,81 @@ +view->pick('OPNsense/StepCA/initialize'); + $this->view->initializeForm = $this->getForm("initialize"); + $this->view->exampleTemplate = exampleTemplate; + $this->view->fullTemplate = fullTemplate; + } +} diff --git a/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/ProvisionersController.php b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/ProvisionersController.php new file mode 100644 index 0000000000..4907ba7a51 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/ProvisionersController.php @@ -0,0 +1,46 @@ +view->pick('OPNsense/StepCA/provisioners'); + // fetch form data "general" in + $this->view->formDialogAddress = $this->getForm("provisioner"); + } +} diff --git a/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/forms/general.xml b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/forms/general.xml new file mode 100644 index 0000000000..d5dc4796fc --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/forms/general.xml @@ -0,0 +1,201 @@ +
+ + CA.Enabled + + checkbox + Enable the StepCA service. + + + CA.Address + + text + IP:Port + + + + info + + + CA.DNSNames + + select_multiple + + true + IP:Port + + + + + header + true + + + CA.authority.policy.x509.allow.DNSNames + + select_multiple + + true + A dns rule for www.example.com, only matching the domain www.example.com. A dns rule for *.internal.example.com, matching all subdomains of internal.example.com. + + + CA.authority.policy.x509.allow.IPNames + + select_multiple + + true + An ip rule for the 192.168.0.0/24 CIDR, matching all IPs in the range from 192.168.0.0-192.168.0.255 + + + CA.authority.policy.x509.deny.DNSNames + + select_multiple + + true + A dns rule for www.example.com, only matching the domain www.example.com. A dns rule for *.internal.example.com, matching all subdomains of internal.example.com. + + + CA.authority.policy.x509.deny.IPNames + + select_multiple + + true + An ip rule for the 192.168.0.0/24 CIDR, matching all IPs in the range from 192.168.0.0-192.168.0.255 + + + + + header + true + + + CA.authority.policy.ssh.host.allow.DNSNames + + select_multiple + + true + A dns rule for www.example.com, only matching the domain www.example.com. A dns rule for *.internal.example.com, matching all subdomains of internal.example.com. + + + CA.authority.policy.ssh.host.allow.IPNames + + select_multiple + + true + An ip rule for the 192.168.0.0/24 CIDR, matching all IPs in the range from 192.168.0.0-192.168.0.255 + + + CA.authority.policy.ssh.host.deny.DNSNames + + select_multiple + + true + A dns rule for www.example.com, only matching the domain www.example.com. A dns rule for *.internal.example.com, matching all subdomains of internal.example.com. + + + CA.authority.policy.ssh.host.deny.IPNames + + select_multiple + + true + An ip rule for the 192.168.0.0/24 CIDR, matching all IPs in the range from 192.168.0.0-192.168.0.255 + + + + + + header + true + + + CA.authority.claims.minTLSCertDuration + + text + do not allow certificates with a duration less than this value. + + + CA.authority.claims.maxTLSCertDuration + + text + do not allow certificates with a duration greater than this value. + + + CA.authority.claims.defaultTLSCertDuration + + text + if no certificate validity period is specified, use this value. + + + CA.authority.claims.disableRenewal + + checkbox + do not allow any certificates to be renewed. The default is false. + + + CA.authority.claims.allowRenewalAfterExpiry + + checkbox + ☠️ allow expired certificates to be renewed. The default is false. This option adds security risk; proceed with caution and consider alternatives. + + + CA.authority.claims.minUserSSHCertDuration + + text + do not allow certificates with a duration less than this value. + + + CA.authority.claims.maxUserSSHCertDuration + + text + do not allow certificates with a duration greater than this value. + + + CA.authority.claims.defaultUserSSHCertDuration + + text + if no certificate validity period is specified, use this value. + + + CA.authority.claims.minHostSSHCertDuration + + text + do not allow certificates with a duration less than this value. + + + CA.authority.claims.maxHostSSHCertDuration + + text + do not allow certificates with a duration greater than this value. + + + CA.authority.claims.defaultHostSSHCertDuration + + text + if no certificate validity period is specified, use this value. + + + CA.authority.claims.enableSSHCA + + checkbox + enable this provisioner to generate SSH Certificates. The default value is false. + +
diff --git a/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/forms/initialize.xml b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/forms/initialize.xml new file mode 100644 index 0000000000..3d68b309c2 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/forms/initialize.xml @@ -0,0 +1,91 @@ +
+ + Initialize.root.Source + + dropdown + + Select existing CA from Trust. Or (create and) load existing CA from KMS into Trust + + + Initialize.root.TrustCertificate + + dropdown + + Root Certificate. Private key must be available if intermediate is being created + + + Initialize.root.CreateKeyType + + dropdown + + What keytype to create + + + Initialize.root.Lifetime + + text + + + + Initialize.root.YubikeySlot + + text + + Slot Storing CA Root certificate and key + + + Initialize.root.CreateTemplate + + textbox + + Template for CA Root certificate. See https://smallstep.com/docs/step-ca/templates + + + Initialize.intermediate.Source + + dropdown + + Load (pre-created) or Generate new Intermediate CA key and certificate + + + Initialize.intermediate.TrustCertificate + + dropdown + + Intermediate Certificates with private keys + + + Initialize.intermediate.CreateKeyType + + dropdown + + What keytype to create + + + Initialize.intermediate.Lifetime + + text + + + + Initialize.intermediate.YubikeySlot + + text + + Slot Storing CA Intermediate certificate and key + + + Initialize.intermediate.CreateTemplate + + textbox + + Template for CA Intermediate certificate. See https://smallstep.com/docs/step-ca/templates + + + Initialize.yubikey.Pin + + text + + Yubikey pin + +
diff --git a/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/forms/provisioner.xml b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/forms/provisioner.xml new file mode 100644 index 0000000000..a77c4852c9 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/controllers/OPNsense/StepCA/forms/provisioner.xml @@ -0,0 +1,131 @@ +
+ + provisioner.Enabled + + checkbox + + + provisioner.Provisioner + + dropdown + Provisioner Type + + + provisioner.Name + + text + A string used to identify the provider + + + provisioner.ForceCN + + checkbox + + Force one of the SANs to become the Common Name, if a common name is not provided. + + + provisioner.Challenges + + select_multiple + + Which ACME challenge types are allowed + + + provisioner.AttestationRoots + + dropdown + + Root Certificate. Private key must be available if intermediate is being created + + + provisioner.CreateTemplate + + textbox + + Template for CA issued (leaf) certificate. See https://smallstep.com/docs/step-ca/templates + + + provisioner.claims.minTLSCertDuration + + text + + do not allow certificates with a duration less than this value. + + + provisioner.claims.maxTLSCertDuration + + text + + do not allow certificates with a duration greater than this value. + + + provisioner.claims.defaultTLSCertDuration + + text + + if no certificate validity period is specified, use this value. + + + provisioner.claims.disableRenewal + + checkbox + + do not allow any certificates to be renewed. The default is false. + + + provisioner.claims.allowRenewalAfterExpiry + + checkbox + + ☠️ allow expired certificates to be renewed. The default is false. This option adds security risk; proceed with caution and consider alternatives. + + + provisioner.claims.minUserSSHCertDuration + + text + + do not allow certificates with a duration less than this value. + + + provisioner.claims.maxUserSSHCertDuration + + text + + do not allow certificates with a duration greater than this value. + + + provisioner.claims.defaultUserSSHCertDuration + + text + + if no certificate validity period is specified, use this value. + + + provisioner.claims.minHostSSHCertDuration + + text + + do not allow certificates with a duration less than this value. + + + provisioner.claims.maxHostSSHCertDuration + + text + + do not allow certificates with a duration greater than this value. + + + provisioner.claims.defaultHostSSHCertDuration + + text + + if no certificate validity period is specified, use this value. + + + provisioner.claims.enableSSHCA + + checkbox + + enable this provisioner to generate SSH Certificates. The default value is false. + +
diff --git a/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/Base/Constraints/SetIfConstraintAbs.php b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/Base/Constraints/SetIfConstraintAbs.php new file mode 100644 index 0000000000..b36e4dcff0 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/Base/Constraints/SetIfConstraintAbs.php @@ -0,0 +1,72 @@ +getOption('node'); + $field_name = $this->getOption('field'); + $check = $this->getOption('check'); + if ($node) { + $model = $node->getParentModel(); + $refNode = $model->getNodeByReference($field_name); + if ($this->isEmpty($node) && $refNode == $check) { + $this->appendMessage($validator, $attribute); + } + } + return true; + } +} + + diff --git a/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/ACL/ACL.xml b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/ACL/ACL.xml new file mode 100644 index 0000000000..75b0f28ce5 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/ACL/ACL.xml @@ -0,0 +1,11 @@ + + + Services: StepCA + + ui/stepca/* + api/stepca/* + ui/diagnostics/log/core/stepca/* + api/diagnostics/log/core/stepca/* + + + diff --git a/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/FieldTypes/CertificateField.php b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/FieldTypes/CertificateField.php new file mode 100644 index 0000000000..87c15e6b05 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/FieldTypes/CertificateField.php @@ -0,0 +1,122 @@ +certificateType = "ca"; + } elseif (trim(strtolower($value)) == "crl") { + $this->certificateType = "crl"; + } else { + $this->certificateType = "cert"; + } + } + + /** + * Whether Certificate must have a private key + * @param string $value Y/N + */ + public function setPrivKeyRequired($value) + { + $this->internalPrivKeyRequired = trim(strtoupper($value)) == "Y"; + } + + /** + * generate validation data (list of certificates) + */ + protected function actionPostLoadingEvent() + { + if ($this->internalPrivKeyRequired) { + $configObj = Config::getInstance()->object(); + foreach ($configObj->{$this->certificateType} as $cert) { + if ($this->certificateType == 'ca' && (string)$cert->x509_extensions == 'ocsp') { + // skip ocsp signing certs + continue; + } + if (empty($cert->prv)) { + // skip cert without private key + continue; + } + $this->internalOptionList[(string)$cert->refid] = (string)$cert->descr; + } + natcasesort($this->internalOptionList); + return; + } + + if (!isset(self::$internalStaticOptionList[$this->certificateType])) { + self::$internalStaticOptionList[$this->certificateType] = array(); + $configObj = Config::getInstance()->object(); + foreach ($configObj->{$this->certificateType} as $cert) { + if ($this->certificateType == 'ca' && (string)$cert->x509_extensions == 'ocsp') { + // skip ocsp signing certs + continue; + } + // if ($this->internalPrivKeyRequired && empty($cert->prv)) { + // // skip cert without private key + // continue; + // } + self::$internalStaticOptionList[$this->certificateType][(string)$cert->refid] = (string)$cert->descr; + } + natcasesort(self::$internalStaticOptionList[$this->certificateType]); + } + $this->internalOptionList = self::$internalStaticOptionList[$this->certificateType]; + } +} diff --git a/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/Initialize.php b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/Initialize.php new file mode 100644 index 0000000000..49cf66d92c --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/Initialize.php @@ -0,0 +1,37 @@ + + //OPNsense/StepCA/Initialize + 0.1.0 + StepCA for OPNsense + + + + Y + yubikeyC + N + N + + From Trust + Create on Yubikey + Load from Yubikey + + + + N + ca + + + This field must be set. + SetIfConstraint + Source + trust + + + + + 9a + N + /^[0-9a-f]{2}$/u + This does not look like a Yubikey slot + + + This field must be set. + SetIfConstraint + Source + yubikeyC + + + This field must be set. + SetIfConstraint + Source + yubikeyL + + + + + Y + "subject": "OPNsense StepCA Root", +"issuer": "OPNsense StepCA Root", +"keyUsage": ["certSign", "crlSign"], +"basicConstraints": { + "isCA": true, + "maxPathLen": 1 +} + + + This field must be set. + SetIfConstraint + Source + yubikeyC + + + + + N + ECC-P384 + N + N + + RSA 2048 + RSA 3072 + RSA 4096 + ECC secp256r1 + ECC secp384r1 + ECC Ed25519 + + + + This field must be set. + SetIfConstraint + Source + yubikeyC + + + + + N + 365 + 1 + + + This field must be set. + SetIfConstraint + Source + yubikeyC + + + + + + + Y + yubikeyC + N + N + + From Trust + Create on Yubikey + Load from Yubikey + + + + N + ca + + + This field must be set. + SetIfConstraint + aSource + trust + + + + + 9c + N + /^[0-9a-f]{2}$/u + This does not look like a Yubikey slot + + + This field must be set. + SetIfConstraint + Source + yubikeyC + + + This field must be set. + SetIfConstraint + Source + yubikeyL + + + + + Y + "subject": "OPNsense StepCA Intermediate", +"keyUsage": ["certSign", "crlSign"], +"basicConstraints": { + "isCA": true, + "maxPathLen": 0 +} + + + + This field must be set. + SetIfConstraint + Source + yubikeyC + + + + + N + ECC-P384 + N + N + + RSA 2048 + RSA 3072 + RSA 4096 + ECC secp256r1 + ECC secp384r1 + ECC Ed25519 + + + + This field must be set. + SetIfConstraint + Source + yubikeyC + + + + + N + 365 + 1 + + + This field must be set. + SetIfConstraint + Source + yubikeyC + + + + + + + 123456 + N + /^.{4,}$/u + This does not look like a Yubikey pin + + + This field must be set. + SetIfConstraintAbs + root.Source + yubikeyC + + + This field must be set. + SetIfConstraintAbs + root.Source + yubikeyL + + + This field must be set. + SetIfConstraintAbs + intermediate.Source + yubikeyC + + + This field must be set. + SetIfConstraintAbs + intermediate.Source + yubikeyL + + + + + + + N + + + diff --git a/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/Menu/Menu.xml b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/Menu/Menu.xml new file mode 100644 index 0000000000..685c035061 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/Menu/Menu.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/StepCA.php b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/StepCA.php new file mode 100644 index 0000000000..8976e8f2d5 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/models/OPNsense/StepCA/StepCA.php @@ -0,0 +1,37 @@ + + //OPNsense/StepCA/CA + 0.1.0 + StepCA for OPNsense + + + 0 + Y + +
+ 127.0.0.1:9443 + Y +
+ + localhost + Y + Y + , + Y + + + + Y + badgerv2 + N + N + + Badger (V2) + BoltDB + PostgreSQL + MySQL + + + + N + + + + This field must be set. + SetIfConstraint + DB + postgresql + + + This field must be set. + SetIfConstraint + DB + mysql + + + + + N + + + + This field must be set. + SetIfConstraint + DB + postgresql + + + This field must be set. + SetIfConstraint + DB + mysql + + + + + + + + N + + + N + 24h + + + N + 24h + + + N + + + N + + + N + + + N + + + N + + + N + + + N + + + N + + N + + + + + + + N + N + , + Y + Y + + + + N + Y + N + , + Y + + + + + + N + N + , + Y + Y + + + + N + Y + N + , + Y + + + + + + + + + N + N + , + Y + Y + + + + N + Y + N + , + Y + + + + + + N + N + , + Y + Y + + + + N + Y + N + , + Y + + + + + + + + N + Y + , + + + + + + N + Y + , + + + + + + + + + + 1 + Y + + + Y + acme + N + N + + ACME + JWT + + + + acme + Y + /^([0-9a-z_\-]){4,32}$/u + Should be a string between 4 and 32 characters, containing lower-case characters, numbers, dashes and underscores. + + + A Provisioner with this name already exists. + UniqueConstraint + + + + + 1 + Y + + + N + N + N + N + N + N + N + N + N + N + N + N + + + Y + http-01 + N + Y + + http-01 + dns-01 + tls-alpn-01 + device-attest-01 + wire-oidc-01 + wire-dpop-01 + + + + N + + N + Y + + apple + step + tpm + + + + This field must be set. + SetIfConstraint + Challenges + device-attest-01 + + + + + N + ca + Y + + + This field must be set. + SetIfConstraint + Attestation + tpm + + + + + Y + "subject": {{ toJson .Subject }}, +"sans": {{ toJson .SANs }}, +{{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }} +"keyUsage": ["keyEncipherment", "digitalSignature"], +{{- else }} +"keyUsage": ["digitalSignature"], +{{- end }} +"extKeyUsage": ["serverAuth", "clientAuth"] + + + +
+ diff --git a/security/step-certificates/src/opnsense/mvc/app/views/OPNsense/StepCA/general.volt b/security/step-certificates/src/opnsense/mvc/app/views/OPNsense/StepCA/general.volt new file mode 100644 index 0000000000..6bbffd1b66 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/views/OPNsense/StepCA/general.volt @@ -0,0 +1,72 @@ +{# + +Copyright (C) 2024 Volodymyr Paprotski +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +#} + + + + + +
+ {{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_GeneralSettings'])}} +
+ +
+
+ +
+
+
diff --git a/security/step-certificates/src/opnsense/mvc/app/views/OPNsense/StepCA/initialize.volt b/security/step-certificates/src/opnsense/mvc/app/views/OPNsense/StepCA/initialize.volt new file mode 100644 index 0000000000..5ce62c8c00 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/views/OPNsense/StepCA/initialize.volt @@ -0,0 +1,135 @@ +{# + +Copyright (C) 2024 Volodymyr Paprotski + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +#} + + + +
+ {{ partial("layout_partials/base_form",['fields':initializeForm,'id':'frm_InitializeSettings','apply_btn_id':'reconfigureAct'])}} +
+{# +
+ + + + +
+Example template: +
+{{ exampleTemplate }}
+                
+Full template: +
+{{ fullTemplate }}
+                
+
+
+#} diff --git a/security/step-certificates/src/opnsense/mvc/app/views/OPNsense/StepCA/provisioners.volt b/security/step-certificates/src/opnsense/mvc/app/views/OPNsense/StepCA/provisioners.volt new file mode 100644 index 0000000000..ac0c5f47d1 --- /dev/null +++ b/security/step-certificates/src/opnsense/mvc/app/views/OPNsense/StepCA/provisioners.volt @@ -0,0 +1,103 @@ +{# +# Copyright (C) 2024 Volodymyr Paprotski +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +#} + + + + + + + + + + + + + + + + + + + + + + +
{{ lang._('ID') }}{{ lang._('Enabled') }}{{ lang._('Provisioner') }}{{ lang._('Name') }}{{ lang._('Commands') }}
+ + +
+
+
+ +

+
+ + +{{ partial("layout_partials/base_dialog",['fields':formDialogAddress,'id':'DialogAddress','label':lang._('Edit Provisioner')])}} diff --git a/security/step-certificates/src/opnsense/scripts/OPNsense/StepCA/initCA.php b/security/step-certificates/src/opnsense/scripts/OPNsense/StepCA/initCA.php new file mode 100755 index 0000000000..d52ac3fbea --- /dev/null +++ b/security/step-certificates/src/opnsense/scripts/OPNsense/StepCA/initCA.php @@ -0,0 +1,302 @@ +#!/usr/local/bin/php +object(); + foreach ($config->ca as $caCrt) { + $caX509 = openssl_x509_parse(base64_decode($caCrt->crt)); + if ($caX509['serialNumber'] == $crtX509['serialNumber'] && + $caX509['extensions']['subjectKeyIdentifier'] == $crtX509['extensions']['subjectKeyIdentifier']) { + $refid = $cert['refid']; + return false; + } + } + + $newca = $config->addChild('ca'); + $newca->addAttribute('uuid', generate_uuid()); + foreach (array_keys($cert) as $cacfg) { + $newca->addChild($cacfg, (string)$cert[$cacfg]); + } + return true; +} + +function main() : string { + global $rootcert, $intcert, $intkey, $roottpl, $inttpl; + + $configobj = Config::getInstance()->object(); + $config = $configobj->OPNsense->StepCA->Initialize; + $sourceR = $config->root->Source; + $sourceI = $config->intermediate->Source; + + info_log("InitCA started. Root: ".$sourceR." Intermediate: ".$sourceI); + + if (str_starts_with($sourceR, "yubikey") || str_starts_with($sourceI, "yubikey") ) { + system("/usr/local/etc/rc.d/pcscd start"); + } else { + system("/usr/local/etc/rc.d/pcscd stop"); + } + setupDirs(); + + $rootID = uniqid(); + if ($sourceR == 'yubikeyC') { + $keyTypeDash = $config->root->CreateKeyType; + $kmsKey = 'yubikey:slot-id='.$config->root->YubikeySlot; + $kmsURI = 'yubikey:pin-value='.$config->yubikey->Pin; + $duration = (int)$config->root->Lifetime; + $status = createOnKMS($keyTypeDash, $kmsKey, $kmsURI, $rootcert, $duration, $roottpl); + if (null != $status) { + return $status; + } + } elseif ($sourceR == 'yubikeyL') { + $kmsKey = 'yubikey:slot-id='.$config->root->YubikeySlot; + $kmsURI = 'yubikey:pin-value='.$config->yubikey->Pin; + $status = loadFromKMS($kmsKey, $kmsURI, $rootcert); + if (null != $status) { + return $status; + } + } elseif ($sourceR == 'trust') { + $rootID = $config->root->TrustCertificate; + $status = certFromTrust($rootID, $rootcert); + if (null != $status) { + return $status; + } + } + + $saveConfig = false; + if ($sourceR != 'trust') { + $saveConfig = addTrust($rootcert, $rootID); + } + + if ($sourceI == 'yubikeyC') { + if ($sourceR != 'yubikeyC' && $sourceR != 'yubikeyL') { + // could be fixed by importing private key into yubikey; perhaps useful for HA setup + err_log("Not Implemented: Cannot create intermediate yubikey cert without root yubikey key."); + return 'not implemented. create yubikey with another yubikey'; + } + $keyTypeDash = $config->intermediate->CreateKeyType; + $kmsKey = 'yubikey:slot-id='.$config->intermediate->YubikeySlot; + $kmsURI = 'yubikey:pin-value='.$config->yubikey->Pin; + $duration = (int)$config->intermediate->Lifetime; + $kmsCAKey = 'yubikey:slot-id='.$config->root->YubikeySlot; + createOnKMS($keyTypeDash, $kmsKey, $kmsURI, $intcert, $duration, $inttpl, $kmsCAKey, $rootcert); + } elseif ($sourceR == 'yubikeyL') { + $kmsKey = 'yubikey:slot-id='.$config->intermediate->YubikeySlot; + $kmsURI = 'yubikey:pin-value='.$config->yubikey->Pin; + $status = loadFromKMS($kmsKey, $kmsURI, $intcert); + if (null != $status) { + return $status; + } + } elseif ($sourceI == 'trust') { + $certID = $config->intermediate->TrustCertificate; + $status = certFromTrust($certID, $intcert, $intkey); + if (null != $status) { + return $status; + } + } + + if ($sourceI != 'trust') { + $certID = uniqid(); + $saveConfig |= addTrust($intcert, $certID, $rootID); + } + + if ($saveConfig) { + info_log("Updating Trust with new certificates"); + Config::getInstance()->save(); + } + + return "success"; +} + +$status = main(); +print '{"status":"'.$status.'"}'; + +// /usr/local/etc/rc.d/pcscd start +// $password = base64_encode(random_bytes(64)); +// step-kms-plugin create --kty EC --crv P-384 --kms 'yubikey:pin-value=123456' 'yubikey:slot-id=9a'; +// step certificate create --force --template /usr/local/etc/step/ca/templates/root.tpl --kms 'yubikey:pin-value=123456' --key 'yubikey:slot-id=9a' "Smallstep Root CA" /usr/local/etc/step/ca/certs/root_ca.crt +// step certificate create --force --template /usr/local/etc/step/ca/templates/intermediate.tpl --kms 'yubikey:pin-value=123456' --key 'yubikey:slot-id=9c' --ca-kms 'yubikey:pin-value=123456' -ca-key 'yubikey:slot-id=9a' --ca /usr/local/etc/step/ca/certs/root_ca.crt "Smallstep Intermediate CA" /usr/local/etc/step/ca/certs/intermediate_ca.crt diff --git a/security/step-certificates/src/opnsense/service/conf/actions.d/actions_stepca.conf b/security/step-certificates/src/opnsense/service/conf/actions.d/actions_stepca.conf new file mode 100644 index 0000000000..c31f8e2f0c --- /dev/null +++ b/security/step-certificates/src/opnsense/service/conf/actions.d/actions_stepca.conf @@ -0,0 +1,35 @@ +[start] +command:/usr/local/etc/rc.d/step-ca start +parameters: +type:script +message:starting step-ca + +[stop] +command:/usr/local/etc/rc.d/step-ca stop +parameters: +type:script +message:stopping step-ca + +[restart] +command:/usr/local/etc/rc.d/step-ca restart +parameters: +type:script +message:restarting step-ca + +[reload] +command:/usr/local/etc/rc.d/step-ca reload +parameters: +type:script +message:reconfiguring step-ca + +[status] +command:/usr/local/etc/rc.d/step-ca status; exit 0 +parameters: +type:script_output +message:requesting step-ca status + +[initca] +command:/usr/local/opnsense/scripts/OPNsense/StepCA/initCA.php +parameters: +type:script_output +message:setup certificate files for step-ca diff --git a/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/+TARGETS b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/+TARGETS new file mode 100644 index 0000000000..d22bc52039 --- /dev/null +++ b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/+TARGETS @@ -0,0 +1,7 @@ +rc.conf.d:/etc/rc.conf.d/step_ca +rc.conf.d:/etc/rc.conf.d/pcscd +ca.json:/usr/local/etc/step/ca/config/ca.json +password.txt:/usr/local/etc/step/password.txt +root.tpl:/usr/local/etc/step/ca/templates/root.tpl +intermediate.tpl:/usr/local/etc/step/ca/templates/intermediate.tpl +leaf.tpl:/usr/local/etc/step/ca/templates/leaf.[OPNsense.StepCA.CA.provisioners.provisioner.%.Name].tpl \ No newline at end of file diff --git a/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/ca.json b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/ca.json new file mode 100644 index 0000000000..b247bddc67 --- /dev/null +++ b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/ca.json @@ -0,0 +1,154 @@ +{#- ############################### -#} +{#- GLOBAL VARIABLES -#} +{#- ############################### -#} + +{%- set keySource = OPNsense.StepCA.Initialize.intermediate.Source|default("trust") %} +{%- set dbType = OPNsense.StepCA.CA.db.DB|default("badgerv2") %} + +{#- ############################### -#} +{#- MACROS -#} +{#- ############################### -#} + +{%- macro privkey() -%} + {%- if keySource.startswith("yubikey") -%} + "yubikey:slot-id={{ OPNsense.StepCA.Initialize.intermediate.YubikeySlot }}" + {%- else -%} + "/usr/local/etc/step/ca/secrets/intermediate_ca_key" + {%- endif %} +{%- endmacro %} + +{%- macro kms() -%} + {%- if keySource.startswith("yubikey") -%} + "kms": { + "type": "yubikey", + "pin": "{{ OPNsense.StepCA.Initialize.yubikey.Pin }}" + }, + {%- endif %} +{%- endmacro %} + +{%- macro dbSource() -%} + {%- if dbType == "badgerv2" -%} + "dataSource": "/usr/local/etc/step/ca/db" + {%- elif dbType == "bbolt" -%} + "dataSource": "./stepdb" + {%- else -%} + "dataSource": "{{ OPNsense.StepCA.CA.db.DBUrl }}", + "database": "{{ OPNsense.StepCA.CA.db.Database }}" + {%- endif -%} +{% endmacro %} + +{%- macro hostPolicy(type, node, indentCnt, comma) -%} + {%- set policy = dict() -%} + {%- if node.allow.DNSNames | length > 0 or node.allow.IPNames | length > 0 -%} + {%- set _ = policy.update({"allow": dict()}) -%} + {%- if node.allow.DNSNames|length > 0 %}{% set _ = policy.allow.update({"dns": node.allow.DNSNames.split(',')}) %}{% endif -%} + {%- if node.allow.IPNames |length > 0 %}{% set _ = policy.allow.update({"ip": node.allow.IPNames.split(',') }) %}{% endif -%} + {%- endif -%} + {%- if node.deny.DNSNames | length > 0 or node.deny.IPNames | length > 0 -%} + {%- set _ = policy.update({"deny": dict()}) -%} + {%- if node.deny.DNSNames|length > 0 %}{% set _ = policy.deny.update({"dns": node.deny.DNSNames.split(',')}) %}{% endif -%} + {%- if node.deny.IPNames |length > 0 %}{% set _ = policy.deny.update({"ip": node.deny.IPNames.split(',') }) %}{% endif -%} + {%- endif -%} + {%- if policy|length > 0 %}"{{ type}}":{{ policy | tojson(4) | indent(indentCnt) }}{{comma}}{% endif -%} +{%- endmacro -%} + +{%- macro claims(node, indentCnt) %} +{%- if node.allowRenewalAfterExpiry|default("0") == "1" -%} + {%- set _ = node.update({"allowRenewalAfterExpiry": true}) -%} +{%- else -%} + {%- set _ = node.pop("allowRenewalAfterExpiry", None) -%} +{%- endif -%} +{%- if node.disableRenewal|default("0") == "1" -%} + {%- set _ = node.update({"disableRenewal": true}) -%} +{%- else -%} + {%- set _ = node.pop("disableRenewal", None) -%} +{%- endif -%} +{%- if node.enableSSHCA|default("0") == "1" -%} + {%- set _ = node.update({"enableSSHCA": true}) -%} +{%- else -%} + {%- set _ = node.pop("enableSSHCA", None) -%} +{%- endif -%} +{{ node | tojson(4) | indent(indentCnt )}} +{%- endmacro -%} + +{%- macro provisioners() %} + {%- for prov in helpers.toList('OPNsense.StepCA.CA.provisioners.provisioner') %} + {%- if prov.Enabled == "1" %} + {%- if not loop.first%},{%- endif -%} + {%- if prov.Provisioner == "acme" %} + + { + "type": "ACME", + "name": "{{prov.Name}}", + "forceCN": {% if prov.ForceCN == 1 %}true{%else%}false{%- endif -%}, + "claims": {{ claims(prov.claims,16) }}, + "termsOfService": "", + "website": "", + "caaIdentities": [], + "challenges": {{ prov.Challenges.split(',') | tojson() }}, + {%if 'device-attest-01' in prov.Challenges -%} + "attestationFormats": [ {{ prov.Attestation.split(',') | tojson() }}], + "attestationRoots": "", + {%- endif -%} + "options": { + "x509": { + "templateFile": "/usr/local/etc/step/ca/templates/leaf.{{prov.Name}}.tpl" + } + } + } + {%- endif -%} + {%- endif -%} + {%- endfor %} +{%- endmacro%} + +{#- ############################### -#} +{#- ca.json -#} +{#- ############################### -#} + +{ + "root": "/usr/local/etc/step/ca/certs/root_ca.crt", + "federatedRoots": null, + "crt": "/usr/local/etc/step/ca/certs/intermediate_ca.crt", + "key": {{ privkey() }}, + {#"ssh": { + "hostKey": "/examples/pki/secrets/secrets.host.key", + "userKey": "/examples/pki/secrets/secrets.user.key" + },#} + {{ kms() }} + "address": "{{ OPNsense.StepCA.CA.Address }}", + "insecureAddress": "", + "dnsNames": {{ OPNsense.StepCA.CA.DNSNames.split(',') | tojson() }}, + "logger": { + "format": "text" + }, + "db": { + "type": "{{ dbType }}", + {{ dbSource() }} + }, + "authority": { + "claims": {{ claims(OPNsense.StepCA.CA.authority.claims, 8)}}, + "policy": { + {{ hostPolicy("x509", OPNsense.StepCA.CA.authority.policy.x509, 12, ',')}} + "ssh": { + {# "user": { + "allow": { + "email": ["@local"] + }, + "deny": { + "email": ["root@local"] + }}, -#} + {{ hostPolicy("host", OPNsense.StepCA.CA.authority.policy.ssh.host, 16, '')}} + } + }, + "provisioners": [ {{ provisioners() }}] + }, + "tls": { + "cipherSuites": [ + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + ], + "minVersion": 1.2, + "maxVersion": 1.3, + "renegotiation": false + } +} diff --git a/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/intermediate.tpl b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/intermediate.tpl new file mode 100644 index 0000000000..e21434844f --- /dev/null +++ b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/intermediate.tpl @@ -0,0 +1,3 @@ +{ +{{OPNsense.StepCA.Initialize.intermediate.CreateTemplate}} +} \ No newline at end of file diff --git a/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/leaf.tpl b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/leaf.tpl new file mode 100644 index 0000000000..0ed4196f54 --- /dev/null +++ b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/leaf.tpl @@ -0,0 +1,11 @@ +{%- for prov in helpers.toList('OPNsense.StepCA.CA.provisioners.provisioner') %} +{%- if prov.Enabled == "1" %} +{%- if prov.Provisioner == "acme" %} +{% if TARGET_FILTERS['OPNsense.StepCA.CA.provisioners.provisioner.' ~ loop.index0] or TARGET_FILTERS['OPNsense.StepCA.CA.provisioners.provisioner'] %} +{ + {{prov.CreateTemplate}} +} +{%- endif -%} +{%- endif -%} +{%- endif -%} +{%- endfor %} \ No newline at end of file diff --git a/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/password.txt b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/password.txt new file mode 100644 index 0000000000..e8cc1848a8 --- /dev/null +++ b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/password.txt @@ -0,0 +1 @@ +{ { OPNsense.StepCA.Initialize.Password } } \ No newline at end of file diff --git a/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/rc.conf.d b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/rc.conf.d new file mode 100644 index 0000000000..ca5cc676f6 --- /dev/null +++ b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/rc.conf.d @@ -0,0 +1,13 @@ +{% if OPNsense.StepCA.CA.Enabled|default("0") != "0" %} +step_ca_enable="YES" +{% else %} +step_ca_enable="NO" +{% endif %} +{# Yubikey requires pcscd enabled. #} +{% set keySourceR = OPNsense.StepCA.Initialize.root.Source|default("trust") %} +{% set keySourceI = OPNsense.StepCA.Initialize.intermediate.Source|default("trust") %} +{% if keySourceR.startswith("yubikey") or keySourceI.startswith("yubikey") %} +pcscd_enable="YES" +{% else %} +pcscd_enable="NO" +{% endif %} diff --git a/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/root.tpl b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/root.tpl new file mode 100644 index 0000000000..666e201218 --- /dev/null +++ b/security/step-certificates/src/opnsense/service/templates/OPNsense/StepCA/root.tpl @@ -0,0 +1,3 @@ +{ +{{OPNsense.StepCA.Initialize.root.CreateTemplate}} +} \ No newline at end of file diff --git a/security/step-certificates/src/opnsense/service/templates/OPNsense/Syslog/local/stepca.conf b/security/step-certificates/src/opnsense/service/templates/OPNsense/Syslog/local/stepca.conf new file mode 100644 index 0000000000..f2c605b28c --- /dev/null +++ b/security/step-certificates/src/opnsense/service/templates/OPNsense/Syslog/local/stepca.conf @@ -0,0 +1,6 @@ +################################################################### +# Local syslog-ng configuration filter definition [stepca]. +################################################################### +filter f_local_stepca { + program("step_ca") or (match("^stepca-initca:" value("MESSAGE"))); +};