feat: Integración de Siguldry PKCS#11 y spooler de firmas
* Implementación del rol de Ansible 'siguldry' para CentOS Stream 10 * Migración del plugin de firmas de Koji Hub a un spooler asíncrono * Modificación de roles para utilizar empaquetamiento RPM nativo de siguldry-pkcs11 * Documentación de decisiones (ADRs) y roadmap en docs/technical/ * Limpieza de binarios locales pesados de PKCS#11 en Ansible Co-developed-by: Gemini AI <renich+gemini@woralelandia.com> Signed-off-by: Rénich Bon Ćirić <renich@woralelandia.com>
This commit is contained in:
parent
d1203ba555
commit
89d6f66be8
32 changed files with 1453 additions and 321 deletions
|
|
@ -147,3 +147,60 @@
|
|||
key: "{{ item.stdout }}"
|
||||
state: present
|
||||
loop: "{{ scanned_keys.results }}"
|
||||
|
||||
- name: Add all Koji node hosts to /etc/hosts
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/hosts
|
||||
line: "{{ hostvars[item].ansible_default_ipv4.address }} {{ item }}"
|
||||
regexp: ".*{{ item }}"
|
||||
state: present
|
||||
loop: "{{ groups['all'] }}"
|
||||
when:
|
||||
- inventory_hostname != 'localhost'
|
||||
- item != 'localhost'
|
||||
- hostvars[item].ansible_default_ipv4.address is defined
|
||||
|
||||
- name: Configure DNF speed and mirrors
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/dnf/dnf.conf
|
||||
marker: "# {mark} ANSIBLE MANAGED DNF CONFIG"
|
||||
block: |
|
||||
max_parallel_downloads = 10
|
||||
minrate=1M
|
||||
timeout=10
|
||||
when: inventory_hostname != 'localhost'
|
||||
|
||||
- name: Install EPEL release repository
|
||||
ansible.builtin.dnf:
|
||||
name: epel-release
|
||||
state: present
|
||||
when: inventory_hostname != 'localhost'
|
||||
|
||||
- name: Install system utility tools
|
||||
ansible.builtin.dnf:
|
||||
name:
|
||||
- bash-completion
|
||||
- htop
|
||||
- iftop
|
||||
- iotop
|
||||
- rsync
|
||||
- tmux
|
||||
- vim-enhanced
|
||||
state: present
|
||||
when: inventory_hostname != 'localhost'
|
||||
|
||||
- name: Perform full package upgrade and distro-sync
|
||||
ansible.builtin.dnf:
|
||||
name: "*"
|
||||
state: latest
|
||||
register: dnf_upgrade_result
|
||||
when: inventory_hostname != 'localhost'
|
||||
|
||||
- name: Reboot if packages were upgraded
|
||||
ansible.builtin.reboot:
|
||||
reboot_timeout: 600
|
||||
when:
|
||||
- inventory_hostname != 'localhost'
|
||||
- dnf_upgrade_result.changed
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -80,20 +80,48 @@
|
|||
changed_when: "'External repository created' in koji_bootstrap_add_epel_repo_result.stdout"
|
||||
failed_when: "koji_bootstrap_add_epel_repo_result.rc != 0 and 'already exists' not in koji_bootstrap_add_epel_repo_result.stderr"
|
||||
|
||||
- name: Add CentOS Stream 10 CRB external repository
|
||||
ansible.builtin.command: >
|
||||
sudo -u {{ koji_admin_user }} koji
|
||||
--config={{ koji_hub_admin_user_koji_dir }}/config
|
||||
add-external-repo -t {{ koji_build_tag }}-build centos-stream-10-crb
|
||||
https://mirror.stream.centos.org/10-stream/CRB/x86_64/os/
|
||||
register: koji_bootstrap_add_crb_repo_result
|
||||
changed_when: "'External repository created' in koji_bootstrap_add_crb_repo_result.stdout"
|
||||
failed_when: "koji_bootstrap_add_crb_repo_result.rc != 0 and 'already exists' not in koji_bootstrap_add_crb_repo_result.stderr"
|
||||
|
||||
- name: Download repomd.xml from CentOS Stream 10 build repo
|
||||
ansible.builtin.get_url:
|
||||
url: https://kojihub.stream.centos.org/kojifiles/repos/c10s-build/latest/x86_64/repodata/repomd.xml
|
||||
dest: /tmp/c10s-repomd.xml
|
||||
|
||||
- name: Install zstd for decompression of comps
|
||||
ansible.builtin.dnf:
|
||||
name: zstd
|
||||
state: present
|
||||
|
||||
- name: Extract comps.xml path
|
||||
ansible.builtin.shell: grep 'comps.xml"' /tmp/c10s-repomd.xml | head -n 1 | sed -e 's/.*href="//' -e 's/".*//'
|
||||
ansible.builtin.shell: grep 'comps.xml' /tmp/c10s-repomd.xml | head -n 1 | sed -e 's/.*href="//' -e 's/".*//'
|
||||
register: comps_path
|
||||
changed_when: false
|
||||
|
||||
- name: Download comps.xml
|
||||
ansible.builtin.get_url:
|
||||
url: "https://kojihub.stream.centos.org/kojifiles/repos/c10s-build/latest/x86_64/{{ comps_path.stdout }}"
|
||||
dest: /tmp/c10s-comps.xml
|
||||
dest: /tmp/c10s-comps-raw
|
||||
|
||||
- name: Decompress comps.xml if needed
|
||||
ansible.builtin.shell: |
|
||||
set -e
|
||||
file_type=$(file -b --mime-type /tmp/c10s-comps-raw)
|
||||
if [[ "$file_type" == "application/zstd" ]]; then
|
||||
unzstd -f -o /tmp/c10s-comps.xml /tmp/c10s-comps-raw
|
||||
elif [[ "$file_type" == "application/gzip" || "$file_type" == "application/x-gzip" ]]; then
|
||||
gunzip -c /tmp/c10s-comps-raw > /tmp/c10s-comps.xml
|
||||
else
|
||||
cp -f /tmp/c10s-comps-raw /tmp/c10s-comps.xml
|
||||
fi
|
||||
changed_when: true
|
||||
|
||||
- name: Import comps groups
|
||||
ansible.builtin.command: >
|
||||
|
|
@ -103,6 +131,76 @@
|
|||
register: koji_bootstrap_import_comps_result
|
||||
changed_when: "'Groups imported' in koji_bootstrap_import_comps_result.stdout"
|
||||
|
||||
- name: Create build group on tag
|
||||
ansible.builtin.command: >
|
||||
sudo -u {{ koji_admin_user }} koji
|
||||
--config={{ koji_hub_admin_user_koji_dir }}/config
|
||||
add-group {{ koji_build_tag }}-build build
|
||||
register: koji_add_group_build_result
|
||||
changed_when: "'Group added' in koji_add_group_build_result.stdout"
|
||||
failed_when: "koji_add_group_build_result.rc != 0 and 'already exists' not in koji_add_group_build_result.stderr"
|
||||
|
||||
- name: Create srpm-build group on tag
|
||||
ansible.builtin.command: >
|
||||
sudo -u {{ koji_admin_user }} koji
|
||||
--config={{ koji_hub_admin_user_koji_dir }}/config
|
||||
add-group {{ koji_build_tag }}-build srpm-build
|
||||
register: koji_add_group_srpm_result
|
||||
changed_when: "'Group added' in koji_add_group_srpm_result.stdout"
|
||||
failed_when: "koji_add_group_srpm_result.rc != 0 and 'already exists' not in koji_add_group_srpm_result.stderr"
|
||||
|
||||
- name: Populate build group packages
|
||||
ansible.builtin.command: >
|
||||
sudo -u {{ koji_admin_user }} koji
|
||||
--config={{ koji_hub_admin_user_koji_dir }}/config
|
||||
add-group-pkg {{ koji_build_tag }}-build build {{ item }}
|
||||
loop:
|
||||
- bash
|
||||
- bzip2
|
||||
- coreutils
|
||||
- cpio
|
||||
- diffutils
|
||||
- findutils
|
||||
- gawk
|
||||
- gcc
|
||||
- gcc-c++
|
||||
- grep
|
||||
- gzip
|
||||
- info
|
||||
- make
|
||||
- patch
|
||||
- redhat-rpm-config
|
||||
- rpm-build
|
||||
- sed
|
||||
- tar
|
||||
- unzip
|
||||
- util-linux
|
||||
- which
|
||||
- shadow-utils
|
||||
register: koji_add_group_pkg_build_result
|
||||
changed_when: "'Added package' in koji_add_group_pkg_build_result.stdout"
|
||||
failed_when: "koji_add_group_pkg_build_result.rc != 0 and 'already in group' not in koji_add_group_pkg_build_result.stderr"
|
||||
|
||||
- name: Populate srpm-build group packages
|
||||
ansible.builtin.command: >
|
||||
sudo -u {{ koji_admin_user }} koji
|
||||
--config={{ koji_hub_admin_user_koji_dir }}/config
|
||||
add-group-pkg {{ koji_build_tag }}-build srpm-build {{ item }}
|
||||
loop:
|
||||
- bash
|
||||
- gnupg2
|
||||
- make
|
||||
- redhat-rpm-config
|
||||
- rpm-build
|
||||
- shadow-utils
|
||||
- tar
|
||||
- unzip
|
||||
- wget
|
||||
- curl
|
||||
register: koji_add_group_pkg_srpm_result
|
||||
changed_when: "'Added package' in koji_add_group_pkg_srpm_result.stdout"
|
||||
failed_when: "koji_add_group_pkg_srpm_result.rc != 0 and 'already in group' not in koji_add_group_pkg_srpm_result.stderr"
|
||||
|
||||
- name: Add Koji builder hosts
|
||||
ansible.builtin.include_tasks: add_host.yaml
|
||||
loop: "{{ groups['kojibuilders'] }}"
|
||||
|
|
@ -147,3 +245,15 @@
|
|||
register: koji_bootstrap_add_builder_user_result
|
||||
changed_when: "'User added' in koji_bootstrap_add_builder_user_result.stdout"
|
||||
failed_when: "koji_bootstrap_add_builder_user_result.rc != 0 and 'already exists' not in koji_bootstrap_add_builder_user_result.stderr"
|
||||
|
||||
- name: Add default packages to main tag
|
||||
ansible.builtin.command: >
|
||||
sudo -u {{ koji_admin_user }} koji
|
||||
--config={{ koji_hub_admin_user_koji_dir }}/config
|
||||
add-pkg --owner {{ koji_admin_user }} {{ koji_build_tag }} {{ item }}
|
||||
loop:
|
||||
- bash
|
||||
register: koji_add_pkg_result
|
||||
changed_when: "'Adding 0 packages' not in koji_add_pkg_result.stdout"
|
||||
failed_when: koji_add_pkg_result.rc != 0
|
||||
|
||||
|
|
|
|||
241
roles/koji_hub/files/koji-sign-spooler
Normal file
241
roles/koji_hub/files/koji-sign-spooler
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
#!/usr/bin/python3
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import glob
|
||||
import logging
|
||||
import configparser
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout)
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger('koji-sign-spooler')
|
||||
|
||||
SPOOL_DIR = '/var/spool/koji-sign/queue'
|
||||
FAILED_DIR = '/var/spool/koji-sign/failed'
|
||||
|
||||
def get_siguldry_key_info():
|
||||
"""
|
||||
Retrieves the public key and key ID/fingerprint from Siguldry.
|
||||
"""
|
||||
env = os.environ.copy()
|
||||
env['SIGULDRY_SOCKET'] = '/run/siguldry-client-proxy/siguldry-client-proxy.socket'
|
||||
|
||||
# Get key info
|
||||
res = subprocess.run(
|
||||
['siguldry-client', '--credentials-directory', '/etc/credstore', '-c', '/etc/siguldry/client.toml', 'key', 'get', 'mxos-signing-key', '--certificates'],
|
||||
env=env,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
if res.returncode != 0:
|
||||
logger.error(f"Failed to get key info from Siguldry: {res.stderr}")
|
||||
return None, None
|
||||
|
||||
output = res.stdout
|
||||
start_marker = "-----BEGIN PGP PUBLIC KEY BLOCK-----"
|
||||
end_marker = "-----END PGP PUBLIC KEY BLOCK-----"
|
||||
|
||||
start_idx = output.find(start_marker)
|
||||
end_idx = output.find(end_marker)
|
||||
if start_idx == -1 or end_idx == -1:
|
||||
logger.error("Could not find PGP public key block in Siguldry client output.")
|
||||
return None, None
|
||||
|
||||
pubkey = output[start_idx:end_idx + len(end_marker)]
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.pub', delete=False) as f:
|
||||
f.write(pubkey)
|
||||
tmp_name = f.name
|
||||
|
||||
try:
|
||||
gpg_res = subprocess.run(
|
||||
['gpg', '--show-keys', '--with-colons', tmp_name],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
if gpg_res.returncode != 0:
|
||||
logger.error(f"Failed to read GPG key ID: {gpg_res.stderr}")
|
||||
return None, None
|
||||
|
||||
key_id = None
|
||||
fingerprint = None
|
||||
for line in gpg_res.stdout.splitlines():
|
||||
parts = line.split(':')
|
||||
if parts[0] == 'pub':
|
||||
key_id = parts[4][-8:]
|
||||
elif parts[0] == 'fpr':
|
||||
fingerprint = parts[9]
|
||||
break
|
||||
|
||||
sig_key_id = fingerprint if fingerprint else key_id
|
||||
return pubkey, sig_key_id
|
||||
finally:
|
||||
try:
|
||||
os.remove(tmp_name)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def process_queue():
|
||||
if not os.path.exists(SPOOL_DIR):
|
||||
return
|
||||
|
||||
# Import kojihub dynamically so that it loads after service starts
|
||||
import kojihub
|
||||
import koji
|
||||
|
||||
# Read configuration file to get KojiDir
|
||||
config = configparser.ConfigParser()
|
||||
config_files = ['/etc/koji-hub/hub.conf'] + sorted(glob.glob('/etc/koji-hub/hub.conf.d/*.conf'))
|
||||
config.read(config_files)
|
||||
|
||||
koji_dir = config.get('hub', 'KojiDir', fallback='/mnt/koji')
|
||||
|
||||
# Get list of files in spool directory
|
||||
files = sorted(os.listdir(SPOOL_DIR))
|
||||
if not files:
|
||||
return
|
||||
|
||||
# Connect to database
|
||||
try:
|
||||
kojihub.db.connect()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to connect to Koji Hub DB: {e}")
|
||||
return
|
||||
|
||||
for filename in files:
|
||||
file_path = os.path.join(SPOOL_DIR, filename)
|
||||
if not os.path.isfile(file_path):
|
||||
continue
|
||||
|
||||
try:
|
||||
build_id = int(filename)
|
||||
except ValueError:
|
||||
logger.error(f"Invalid build ID in spool file name: {filename}")
|
||||
os.rename(file_path, os.path.join(FAILED_DIR, filename))
|
||||
continue
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
build_nvr = f.read().strip()
|
||||
|
||||
logger.info(f"Processing build {build_id} ({build_nvr}) for signing")
|
||||
|
||||
# Retry loop to wait for the completeBuild transaction to commit
|
||||
rpms = []
|
||||
for attempt in range(5):
|
||||
try:
|
||||
rpms = kojihub.list_rpms(buildID=build_id)
|
||||
if rpms:
|
||||
break
|
||||
except Exception as e:
|
||||
logger.warning(f"Error querying RPMs (attempt {attempt+1}): {e}")
|
||||
logger.info(f"RPMs not found yet for build {build_id}, waiting 1 second... (attempt {attempt+1}/5)")
|
||||
time.sleep(1)
|
||||
|
||||
if not rpms:
|
||||
logger.error(f"No RPMs found for build {build_id} after retries. Moving spool file to failed.")
|
||||
os.makedirs(FAILED_DIR, exist_ok=True)
|
||||
os.rename(file_path, os.path.join(FAILED_DIR, filename))
|
||||
continue
|
||||
|
||||
# Get Siguldry key information
|
||||
pubkey, sig_key_id = get_siguldry_key_info()
|
||||
if not sig_key_id:
|
||||
logger.error("Failed to retrieve Siguldry key ID. Skipping build signing.")
|
||||
os.makedirs(FAILED_DIR, exist_ok=True)
|
||||
os.rename(file_path, os.path.join(FAILED_DIR, filename))
|
||||
continue
|
||||
|
||||
# Write public key to koji topdir so it can be served
|
||||
pubkey_path = os.path.join(koji_dir, 'gpg_key.pub')
|
||||
try:
|
||||
with open(pubkey_path, 'w') as f:
|
||||
f.write(pubkey)
|
||||
logger.info(f"Wrote public GPG key to {pubkey_path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to write public key to {pubkey_path}: {e}")
|
||||
|
||||
success = True
|
||||
for rpm in rpms:
|
||||
rpm_id = rpm['id']
|
||||
rpm_nvr = rpm['nvr']
|
||||
rpm_path = rpm['path']
|
||||
full_rpm_path = os.path.join(koji_dir, rpm_path)
|
||||
|
||||
try:
|
||||
logger.info(f"Signing RPM {rpm_nvr} via rpmsign and Siguldry PKCS#11")
|
||||
env = os.environ.copy()
|
||||
env['PKCS11_MODULE_PATH'] = '/usr/lib64/libsiguldry_pkcs11.so'
|
||||
env['SIGULDRY_SOCKET'] = '/run/siguldry-client-proxy/siguldry-client-proxy.socket'
|
||||
|
||||
# Sign using rpmsign command with Sequoia backend macro definitions
|
||||
cmd = [
|
||||
'rpmsign',
|
||||
'--define', '_openpgp_sign sq',
|
||||
'--define', f'_openpgp_sign_id {sig_key_id}',
|
||||
'--addsign', full_rpm_path
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
env=env,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
logger.error(f"rpmsign failed for {rpm_nvr}: stdout: {result.stdout}, stderr: {result.stderr}")
|
||||
success = False
|
||||
else:
|
||||
logger.info(f"Successfully signed RPM {rpm_nvr} on disk. Extracting signature header.")
|
||||
sig_data = koji.rip_rpm_sighdr(full_rpm_path)
|
||||
kojihub.add_rpm_sig(rpm_id, sig_data, sigkey=sig_key_id)
|
||||
logger.info(f"Successfully imported signature for {rpm_nvr} with key {sig_key_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error signing RPM {rpm_nvr}: {e}")
|
||||
success = False
|
||||
|
||||
if success:
|
||||
try:
|
||||
kojihub.context.cnx.commit()
|
||||
logger.info(f"All signatures committed for build {build_id} ({build_nvr}).")
|
||||
os.remove(file_path)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to commit signatures to database: {e}")
|
||||
try:
|
||||
kojihub.context.cnx.rollback()
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
logger.error(f"Failed to sign some/all RPMs for build {build_id}. Keeping spool file for retry.")
|
||||
break
|
||||
|
||||
try:
|
||||
kojihub.context.cnx.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def main():
|
||||
logger.info("Koji Sign Spooler Daemon started")
|
||||
os.makedirs(SPOOL_DIR, exist_ok=True)
|
||||
os.makedirs(FAILED_DIR, exist_ok=True)
|
||||
|
||||
while True:
|
||||
try:
|
||||
process_queue()
|
||||
except Exception as e:
|
||||
logger.error(f"Unhandled exception in spooler loop: {e}")
|
||||
time.sleep(5)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
14
roles/koji_hub/files/koji-sign-spooler.service
Normal file
14
roles/koji_hub/files/koji-sign-spooler.service
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[Unit]
|
||||
Description=Koji Sign Spooler Daemon
|
||||
After=httpd.service postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/libexec/koji-sign-spooler
|
||||
User=root
|
||||
Group=apache
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
@ -1,66 +1,43 @@
|
|||
import koji
|
||||
import kojihub
|
||||
import xmlrpc.client
|
||||
from koji.plugin import callback
|
||||
import logging
|
||||
import os
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger = logging.getLogger('koji.plugin.sigul_sign')
|
||||
|
||||
class SigulSignPlugin(kojihub.Plugin):
|
||||
def __init__(self, hub):
|
||||
super(SigulSignPlugin, self).__init__(hub)
|
||||
self.sigul_server = self.hub.config.get('SigulServer')
|
||||
if not self.sigul_server:
|
||||
logger.error("SigulServer not configured in Koji Hub. Automated signing will not work.")
|
||||
return
|
||||
SPOOL_DIR = '/var/spool/koji-sign/queue'
|
||||
|
||||
self.sigul_proxy = xmlrpc.client.ServerProxy(self.sigul_server)
|
||||
logger.info(f"SigulSignPlugin initialized with server: {self.sigul_server}")
|
||||
@callback('postBuildStateChange')
|
||||
def sigul_sign(cbtype, *args, **kws):
|
||||
"""
|
||||
Called after a build state changes.
|
||||
We will queue the build for signing if the state changed to COMPLETE.
|
||||
"""
|
||||
if kws.get('attribute') != 'state':
|
||||
return
|
||||
if kws.get('new') != koji.BUILD_STATES['COMPLETE']:
|
||||
return
|
||||
|
||||
def postBuild(self, build_id, build, request, **kwargs):
|
||||
"""
|
||||
Called after a build is completed.
|
||||
We will attempt to sign the RPMs in this build.
|
||||
"""
|
||||
if build.get('state') != koji.BUILD_STATES['COMPLETE']:
|
||||
logger.info(f"Build {build_id} not complete, skipping signing.")
|
||||
return
|
||||
build = kws.get('info')
|
||||
if not build:
|
||||
logger.error("No build info found in callback.")
|
||||
return
|
||||
|
||||
logger.info(f"Attempting to sign RPMs for build {build_id} ({build.get('nvr')})")
|
||||
build_id = build['id']
|
||||
build_nvr = build.get('nvr', str(build_id))
|
||||
|
||||
# Ensure spool directory exists
|
||||
try:
|
||||
if not os.path.exists(SPOOL_DIR):
|
||||
os.makedirs(SPOOL_DIR, mode=0o770)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to create spool directory {SPOOL_DIR}: {e}")
|
||||
return
|
||||
|
||||
# Get RPMs associated with the build
|
||||
rpms = self.hub.getBuildRPMs(build_id)
|
||||
if not rpms:
|
||||
logger.info(f"No RPMs found for build {build_id}, skipping signing.")
|
||||
return
|
||||
|
||||
for rpm in rpms:
|
||||
rpm_id = rpm['id']
|
||||
rpm_nvr = rpm['nvr']
|
||||
rpm_path = rpm['path'] # This path is relative to KojiDir
|
||||
|
||||
# Construct full path to RPM on the Koji filesystem
|
||||
# In a real scenario, you might need to fetch the RPM from Koji's storage
|
||||
# For simplicity, we assume Sigul can access Koji's storage directly or via URL
|
||||
full_rpm_path = f"{self.hub.config.get('KojiDir')}/{rpm_path}"
|
||||
|
||||
try:
|
||||
# Call Sigul to sign the RPM
|
||||
# This is a mock call. A real Sigul API call would be more complex.
|
||||
logger.info(f"Calling Sigul to sign {full_rpm_path}")
|
||||
# result = self.sigul_proxy.sign_rpm(full_rpm_path) # Example Sigul API call
|
||||
result = {'success': True, 'signature': 'mock_signature_data'} # Mock result
|
||||
|
||||
if result.get('success'):
|
||||
logger.info(f"Successfully signed RPM {rpm_nvr}. Importing signature to Koji.")
|
||||
# Import signature back into Koji
|
||||
# This is a simplified example. Koji's import-sig expects specific data.
|
||||
# For a real plugin, you'd get the actual signature from Sigul and format it.
|
||||
self.hub.importSig(rpm_id, 'mock_sig_id', 'mock_sig_type', result['signature'])
|
||||
logger.info(f"Signature imported for {rpm_nvr}.")
|
||||
else:
|
||||
logger.error(f"Sigul failed to sign {rpm_nvr}: {result.get('error', 'Unknown error')}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error signing RPM {rpm_nvr}: {e}")
|
||||
|
||||
# Register the plugin
|
||||
kojihub.add_plugin(SigulSignPlugin)
|
||||
spool_file = os.path.join(SPOOL_DIR, str(build_id))
|
||||
try:
|
||||
with open(spool_file, 'w') as f:
|
||||
f.write(f"{build_nvr}\n")
|
||||
logger.info(f"Queued build {build_id} ({build_nvr}) for signing in {spool_file}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to queue build {build_id} for signing: {e}")
|
||||
|
|
@ -9,3 +9,8 @@
|
|||
ansible.builtin.service:
|
||||
name: firewalld
|
||||
state: reloaded
|
||||
|
||||
- name: Restart koji-sign-spooler
|
||||
ansible.builtin.service:
|
||||
name: koji-sign-spooler
|
||||
state: restarted
|
||||
|
|
|
|||
|
|
@ -304,6 +304,52 @@
|
|||
mode: "0644"
|
||||
notify: Restart httpd
|
||||
|
||||
- name: Copy Sigul signing plugin to Koji Hub plugins directory
|
||||
ansible.builtin.copy:
|
||||
src: sigul_sign.py
|
||||
dest: /usr/lib/koji-hub-plugins/sigul_sign.py
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart httpd
|
||||
|
||||
- name: Create spool directories for Koji signing
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: apache
|
||||
group: apache
|
||||
mode: "0770"
|
||||
loop:
|
||||
- /var/spool/koji-sign
|
||||
- /var/spool/koji-sign/queue
|
||||
- /var/spool/koji-sign/failed
|
||||
|
||||
- name: Copy Koji sign spooler daemon to libexec
|
||||
ansible.builtin.copy:
|
||||
src: koji-sign-spooler
|
||||
dest: /usr/libexec/koji-sign-spooler
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
notify: Restart koji-sign-spooler
|
||||
|
||||
- name: Copy Koji sign spooler systemd service
|
||||
ansible.builtin.copy:
|
||||
src: koji-sign-spooler.service
|
||||
dest: /usr/lib/systemd/system/koji-sign-spooler.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart koji-sign-spooler
|
||||
|
||||
- name: Enable and start koji-sign-spooler service
|
||||
ansible.builtin.systemd:
|
||||
name: koji-sign-spooler
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
|
||||
- name: Template Koji callback script
|
||||
ansible.builtin.template:
|
||||
src: koji_callbacks.py.j2
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ DBUser = {{ koji_db_user }}
|
|||
DBHost = {{ koji_db_host }}
|
||||
DBPass = {{ koji_db_password }}
|
||||
KojiDir = {{ koji_top_dir }}
|
||||
AllowedSCMs = {{ koji_allowed_scms | default('git.mx-os.mx:/RPMs/*.git:*:use_common_checkout') }}
|
||||
|
||||
## Auth-related options ##
|
||||
# CheckClientIP = True
|
||||
|
|
@ -56,7 +57,8 @@ DisableNotifications = False
|
|||
|
||||
## Koji hub plugins
|
||||
PluginPath = /usr/lib/koji-hub-plugins
|
||||
Plugins = echo
|
||||
Plugins = echo sigul_sign
|
||||
SigulServer = {{ sigul_server }}
|
||||
|
||||
## Debugging
|
||||
KojiDebug = On
|
||||
|
|
|
|||
|
|
@ -36,6 +36,13 @@
|
|||
groups: mock
|
||||
state: present
|
||||
|
||||
- name: Ensure system user kojid is in the mock group
|
||||
ansible.builtin.user:
|
||||
name: kojid
|
||||
groups: mock
|
||||
append: true
|
||||
state: present
|
||||
|
||||
- name: Ensure /etc/mock/koji has correct permissions
|
||||
ansible.builtin.file:
|
||||
path: /etc/mock/koji
|
||||
|
|
@ -107,6 +114,15 @@
|
|||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Configure global Mock bind mount for get-sources script
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/mock/site-defaults.cfg
|
||||
block: |
|
||||
# Bind mount the MxOS source fetching wrapper globally
|
||||
config_opts['plugin_conf']['bind_mount_enable'] = True
|
||||
config_opts['plugin_conf']['bind_mount_opts'] = {'dirs': [('/usr/local/bin/mxos-get-sources.bash', '/usr/local/bin/mxos-get-sources.bash')]}
|
||||
marker: "# {mark} ANSIBLE MANAGED BLOCK: MXOS BIND MOUNT"
|
||||
|
||||
- name: Configure MxOS Mock config
|
||||
ansible.builtin.template:
|
||||
src: mxos0-build-x86_64.cfg.j2
|
||||
|
|
@ -125,3 +141,4 @@
|
|||
name: kojid
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
- name: Reload firewalld
|
||||
ansible.builtin.service:
|
||||
name: firewalld
|
||||
state: reloaded
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
---
|
||||
- name: Install Python and Flask
|
||||
ansible.builtin.dnf:
|
||||
name:
|
||||
- python3
|
||||
- python3-flask
|
||||
state: present
|
||||
|
||||
- name: Create mock Sigul server directory
|
||||
ansible.builtin.file:
|
||||
path: /opt/mock-sigul-server
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Create mock Sigul server Python script
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
from flask import Flask, request, jsonify
|
||||
import logging
|
||||
import os
|
||||
|
||||
app = Flask(__name__)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
@app.route('/sigul', methods=['POST'])
|
||||
def sigul_endpoint():
|
||||
app.logger.info(f"Received request: {request.json}")
|
||||
# Simulate successful signing
|
||||
return jsonify({"success": True, "signature": "mock_signature_data_from_sigul"})
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=8080)
|
||||
dest: /opt/mock-sigul-server/app.py
|
||||
mode: "0644"
|
||||
|
||||
- name: Create systemd service file for mock Sigul server
|
||||
ansible.builtin.template:
|
||||
src: mock-sigul-server.service.j2
|
||||
dest: /etc/systemd/system/mock-sigul-server.service
|
||||
mode: "0644"
|
||||
|
||||
- name: Start and enable mock Sigul server service
|
||||
ansible.builtin.systemd:
|
||||
name: mock-sigul-server
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
|
||||
- name: Open firewall for mock Sigul server (port 8080)
|
||||
ansible.posix.firewalld:
|
||||
port: 8080/tcp
|
||||
permanent: true
|
||||
state: enabled
|
||||
notify: Reload firewalld
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
[Unit]
|
||||
Description=Mock Sigul Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/opt/mock-sigul-server
|
||||
ExecStart=/usr/bin/python3 /opt/mock-sigul-server/app.py
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
266
roles/siguldry/tasks/main.yaml
Normal file
266
roles/siguldry/tasks/main.yaml
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
---
|
||||
- name: "Generate: Siguldry Certificates on Localhost"
|
||||
delegate_to: localhost
|
||||
run_once: true
|
||||
block:
|
||||
- name: "Generate Siguldry CA private key"
|
||||
community.crypto.openssl_privatekey:
|
||||
path: "{{ koji_pki_dir }}/siguldry_ca.key"
|
||||
size: 4096
|
||||
mode: "0600"
|
||||
|
||||
- name: "Generate Siguldry CA CSR"
|
||||
community.crypto.openssl_csr:
|
||||
path: "{{ koji_pki_dir }}/siguldry_ca.csr"
|
||||
privatekey_path: "{{ koji_pki_dir }}/siguldry_ca.key"
|
||||
common_name: "Siguldry Certificate Authority"
|
||||
organization_name: "MxOS Siguldry"
|
||||
basic_constraints:
|
||||
- "CA:TRUE"
|
||||
key_usage:
|
||||
- keyCertSign
|
||||
- cRLSign
|
||||
|
||||
- name: "Generate self-signed Siguldry CA certificate"
|
||||
community.crypto.x509_certificate:
|
||||
path: "{{ koji_pki_dir }}/siguldry_ca.crt"
|
||||
csr_path: "{{ koji_pki_dir }}/siguldry_ca.csr"
|
||||
privatekey_path: "{{ koji_pki_dir }}/siguldry_ca.key"
|
||||
provider: selfsigned
|
||||
selfsigned_not_after: "+3650d"
|
||||
|
||||
- name: "Generate private key for Siguldry Server"
|
||||
community.crypto.openssl_privatekey:
|
||||
path: "{{ koji_pki_dir }}/siguldry-server.key"
|
||||
size: 4096
|
||||
mode: "0600"
|
||||
|
||||
- name: "Generate CSR for Siguldry Server"
|
||||
community.crypto.openssl_csr:
|
||||
path: "{{ koji_pki_dir }}/siguldry-server.csr"
|
||||
privatekey_path: "{{ koji_pki_dir }}/siguldry-server.key"
|
||||
common_name: "{{ koji_hub_fqdn }}"
|
||||
|
||||
- name: Create openssl config for Siguldry Server
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ koji_pki_dir }}/openssl-siguldry-server.cnf"
|
||||
mode: "0644"
|
||||
content: |
|
||||
[v3_ca]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = serverAuth, clientAuth
|
||||
subjectAltName = DNS:{{ koji_hub_fqdn }},DNS:server.example.com
|
||||
|
||||
- name: Sign Siguldry Server certificate
|
||||
ansible.builtin.shell:
|
||||
cmd: >
|
||||
openssl x509 -req
|
||||
-in {{ koji_pki_dir }}/siguldry-server.csr
|
||||
-CA {{ koji_pki_dir }}/siguldry_ca.crt
|
||||
-CAkey {{ koji_pki_dir }}/siguldry_ca.key
|
||||
-CAcreateserial
|
||||
-out {{ koji_pki_dir }}/siguldry-server.crt
|
||||
-days 3650
|
||||
-sha256
|
||||
-extfile {{ koji_pki_dir }}/openssl-siguldry-server.cnf
|
||||
-extensions v3_ca
|
||||
creates: "{{ koji_pki_dir }}/siguldry-server.crt"
|
||||
|
||||
- name: "Generate private key for Siguldry Bridge"
|
||||
community.crypto.openssl_privatekey:
|
||||
path: "{{ koji_pki_dir }}/siguldry-bridge.key"
|
||||
size: 4096
|
||||
mode: "0600"
|
||||
|
||||
- name: "Generate CSR for Siguldry Bridge"
|
||||
community.crypto.openssl_csr:
|
||||
path: "{{ koji_pki_dir }}/siguldry-bridge.csr"
|
||||
privatekey_path: "{{ koji_pki_dir }}/siguldry-bridge.key"
|
||||
common_name: "{{ koji_hub_fqdn }}"
|
||||
|
||||
- name: Create openssl config for Siguldry Bridge
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ koji_pki_dir }}/openssl-siguldry-bridge.cnf"
|
||||
mode: "0644"
|
||||
content: |
|
||||
[v3_ca]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = DNS:{{ koji_hub_fqdn }},DNS:bridge.example.com
|
||||
|
||||
- name: Sign Siguldry Bridge certificate
|
||||
ansible.builtin.shell:
|
||||
cmd: >
|
||||
openssl x509 -req
|
||||
-in {{ koji_pki_dir }}/siguldry-bridge.csr
|
||||
-CA {{ koji_pki_dir }}/siguldry_ca.crt
|
||||
-CAkey {{ koji_pki_dir }}/siguldry_ca.key
|
||||
-CAcreateserial
|
||||
-out {{ koji_pki_dir }}/siguldry-bridge.crt
|
||||
-days 3650
|
||||
-sha256
|
||||
-extfile {{ koji_pki_dir }}/openssl-siguldry-bridge.cnf
|
||||
-extensions v3_ca
|
||||
creates: "{{ koji_pki_dir }}/siguldry-bridge.crt"
|
||||
|
||||
- name: "Generate private key for Siguldry Client"
|
||||
community.crypto.openssl_privatekey:
|
||||
path: "{{ koji_pki_dir }}/siguldry-client.key"
|
||||
size: 4096
|
||||
mode: "0600"
|
||||
|
||||
- name: "Generate CSR for Siguldry Client"
|
||||
community.crypto.openssl_csr:
|
||||
path: "{{ koji_pki_dir }}/siguldry-client.csr"
|
||||
privatekey_path: "{{ koji_pki_dir }}/siguldry-client.key"
|
||||
common_name: "kojiadmin"
|
||||
|
||||
- name: Create openssl config for Siguldry Client
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ koji_pki_dir }}/openssl-siguldry-client.cnf"
|
||||
mode: "0644"
|
||||
content: |
|
||||
[v3_ca]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = clientAuth
|
||||
subjectAltName = DNS:kojiadmin
|
||||
|
||||
- name: Sign Siguldry Client certificate
|
||||
ansible.builtin.shell:
|
||||
cmd: >
|
||||
openssl x509 -req
|
||||
-in {{ koji_pki_dir }}/siguldry-client.csr
|
||||
-CA {{ koji_pki_dir }}/siguldry_ca.crt
|
||||
-CAkey {{ koji_pki_dir }}/siguldry_ca.key
|
||||
-CAcreateserial
|
||||
-out {{ koji_pki_dir }}/siguldry-client.crt
|
||||
-days 3650
|
||||
-sha256
|
||||
-extfile {{ koji_pki_dir }}/openssl-siguldry-client.cnf
|
||||
-extensions v3_ca
|
||||
creates: "{{ koji_pki_dir }}/siguldry-client.crt"
|
||||
|
||||
- name: Clean up temporary openssl configs
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- "{{ koji_pki_dir }}/openssl-siguldry-server.cnf"
|
||||
- "{{ koji_pki_dir }}/openssl-siguldry-bridge.cnf"
|
||||
- "{{ koji_pki_dir }}/openssl-siguldry-client.cnf"
|
||||
|
||||
- name: Install Siguldry package
|
||||
ansible.builtin.dnf:
|
||||
name:
|
||||
- siguldry
|
||||
- siguldry-pkcs11
|
||||
- gnupg-pkcs11-scd
|
||||
state: present
|
||||
|
||||
- name: Ensure Siguldry config directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/siguldry
|
||||
state: directory
|
||||
owner: root
|
||||
group: siguldry
|
||||
mode: "0750"
|
||||
|
||||
- name: Deploy Siguldry configuration files
|
||||
ansible.builtin.template:
|
||||
src: "{{ item.src }}"
|
||||
dest: "/etc/siguldry/{{ item.dest }}"
|
||||
owner: root
|
||||
group: siguldry
|
||||
mode: "0640"
|
||||
loop:
|
||||
- { src: "server.toml.j2", dest: "server.toml" }
|
||||
- { src: "bridge.toml.j2", dest: "bridge.toml" }
|
||||
- { src: "client.toml.j2", dest: "client.toml" }
|
||||
|
||||
- name: Ensure systemd credstore directory exists
|
||||
ansible.builtin.file:
|
||||
path: /etc/credstore
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0700"
|
||||
|
||||
- name: Deploy Siguldry TLS credentials to systemd credstore
|
||||
ansible.builtin.copy:
|
||||
src: "{{ koji_pki_dir }}/{{ item.src }}"
|
||||
dest: "/etc/credstore/{{ item.dest }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
loop:
|
||||
- { src: "siguldry_ca.crt", dest: "siguldry.ca_certificate.pem" }
|
||||
- { src: "siguldry-server.crt", dest: "siguldry.server.certificate.pem" }
|
||||
- { src: "siguldry-server.key", dest: "siguldry.server.private_key.pem" }
|
||||
- { src: "siguldry-bridge.crt", dest: "siguldry.bridge.certificate.pem" }
|
||||
- { src: "siguldry-bridge.key", dest: "siguldry.bridge.private_key.pem" }
|
||||
- { src: "siguldry-client.crt", dest: "siguldry.client.certificate.pem" }
|
||||
- { src: "siguldry-client.key", dest: "siguldry.client.private_key.pem" }
|
||||
|
||||
- name: Deploy Siguldry signing key passphrase to systemd credstore
|
||||
ansible.builtin.copy:
|
||||
content: "{{ gpg_key_passphrase }}"
|
||||
dest: /etc/credstore/siguldry.signing_key.passphrase
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0600"
|
||||
|
||||
- name: Start and enable Siguldry services
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
loop:
|
||||
- siguldry-bridge.service
|
||||
- siguldry-server.service
|
||||
- siguldry-client-proxy.socket
|
||||
- siguldry-signer.socket
|
||||
|
||||
- name: Run Siguldry database migrations
|
||||
become: true
|
||||
become_user: siguldry
|
||||
ansible.builtin.command:
|
||||
cmd: siguldry-server -c /etc/siguldry/server.toml manage migrate
|
||||
register: siguldry_migration_result
|
||||
changed_when: "'No migrations to apply' not in siguldry_migration_result.stdout"
|
||||
|
||||
- name: Check if remote user kojiadmin exists in Siguldry
|
||||
become: true
|
||||
become_user: siguldry
|
||||
ansible.builtin.shell:
|
||||
cmd: siguldry-server -c /etc/siguldry/server.toml manage users list
|
||||
register: siguldry_users_list
|
||||
changed_when: false
|
||||
|
||||
- name: Add remote user kojiadmin to Siguldry database
|
||||
become: true
|
||||
become_user: siguldry
|
||||
ansible.builtin.command:
|
||||
cmd: siguldry-server -c /etc/siguldry/server.toml manage users create kojiadmin
|
||||
when: "'kojiadmin' not in siguldry_users_list.stdout"
|
||||
|
||||
- name: Check if mxos-signing-key exists in Siguldry
|
||||
become: true
|
||||
become_user: siguldry
|
||||
ansible.builtin.shell:
|
||||
cmd: siguldry-server -c /etc/siguldry/server.toml manage key list
|
||||
register: siguldry_keys_list
|
||||
changed_when: false
|
||||
|
||||
- name: Create mxos-signing-key in Siguldry
|
||||
become: true
|
||||
become_user: siguldry
|
||||
ansible.builtin.shell:
|
||||
cmd: >
|
||||
siguldry-server -c /etc/siguldry/server.toml manage key create
|
||||
kojiadmin mxos-signing-key
|
||||
--password-file <(echo "{{ gpg_key_passphrase }}")
|
||||
when: "'mxos-signing-key' not in siguldry_keys_list.stdout"
|
||||
8
roles/siguldry/templates/bridge.toml.j2
Normal file
8
roles/siguldry/templates/bridge.toml.j2
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Siguldry bridge configuration for MxOS Koji
|
||||
server_listening_address = "[::]:44333"
|
||||
client_listening_address = "[::]:44334"
|
||||
|
||||
[credentials]
|
||||
private_key = "siguldry.bridge.private_key.pem"
|
||||
certificate = "siguldry.bridge.certificate.pem"
|
||||
ca_certificate = "siguldry.ca_certificate.pem"
|
||||
17
roles/siguldry/templates/client.toml.j2
Normal file
17
roles/siguldry/templates/client.toml.j2
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Siguldry client configuration for MxOS Koji
|
||||
server_hostname = "{{ koji_hub_fqdn }}"
|
||||
bridge_hostname = "{{ koji_hub_fqdn }}"
|
||||
bridge_port = 44334
|
||||
|
||||
[[keys]]
|
||||
key_name = "mxos-signing-key"
|
||||
passphrase_path = "siguldry.signing_key.passphrase"
|
||||
|
||||
[request_timeout]
|
||||
secs = 30
|
||||
nanos = 0
|
||||
|
||||
[credentials]
|
||||
private_key = "siguldry.client.private_key.pem"
|
||||
certificate = "siguldry.client.certificate.pem"
|
||||
ca_certificate = "siguldry.ca_certificate.pem"
|
||||
20
roles/siguldry/templates/server.toml.j2
Normal file
20
roles/siguldry/templates/server.toml.j2
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Siguldry server configuration for MxOS Koji
|
||||
state_directory = "/var/lib/siguldry/"
|
||||
bridge_hostname = "{{ koji_hub_fqdn }}"
|
||||
bridge_port = 44333
|
||||
connection_pool_size = 32
|
||||
user_password_length = 8
|
||||
openpgp_user_id = "MxOS Signing Key <koji@{{ koji_hub_fqdn }}>"
|
||||
pkcs11_bindings = []
|
||||
|
||||
[credentials]
|
||||
private_key = "siguldry.server.private_key.pem"
|
||||
certificate = "siguldry.server.certificate.pem"
|
||||
ca_certificate = "siguldry.ca_certificate.pem"
|
||||
|
||||
[certificate_subject]
|
||||
country = "MX"
|
||||
state_or_province = "Mexico"
|
||||
locality = "Mexico City"
|
||||
organization = "MxOS"
|
||||
organizational_unit = "Infraestructura"
|
||||
Loading…
Reference in a new issue