├── hub ├── root │ └── .pgpass ├── etc │ ├── httpd │ │ └── conf.d │ │ │ ├── servername.conf │ │ │ ├── test-accessories.conf │ │ │ ├── kojihub.conf │ │ │ ├── kojiweb.conf │ │ │ └── ssl.conf │ ├── sudoers.d │ │ └── apache │ ├── kojiweb │ │ └── web.conf │ ├── pki │ │ └── koji │ │ │ └── ssl.cnf │ └── koji-hub │ │ └── hub.conf ├── docker-scripts │ ├── stop.sh │ ├── build.sh │ ├── start.sh │ └── run.sh ├── bin │ ├── find-ip.py │ ├── entrypoint.sh │ ├── build-koji.sh │ ├── setup.sh │ └── mkuser.sh ├── cgi │ ├── debug-conf.py │ ├── test.py │ ├── content.py │ └── setup.py └── Dockerfile ├── client ├── docker-scripts │ ├── build.sh │ └── run.sh ├── bin │ ├── find-ip.py │ └── entrypoint.sh └── Dockerfile ├── push-all.sh ├── openshift ├── pv-koji-volume.yaml ├── pv-koji-clients-volume.yaml ├── README.md └── koji.yaml ├── docker-compose.yml ├── research └── build-init │ └── relevant-koji-snippets.py └── README.md /hub/root/.pgpass: -------------------------------------------------------------------------------- 1 | koji-db:5432:koji:koji:mypassword 2 | -------------------------------------------------------------------------------- /hub/etc/httpd/conf.d/servername.conf: -------------------------------------------------------------------------------- 1 | ServerName kojihub.local:80 -------------------------------------------------------------------------------- /hub/etc/sudoers.d/apache: -------------------------------------------------------------------------------- 1 | Defaults:apache !requiretty 2 | 3 | apache ALL=(ALL) NOPASSWD: ALL 4 | -------------------------------------------------------------------------------- /hub/docker-scripts/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker stop koji-hub koji-db 4 | docker rm koji-hub koji-db 5 | -------------------------------------------------------------------------------- /hub/docker-scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(dirname $(dirname $(realpath $0))) 4 | 5 | set -x 6 | docker build --tag=docker.io/buildchimp/koji-dojo-hub:dev $DIR 7 | -------------------------------------------------------------------------------- /client/docker-scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR=$(dirname $(dirname $(realpath $0))) 4 | 5 | set -x 6 | docker build --tag=docker.io/buildchimp/koji-dojo-client:dev $DIR 7 | -------------------------------------------------------------------------------- /push-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker login -u buildchimp 4 | for c in docker.io/buildchimp/koji-dojo-hub:dev docker.io/buildchimp/koji-dojo-client:dev docker.io/buildchimp/koji-dojo-builder:dev; do 5 | echo "Pushing: $c" 6 | docker push $c 7 | done -------------------------------------------------------------------------------- /openshift/pv-koji-volume.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: koji-volume 5 | spec: 6 | capacity: 7 | storage: 1Gi 8 | accessModes: 9 | - ReadWriteMany 10 | persistentVolumeReclaimPolicy: Retain 11 | hostPath: 12 | path: /volumes/koji-volume 13 | -------------------------------------------------------------------------------- /openshift/pv-koji-clients-volume.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: koji-clients-volume 5 | spec: 6 | capacity: 7 | storage: 1Gi 8 | accessModes: 9 | - ReadWriteMany 10 | persistentVolumeReclaimPolicy: Retain 11 | hostPath: 12 | path: /volumes/koji-clients-volume 13 | -------------------------------------------------------------------------------- /hub/docker-scripts/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -d temp ]; then 4 | mkdir temp 5 | fi 6 | 7 | #docker run -d --name=koji-db docker.io/buildchimp/koji-db 8 | docker run -d --name=koji-db -e POSTGRES_DB='koji' -e POSTGRES_USER='koji' -e POSTGRES_PASSWORD='mypassword' postgres:9.4 9 | 10 | docker run -d --name=koji-hub -v temp:/koji-clients --link koji-db:koji-db docker.io/buildchimp/koji-dojo-hub:dev 11 | -------------------------------------------------------------------------------- /client/docker-scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker run -d --name=koji-db -e POSTGRES_DB='koji' -e POSTGRES_USER='koji' -e POSTGRES_PASSWORD='mypassword' postgres:9.4 4 | 5 | docker run -d --name=koji-hub -v /opt/koji:/opt/koji -v /opt/koji-clients:/opt/koji-clients --link koji-db docker.io/buildchimp/koji-dojo-hub:dev 6 | 7 | docker run -ti --rm --name=koji-client --volumes-from koji-hub --link koji-hub docker.io/buildchimp/koji-dojo-client:dev 8 | 9 | docker stop koji-hub koji-db 10 | docker rm koji-hub koji-db 11 | -------------------------------------------------------------------------------- /hub/bin/find-ip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import sys 4 | import socket 5 | import fcntl 6 | import struct 7 | 8 | if len(sys.argv) > 1: 9 | ifc = sys.argv[1] 10 | if ifc.endswith(':'): 11 | ifc = ifc[0:-1] 12 | else: 13 | ifc='eth0' 14 | 15 | try: 16 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 17 | ip = socket.inet_ntoa(fcntl.ioctl( 18 | s.fileno(), 19 | 0x8915, # SIOCGIFADDR 20 | struct.pack('256s', ifc[:15]) 21 | )[20:24]) 22 | except: 23 | print "Cannot find IP address for:", ifc 24 | sys.exit(127) 25 | 26 | print str(ip) 27 | -------------------------------------------------------------------------------- /client/bin/find-ip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import sys 4 | import socket 5 | import fcntl 6 | import struct 7 | 8 | if len(sys.argv) > 1: 9 | ifc = sys.argv[1] 10 | if ifc.endswith(':'): 11 | ifc = ifc[0:-1] 12 | else: 13 | ifc='eth0' 14 | 15 | try: 16 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 17 | ip = socket.inet_ntoa(fcntl.ioctl( 18 | s.fileno(), 19 | 0x8915, # SIOCGIFADDR 20 | struct.pack('256s', ifc[:15]) 21 | )[20:24]) 22 | except: 23 | print "Cannot find IP address for:", ifc 24 | sys.exit(127) 25 | 26 | print str(ip) 27 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | 2 | koji-db: 3 | image: "postgres:9.4" 4 | hostname: koji-db 5 | environment: 6 | - POSTGRES_DB=koji 7 | - POSTGRES_USER=koji 8 | - POSTGRES_PASSWORD=mypassword 9 | 10 | koji-hub: 11 | build: hub 12 | hostname: koji-hub 13 | volumes: 14 | - /opt/koji:/opt/koji:rw,z 15 | - /opt/koji-clients:/opt/koji-clients:rw,z 16 | links: 17 | - koji-db 18 | 19 | koji-client: 20 | build: client 21 | hostname: koji-client 22 | volumes_from: 23 | - koji-hub:rw 24 | links: 25 | - koji-hub 26 | 27 | -------------------------------------------------------------------------------- /hub/docker-scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -d /tmp/koji-clients ]; then 4 | mkdir -p /tmp/koji-clients 5 | fi 6 | 7 | if [ ! -d /tmp/koji ]; then 8 | mkdir -p /tmp/koji 9 | fi 10 | 11 | chcon -Rt svirt_sandbox_file_t /tmp/koji-clients 12 | chcon -Rt svirt_sandbox_file_t /tmp/koji 13 | 14 | docker run -d --name=koji-db -e POSTGRES_DB='koji' -e POSTGRES_USER='koji' -e POSTGRES_PASSWORD='mypassword' postgres:9.4 15 | 16 | docker run -ti --rm --name=koji-hub -v /tmp/koji:/opt/koji -v /tmp/koji-clients:/opt/koji-clients --link koji-db:koji-db docker.io/buildchimp/koji-dojo-hub:dev 17 | 18 | docker stop koji-db && docker rm koji-db 19 | -------------------------------------------------------------------------------- /hub/cgi/debug-conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*-# enable debugging 3 | import os 4 | import cgi 5 | import cgitb 6 | 7 | cgitb.enable() 8 | 9 | print "Content-Type: text/plain;charset=utf-8" 10 | print 11 | print """ 12 | [parameters] 13 | """ 14 | params = cgi.FieldStorage() 15 | for key in sorted(params.keys()): 16 | val = params[key] 17 | if isinstance(val, list) is True: 18 | for v in val: 19 | print "%s=%s\n" % (key, v.value) 20 | else: 21 | print "%s=%s\n" % (key, params[key].value) 22 | 23 | print """ 24 | 25 | [variables] 26 | """ 27 | for key in sorted(os.environ.iterkeys()): 28 | print "%s=%s\n" % (key, os.environ.get(key)) 29 | 30 | -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:centos7 2 | MAINTAINER John Casey 3 | 4 | #RUN sed -i '/excludedocs/d' /etc/rpm/macros.imgcreate 5 | RUN sed -i '/nodocs/d' /etc/yum.conf 6 | 7 | RUN yum -y update && \ 8 | yum -y install \ 9 | lsof \ 10 | python-simplejson \ 11 | openssh-server \ 12 | openssh-clients \ 13 | ; yum clean all 14 | 15 | ADD bin/ /usr/local/bin/ 16 | 17 | RUN chmod +x /usr/local/bin/* 18 | RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config 19 | RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd 20 | RUN mkdir /var/run/sshd 21 | RUN echo 'root:mypassword' | chpasswd 22 | 23 | ENTRYPOINT /usr/local/bin/entrypoint.sh 24 | -------------------------------------------------------------------------------- /hub/bin/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | # add koji-db to hosts if not present 5 | KOJI_DB="${KOJI_DB:-koji-db}" 6 | if grep -q -v "koji-db" /etc/hosts; then 7 | KOJI_DB_IP=$(getent hosts $KOJI_DB | awk '{ print $1 }') 8 | echo ${KOJI_DB_IP} koji-db >> /etc/hosts 9 | fi 10 | 11 | build-koji.sh || exit 1 12 | setup.sh || exit 2 13 | 14 | IP=$(find-ip.py) 15 | 16 | # add koji-hub to hosts if not present 17 | if grep -q -v "koji-hub" /etc/hosts; then echo ${IP} koji-hub >> /etc/hosts; fi 18 | 19 | echo "Starting ssh on ${IP} (use ssh root@${IP} with password mypassword" 20 | #/etc/init.d/sshd start 21 | /usr/sbin/sshd 22 | echo "You can connect directly by running" 23 | echo " docker exec -ti koji-hub /bin/bash" 24 | echo "Starting HTTPd on ${IP}" 25 | httpd -D FOREGROUND 26 | -------------------------------------------------------------------------------- /hub/cgi/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*-# enable debugging 3 | import os 4 | import cgi 5 | import cgitb 6 | 7 | cgitb.enable() 8 | 9 | print "Content-Type: text/html;charset=utf-8" 10 | print 11 | print """ 12 | 13 | 14 | Request Debug 15 | 18 | 19 | 20 |

Parameters

21 | """ 22 | params = cgi.FieldStorage() 23 | for key in sorted(params.keys()): 24 | print """ 25 |
%s
26 |
%s
27 | """ % (key, params[key].value) 28 | print """ 29 | """ 30 | print """ 31 |

Variables

32 |
""" 33 | for key in sorted(os.environ.iterkeys()): 34 | print """ 35 |
%s
36 |
%s
37 | """ % (key, os.environ.get(key)) 38 | print """ 39 |
40 | 41 | """ 42 | 43 | -------------------------------------------------------------------------------- /hub/etc/httpd/conf.d/test-accessories.conf: -------------------------------------------------------------------------------- 1 | Alias /clients /opt/koji-clients 2 | 3 | 4 | Options Indexes 5 | 6 | Order allow,deny 7 | Allow from all 8 | 9 | = 2.4> 10 | Require all granted 11 | 12 | 13 | 14 | Alias /httpd /etc/httpd 15 | 16 | 17 | Options Indexes 18 | 19 | Order allow,deny 20 | Allow from all 21 | 22 | = 2.4> 23 | Require all granted 24 | 25 | 26 | 27 | Alias /logs /var/log 28 | 29 | 30 | Options Indexes 31 | 32 | Order allow,deny 33 | Allow from all 34 | 35 | = 2.4> 36 | Require all granted 37 | 38 | 39 | -------------------------------------------------------------------------------- /hub/etc/kojiweb/web.conf: -------------------------------------------------------------------------------- 1 | [web] 2 | SiteName = koji 3 | #KojiTheme = mytheme 4 | 5 | # Key urls 6 | KojiHubURL = http://koji-hub/kojihub 7 | KojiFilesURL = http://koji-hub/kojifiles 8 | 9 | # Kerberos authentication options 10 | # WebPrincipal = koji/web@EXAMPLE.COM 11 | # WebKeytab = /etc/httpd.keytab 12 | # WebCCache = /var/tmp/kojiweb.ccache 13 | # The service name of the principal being used by the hub 14 | # KrbService = host 15 | 16 | # SSL authentication options 17 | WebCert = /etc/pki/koji/certs/kojiweb.crt 18 | ClientCA = /etc/pki/koji/koji_ca_cert.crt 19 | KojiHubCA = /etc/pki/koji/koji_ca_cert.crt 20 | 21 | LoginTimeout = 72 22 | 23 | # This must be changed and uncommented before deployment 24 | Secret = SUPER_SECRET 25 | 26 | LibPath = /usr/share/koji-web/lib 27 | 28 | # If set to True, then the footer will be included literally. 29 | # If False, then the footer will be included as another Kid Template. 30 | # Defaults to True 31 | LiteralFooter = True 32 | 33 | -------------------------------------------------------------------------------- /openshift/README.md: -------------------------------------------------------------------------------- 1 | Prerequisites on running koji on Openshift: 2 | 3 | * Import the template 4 | 5 | * Create a service account should be able to run container as root. 6 | 7 | Run the following commands on master: 8 | ``` 9 | $ sudo su 10 | # oc project 11 | # oc create serviceaccount koji-sa 12 | # oc patch scc anyuid --type=json -p '[{"op": "add", "path": "/users/0", "value":"system:serviceaccount::koji-sa"}]' 13 | ``` 14 | (replace `` with expected namespace) 15 | 16 | * Ask the cluster admin to create koji-volume and koji-clients-volume volumes in this namespace 17 | 18 | For local testing: 19 | ``` 20 | $ sudo su 21 | # mkdir -p /volumes/koji-volume /volumes/koji-clients-volume 22 | # chown nobody:nobody /volumes/koji-volume /volumes/koji-clients-volume 23 | # chcon -u system_u -r object_r -t svirt_sandbox_file_t -l s0 /volumes/koji-volume /volumes/koji-clients-volume 24 | # oc project 25 | # oc create -f openshift/pv-koji-volume.yaml 26 | # oc create -f openshift/pv-koji-clients-volume.yaml 27 | ``` 28 | (replace `` with expected namespace) 29 | 30 | * Start 'hub' build: 31 | ``` 32 | oc start-build hub 33 | ``` 34 | -------------------------------------------------------------------------------- /hub/bin/build-koji.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | if [ ! -d "/opt/koji/.git" ]; then 6 | # allow building from other locations / branches. 7 | GIT_URL=${GIT_URL:-https://pagure.io/koji.git} 8 | GIT_BRANCH=${GIT_BRANCH:-master} 9 | 10 | git clone --branch ${GIT_BRANCH} --verbose --progress ${GIT_URL} /opt/koji 2>&1 11 | fi 12 | 13 | # install the latest version of python-coverage module 14 | wget https://bootstrap.pypa.io/ez_setup.py 15 | python ez_setup.py 16 | wget https://pypi.python.org/packages/2d/10/6136c8e10644c16906edf4d9f7c782c0f2e7ed47ff2f41f067384e432088/coverage-4.1.tar.gz#md5=80e63edaf49f689d304898fafc1007a5 17 | easy_install coverage-4.1.tar.gz 18 | rm -f coverage-4.1.tar.gz 19 | 20 | cd /opt/koji 21 | # Remove previous build to avoid multilib errors. 22 | rm -rf noarch 23 | make test-rpm 24 | 25 | yum -y localinstall noarch/koji-hub*.rpm noarch/koji-1.*.rpm noarch/koji-web*.rpm noarch/python2-koji*.rpm 26 | 27 | echo "Sleep 10s in case database container is still booting..." 28 | sleep 10 29 | echo "...resuming install" 30 | 31 | psql="psql --host=koji-db --username=koji koji" 32 | 33 | cat /opt/koji/docs/schema.sql | $psql 34 | echo "BEGIN WORK; INSERT INTO content_generator(name) VALUES('test-cg'); COMMIT WORK;" | $psql 35 | -------------------------------------------------------------------------------- /hub/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:centos7 2 | MAINTAINER John Casey 3 | 4 | #RUN sed -i '/excludedocs/d' /etc/rpm/macros.imgcreate 5 | RUN sed -i '/nodocs/d' /etc/yum.conf 6 | 7 | VOLUME ["/opt/koji-clients", "/opt/koji"] 8 | 9 | RUN yum -y update && \ 10 | # yum install -y http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm && \ 11 | yum -y install \ 12 | epel-release \ 13 | git \ 14 | yum-utils \ 15 | tar \ 16 | bzip2 \ 17 | rpm-build \ 18 | make \ 19 | patch \ 20 | httpd \ 21 | mod_wsgi \ 22 | mod_ssl \ 23 | lsof \ 24 | python-simplejson \ 25 | PyGreSQL \ 26 | pyOpenSSL \ 27 | python-backports \ 28 | python-backports-ssl_match_hostname \ 29 | python-cheetah \ 30 | python-coverage \ 31 | python-dateutil \ 32 | python-devel \ 33 | python-kerberos \ 34 | python-krbV \ 35 | python-qpid \ 36 | python-saslwrapper \ 37 | saslwrapper \ 38 | postgresql \ 39 | sudo \ 40 | mod_auth_kerb \ 41 | python-cheetah \ 42 | python-markdown \ 43 | python-pygments \ 44 | python-setuptools \ 45 | python-sphinx \ 46 | python-coverage \ 47 | openssh-server \ 48 | wget \ 49 | ; yum clean all 50 | 51 | ADD etc/ /etc/ 52 | ADD bin/ /usr/local/bin/ 53 | ADD root/ /root/ 54 | 55 | RUN chmod 600 /root/.pgpass 56 | RUN chmod +x /usr/local/bin/* 57 | 58 | ADD cgi/*.py /var/www/cgi-bin/ 59 | RUN chmod o+rx /var/log /var/log/httpd 60 | RUN chmod +x /var/www/cgi-bin/*.py 61 | RUN chmod o+rwx /var/www/html 62 | RUN chmod -R o+rx /etc/httpd 63 | 64 | RUN mkdir -p /mnt/koji/{packages,repos,work,scratch} 65 | RUN chown apache.apache /mnt/koji/* 66 | RUN echo 'root:mypassword' | chpasswd 67 | 68 | EXPOSE 22 80 443 69 | 70 | ENTRYPOINT /usr/local/bin/entrypoint.sh 71 | -------------------------------------------------------------------------------- /client/bin/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | # add koji-hub to hosts if not present 5 | if [ -n "$KOJI_HUB" ]; then 6 | KOJI_HUB_IP=$(getent hosts $KOJI_HUB | awk '{ print $1 }') 7 | echo ${KOJI_HUB_IP} koji-hub >> /etc/hosts 8 | fi 9 | 10 | while true; do 11 | echo "Waiting for koji-hub to start..." 12 | hubstart=$(curl -X GET http://koji-hub/) 13 | echo $hubstart 14 | if [ "x$hubstart" != "x" ]; then 15 | echo "koji-hub started:" 16 | break 17 | fi 18 | sleep 5 19 | done 20 | 21 | set -x 22 | 23 | if [ -d /opt/koji/noarch ]; then 24 | yum -y localinstall /opt/koji/noarch/koji-1*.rpm /opt/koji/noarch/python2-koji-1* 25 | else 26 | echo "No koji RPM to install! Installing from EPEL" 27 | #already in docker file 28 | # yum -y install epel-release 29 | yum -y install koji 30 | fi 31 | 32 | mkdir /root/{.koji,bin} 33 | echo "Generating user-specific koji client links and configs" 34 | for userdir in $(ls -d /opt/koji-clients/*); do 35 | user=$(basename $userdir) 36 | echo "Adding: ${user} (${userdir})" 37 | 38 | cat <> /root/.koji/config 39 | [koji-${user}] 40 | server = https://koji-hub/kojihub 41 | authtype = ssl 42 | cert = ${userdir}/client.crt 43 | ca = ${userdir}/clientca.crt 44 | serverca = ${userdir}/serverca.crt 45 | 46 | EOF 47 | 48 | echo "Linking koji to: /root/bin/koji-${user}" 49 | ln -s /usr/bin/koji /root/bin/koji-${user} 50 | done 51 | 52 | echo "Generated configs are:" 53 | cat /root/.koji/config 54 | 55 | echo "Available user-specific koji commands are:" 56 | ls -1 /root/bin/koji-* 57 | 58 | ssh-keygen -t rsa -N '' -f /etc/ssh/ssh_host_rsa_key 59 | ssh-keygen -t dsa -N '' -f /etc/ssh/ssh_host_dsa_key 60 | 61 | IP=$(find-ip.py) 62 | 63 | #echo "SSHd listening on: ${IP}:22" 64 | #/usr/sbin/sshd -D 65 | 66 | echo "Koji client environment started on ${IP}" 67 | # This won't work in openshift 68 | if [ -n "$KOJI_HUB" ]; then 69 | while true; do sleep 10000; done 70 | else 71 | exec /bin/bash -l 72 | fi 73 | 74 | -------------------------------------------------------------------------------- /hub/etc/httpd/conf.d/kojihub.conf: -------------------------------------------------------------------------------- 1 | # 2 | # koji-hub is an xmlrpc interface to the Koji database 3 | # 4 | 5 | Alias /kojihub /usr/share/koji-hub/kojixmlrpc.py 6 | 7 | 8 | Options ExecCGI 9 | SetHandler wsgi-script 10 | 11 | Order allow,deny 12 | Allow from all 13 | 14 | = 2.4> 15 | Require all granted 16 | 17 | 18 | 19 | # Support for mod_python is DEPRECATED. If you still need mod_python support, 20 | # then use the following directory settings instead: 21 | # 22 | # 23 | # SetHandler mod_python 24 | # PythonHandler kojixmlrpc 25 | # PythonOption ConfigFile /etc/koji-hub/hub.conf 26 | # PythonDebug Off 27 | # PythonAutoReload Off 28 | # 29 | 30 | # Also serve /mnt/koji 31 | Alias /kojifiles "/mnt/koji/" 32 | 33 | 34 | Options Indexes SymLinksIfOwnerMatch 35 | #If your top /mnt/koji directory is not owned by the httpd user, then 36 | #you will need to follow all symlinks instead, e.g. 37 | #Options Indexes FollowSymLinks 38 | AllowOverride None 39 | 40 | Order allow,deny 41 | Allow from all 42 | 43 | = 2.4> 44 | Require all granted 45 | 46 | 47 | 48 | # uncomment this to enable authentication via SSL client certificates 49 | 50 | SSLVerifyClient require 51 | SSLVerifyDepth 1 52 | SSLOptions +StdEnvVars 53 | 54 | 55 | # If you need to support koji < 1.4.0 clients using SSL authentication, then use the following instead: 56 | # 57 | # SSLOptions +StdEnvVars 58 | # 59 | # In this case, you will need to enable these options globally (in ssl.conf): 60 | # SSLVerifyClient require 61 | # SSLVerifyDepth 10 62 | -------------------------------------------------------------------------------- /hub/etc/httpd/conf.d/kojiweb.conf: -------------------------------------------------------------------------------- 1 | #We use wsgi by default 2 | Alias /koji "/usr/share/koji-web/scripts/wsgi_publisher.py" 3 | #(configuration goes in /etc/kojiweb/web.conf) 4 | 5 | 6 | Options ExecCGI 7 | SetHandler wsgi-script 8 | 9 | Order allow,deny 10 | Allow from all 11 | 12 | = 2.4> 13 | Require all granted 14 | 15 | 16 | 17 | # Support for mod_python is DEPRECATED. If you still need mod_python support, 18 | # then use the following directory settings instead: 19 | # 20 | # 21 | # # Config for the publisher handler 22 | # SetHandler mod_python 23 | # # Use kojiweb's publisher (provides wsgi compat layer) 24 | # # mod_python's publisher is no longer supported 25 | # PythonHandler wsgi_publisher 26 | # PythonOption koji.web.ConfigFile /etc/kojiweb/web.conf 27 | # PythonAutoReload Off 28 | # # Configuration via PythonOptions is DEPRECATED. Use /etc/kojiweb/web.conf 29 | # Order allow,deny 30 | # Allow from all 31 | # 32 | 33 | # uncomment this to enable authentication via Kerberos 34 | # 35 | # AuthType Kerberos 36 | # AuthName "Koji Web UI" 37 | # KrbMethodNegotiate on 38 | # KrbMethodK5Passwd off 39 | # KrbServiceName HTTP 40 | # KrbAuthRealm EXAMPLE.COM 41 | # Krb5Keytab /etc/httpd.keytab 42 | # KrbSaveCredentials off 43 | # Require valid-user 44 | # ErrorDocument 401 /koji-static/errors/unauthorized.html 45 | # 46 | 47 | # uncomment this to enable authentication via SSL client certificates 48 | 49 | SSLVerifyClient require 50 | SSLVerifyDepth 10 51 | SSLOptions +StdEnvVars 52 | 53 | 54 | Alias /koji-static/ "/usr/share/koji-web/static/" 55 | 56 | 57 | Options None 58 | AllowOverride None 59 | 60 | Order allow,deny 61 | Allow from all 62 | 63 | = 2.4> 64 | Require all granted 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /hub/cgi/content.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright (C) 2015 John Casey (jdcasey@commonjava.org) 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | import os 19 | import sys 20 | 21 | BASE_PATH = '/var/www/html' 22 | 23 | method = os.environ.get('REQUEST_METHOD').lower() 24 | # sanity-check method 25 | if method is None or method not in ('put','delete'): 26 | status='error' 27 | 28 | else: 29 | path = os.environ.get('PATH_INFO').strip("/") 30 | filepath = os.path.join(BASE_PATH, path) 31 | 32 | sys.stderr.write("Path-Info: '%s'\nActual file: '%s'\nBase path: '%s'\n" % (path, filepath, BASE_PATH)) 33 | 34 | if method == 'put': 35 | status = 'updated' 36 | 37 | filedir = os.path.dirname(filepath) 38 | if not os.path.exists(filedir): 39 | os.makedirs(filedir) 40 | 41 | if not os.path.exists(filepath): 42 | status='new' 43 | 44 | content=sys.stdin.read() 45 | with open(filepath, 'w') as f: 46 | f.write(content) 47 | elif method == 'delete': 48 | if not os.path.exists(filepath): 49 | status='missing' 50 | else: 51 | os.remove(filepath) 52 | status='deleted' 53 | 54 | if status == 'error': 55 | print """Status: 400 56 | Content-Type: text/plain 57 | 58 | Only PUT and DELETE requests are supported. 59 | """ 60 | elif status == 'missing': 61 | print """Status: 404 62 | Content-Type: text/plain 63 | 64 | Path not found: /{path} 65 | """.format(path=path) 66 | elif status == 'new': 67 | print """Status: 201 68 | Content-Type: text/plain 69 | Location: /{path} 70 | 71 | Created /{path} 72 | """.format(path=path) 73 | elif status == 'deleted': 74 | print """Status: 204 75 | Content-Type: text/plain 76 | 77 | Deleted /{path} 78 | """.format(path=path) 79 | elif status == 'updated': 80 | print """Status: 200 81 | Content-Type: text/plain 82 | 83 | Modified /{path} 84 | """.format(path=path) 85 | else: 86 | print """Status: 500 87 | Content-Type: text/plain 88 | 89 | Unknown status: {status}. 90 | """.format(status=status) 91 | 92 | -------------------------------------------------------------------------------- /hub/bin/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | create_koji_folders() { 6 | echo "Create Koji folders" 7 | 8 | cd /mnt 9 | if [ ! -d koji ] 10 | then 11 | echo "Creating koji folder" 12 | mkdir koji 13 | fi 14 | cd koji 15 | echo "Creating koji folder structure" 16 | mkdir {packages,repos,work,scratch} 17 | chown apache.apache * 18 | } 19 | 20 | allow_read_logs() { 21 | # selinux is disabled 22 | # setsebool -P httpd_can_network_connect_db=1 allow_httpd_anon_write=1 23 | # chcon -R -t public_content_rw_t /mnt/koji/* 24 | 25 | chmod -R o+rx /var/log 26 | chmod -R g+rs /var/log 27 | chgrp -R nobody /var/log 28 | } 29 | 30 | generate_ssl_certificates() { 31 | echo "Generate SSL certificates" 32 | 33 | IP=$(find-ip.py || echo "kojihub.local") 34 | 35 | mkdir -p /etc/pki/koji/{certs,private,confs} 36 | 37 | cd /etc/pki/koji 38 | 39 | touch index.txt 40 | echo 01 > serial 41 | 42 | # CA 43 | conf=confs/ca.cnf 44 | CA_SAN="IP.1:${IP},DNS.1:localhost,DNS.2:${IP},DNS.3:koji-hub,email:move" 45 | if [ -n "$ADDITIONAL_SAN" ]; then 46 | CA_SAN="IP.1:${IP},DNS.1:localhost,DNS.2:${IP},DNS.3:${ADDITIONAL_SAN},DNS.4:koji-hub,email:move" 47 | fi 48 | 49 | cat ssl.cnf | sed "s/email\:move/${CA_SAN}/"> $conf 50 | cp ssl.cnf $conf 51 | 52 | openssl genrsa -out private/koji_ca_cert.key 2048 53 | openssl req -config $conf -new -x509 -subj "/C=US/ST=Drunken/L=Bed/O=IT/CN=koji-hub" -days 3650 -key private/koji_ca_cert.key -out koji_ca_cert.crt -extensions v3_ca 54 | 55 | cp private/koji_ca_cert.key private/kojihub.key 56 | cp koji_ca_cert.crt certs/kojihub.crt 57 | 58 | mkuser.sh kojiweb admin 59 | mkuser.sh kojiadmin admin 60 | mkuser.sh testadmin admin 61 | mkuser.sh testuser 62 | 63 | mkuser.sh kojibuilder builder 64 | 65 | 66 | chown -R nobody:nobody /opt/koji-clients 67 | } 68 | 69 | create_koji_config_for_root() { 70 | echo "Create /root/.koji/config for user root" 71 | 72 | mkdir /root/.koji 73 | 74 | cat <> /root/.koji/config 75 | [koji] 76 | server = https://localhost/kojihub 77 | authtype = ssl 78 | cert = /opt/koji-clients/kojiadmin/client.crt 79 | ca = /opt/koji-clients/kojiadmin/clientca.crt 80 | serverca = /opt/koji-clients/kojiadmin/serverca.crt 81 | EOF 82 | } 83 | 84 | if [ -d /mnt/koji/packages ] 85 | then 86 | echo "Koji folders exist" 87 | else 88 | create_koji_folders 89 | fi 90 | 91 | if [ -f /etc/pki/koji/certs/kojihub.crt ] 92 | then 93 | echo "Ssl certificates already generated" 94 | else 95 | generate_ssl_certificates 96 | fi 97 | 98 | if [ -f /root/.koji/config ] 99 | then 100 | echo "Ssl certificates already generated" 101 | else 102 | create_koji_config_for_root 103 | fi 104 | 105 | -------------------------------------------------------------------------------- /hub/etc/pki/koji/ssl.cnf: -------------------------------------------------------------------------------- 1 | HOME = . 2 | RANDFILE = .rand 3 | 4 | [ca] 5 | default_ca = ca_default 6 | 7 | [ca_default] 8 | dir = . 9 | certs = $dir/certs 10 | crl_dir = $dir/crl 11 | database = $dir/index.txt 12 | new_certs_dir = $dir/newcerts 13 | certificate = $dir/%s_ca_cert.pem 14 | private_key = $dir/private/%s_ca_key.pem 15 | serial = $dir/serial 16 | crl = $dir/crl.pem 17 | x509_extensions = usr_cert 18 | name_opt = ca_default 19 | cert_opt = ca_default 20 | default_days = 3650 21 | default_crl_days = 30 22 | default_md = sha256 23 | preserve = no 24 | policy = policy_match 25 | 26 | [policy_match] 27 | countryName = match 28 | stateOrProvinceName = match 29 | organizationName = match 30 | organizationalUnitName = optional 31 | commonName = supplied 32 | emailAddress = optional 33 | 34 | [req] 35 | default_bits = 1024 36 | default_keyfile = privkey.pem 37 | distinguished_name = req_distinguished_name 38 | attributes = req_attributes 39 | x509_extensions = v3_ca # The extentions to add to the self signed cert 40 | string_mask = MASK:0x2002 41 | 42 | [req_distinguished_name] 43 | countryName = Country Name (2 letter code) 44 | countryName_default = AT 45 | countryName_min = 2 46 | countryName_max = 2 47 | stateOrProvinceName = State or Province Name (full name) 48 | stateOrProvinceName_default = Vienna 49 | localityName = Locality Name (eg, city) 50 | localityName_default = Vienna 51 | 0.organizationName = Organization Name (eg, company) 52 | 0.organizationName_default = My company 53 | organizationalUnitName = Organizational Unit Name (eg, section) 54 | commonName = Common Name (eg, your name or your server\'s hostname) 55 | commonName_max = 64 56 | emailAddress = Email Address 57 | emailAddress_max = 64 58 | 59 | [req_attributes] 60 | challengePassword = A challenge password 61 | challengePassword_min = 4 62 | challengePassword_max = 20 63 | unstructuredName = An optional company name 64 | 65 | [usr_cert] 66 | basicConstraints = CA:FALSE 67 | nsComment = "OpenSSL Generated Certificate" 68 | subjectKeyIdentifier = hash 69 | authorityKeyIdentifier = keyid,issuer:always 70 | subjectAltName = email:move 71 | 72 | [v3_ca] 73 | subjectKeyIdentifier = hash 74 | authorityKeyIdentifier = keyid:always,issuer:always 75 | basicConstraints = CA:true 76 | subjectAltName = email:move 77 | -------------------------------------------------------------------------------- /hub/cgi/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Copyright (C) 2015 John Casey (jdcasey@commonjava.org) 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | import os 19 | import sys 20 | import subprocess 21 | import tempfile 22 | from datetime import datetime 23 | 24 | BASE_PATH = '/var/www/html/setup-scripts' 25 | 26 | host=os.environ.get('HTTP_HOST') 27 | port=os.environ.get('SERVER_PORT') 28 | if port == '80': 29 | port = '' 30 | else: 31 | port = ':' + port 32 | 33 | docroot=os.environ.get('DOCUMENT_ROOT') 34 | 35 | def script_url(path): 36 | scriptname = path[len(docroot):] 37 | return 'http://%s%s%s' % (host, port, scriptname) 38 | 39 | script=None 40 | log=None 41 | 42 | method = os.environ.get('REQUEST_METHOD').lower() 43 | # sanity-check method 44 | if method is None or method not in ('post'): 45 | status='error' 46 | 47 | else: 48 | if not os.path.exists(BASE_PATH): 49 | os.makedirs(BASE_PATH) 50 | 51 | script = tempfile.NamedTemporaryFile(prefix="setup-", suffix='.sh', delete=False, dir=BASE_PATH) 52 | status = 'ok' 53 | 54 | content=sys.stdin.read() 55 | sys.stderr.write("Creating setup script from:\n\n%s\n" % (content)) 56 | 57 | script.write(content) 58 | script.write('\n') 59 | script.close(); 60 | 61 | output = '' 62 | 63 | sys.stderr.write("Executing temp script: %s" % script.name) 64 | process = subprocess.Popen(['/usr/bin/sudo', '/bin/sh', script.name, '2>&1'], stdout=subprocess.PIPE, shell=False) 65 | output, _err = process.communicate() 66 | 67 | retcode = process.poll() 68 | if retcode: 69 | status = "Script failed with exit value: {0}\n\n{1}".format(retcode, output) 70 | 71 | sys.stderr.write("Script exited with value: %s" % retcode) 72 | 73 | log = os.path.join(BASE_PATH, 'command.log') 74 | entry="# Executed at: {timestamp}\n{script}\n\n".format(timestamp=str(datetime.now()), script=script_url(script.name)) 75 | with open(log, "a") as logfile: 76 | logfile.write(entry) 77 | 78 | 79 | if script is not None: 80 | scriptUrl = script_url(script.name) 81 | else: 82 | scriptUrl = "None" 83 | 84 | if log is not None: 85 | logUrl = script_url(log) 86 | else: 87 | logUrl = "None" 88 | 89 | if status == 'error': 90 | print """Status: 400 91 | Content-Type: text/plain 92 | 93 | Only POST requests are supported. 94 | """ 95 | elif status == 'ok': 96 | print """Status: 200 97 | Content-Type: text/plain 98 | Script-Location: {script} 99 | Script-Log: {log} 100 | 101 | {output} 102 | """.format(output=output, script=scriptUrl, log=logUrl) 103 | else: 104 | print """Status: 500 105 | Content-Type: text/plain 106 | Script-Location: {script} 107 | Script-Log: {log} 108 | 109 | {status}. 110 | """.format(status=status, script=scriptUrl, log=logUrl) 111 | 112 | -------------------------------------------------------------------------------- /hub/etc/koji-hub/hub.conf: -------------------------------------------------------------------------------- 1 | [hub] 2 | 3 | ## ConfigParser style config file, similar to ini files 4 | ## http://docs.python.org/library/configparser.html 5 | ## 6 | ## Note that multiline values can be set by indenting subsequent lines 7 | ## (which means you should not indent regular lines) 8 | 9 | ## Basic options ## 10 | #DBName = koji 11 | #DBUser = koji 12 | #DBHost = db.example.com 13 | #DBPass = example_password 14 | KojiDir = /mnt/koji 15 | 16 | 17 | ## Kerberos authentication options ## 18 | 19 | # AuthPrincipal = host/kojihub@EXAMPLE.COM 20 | # AuthKeytab = /etc/koji.keytab 21 | # ProxyPrincipals = koji/kojiweb@EXAMPLE.COM 22 | ## format string for host principals (%s = hostname) 23 | # HostPrincipalFormat = compile/%s@EXAMPLE.COM 24 | 25 | ## end Kerberos auth configuration 26 | 27 | 28 | 29 | ## SSL client certificate auth configuration ## 30 | #note: ssl auth may also require editing the httpd config (conf.d/kojihub.conf) 31 | 32 | ## the client username is the common name of the subject of their client certificate 33 | # DNUsernameComponent = CN 34 | ## separate multiple DNs with | 35 | # ProxyDNs = /C=US/ST=Massachusetts/O=Example Org/OU=Example User/CN=example/emailAddress=example@example.com 36 | 37 | ## end SSL client certificate auth configuration 38 | 39 | 40 | 41 | ## Other options ## 42 | LoginCreatesUser = On 43 | #KojiWebURL = http://kojiweb.example.com/koji 44 | # The domain name that will be appended to Koji usernames 45 | # when creating email notifications 46 | #EmailDomain = example.com 47 | # whether to send the task owner and package owner email or not on success. this still goes to watchers 48 | NotifyOnSuccess = True 49 | ## Disables all notifications 50 | # DisableNotifications = False 51 | 52 | ## Extended features 53 | ## Support Maven builds 54 | # EnableMaven = False 55 | ## Support Windows builds 56 | # EnableWin = False 57 | 58 | ## Koji hub plugins 59 | ## The path where plugins are found 60 | # PluginPath = /usr/lib/koji-hub-plugins 61 | ## A space-separated list of plugins to load 62 | # Plugins = echo 63 | 64 | ## If KojiDebug is on, the hub will be /very/ verbose and will report exception 65 | ## details to clients for anticipated errors (i.e. koji's own exceptions -- 66 | ## subclasses of koji.GenericError). 67 | # KojiDebug = On 68 | 69 | ## Determines how much detail about exceptions is reported to the client (via faults) 70 | ## Meaningful values: 71 | ## normal - a basic traceback (format_exception) 72 | ## extended - an extended traceback (format_exc_plus) 73 | ## anything else - no traceback, just the error message 74 | ## The extended traceback is intended for debugging only and should NOT be 75 | ## used in production, since it may contain sensitive information. 76 | # KojiTraceback = normal 77 | 78 | ## These options are intended for planned outages 79 | # ServerOffline = False 80 | # OfflineMessage = temporary outage 81 | # LockOut = False 82 | ## If ServerOffline is True, the server will always report a ServerOffline fault (with 83 | ## OfflineMessage as the fault string). 84 | ## If LockOut is True, the server will report a ServerOffline fault for all non-admin 85 | ## requests. 86 | 87 | 88 | DBName = koji 89 | DBUser = koji 90 | DBPass = mypassword 91 | DBHost = koji-db 92 | KojiWebURL = http://koji-hub/koji 93 | EnableMaven = True 94 | EnableWin = True 95 | KojiDebug = On 96 | KojiTraceback = extended 97 | DNUsernameComponent = CN 98 | ProxyDNs = /C=US/ST=Drunken/O=IT/CN=kojiweb 99 | 100 | -------------------------------------------------------------------------------- /hub/bin/mkuser.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | psql="psql --host=koji-db --username=koji koji" 6 | 7 | IP=$(find-ip.py || "koji-hub.local") 8 | 9 | user=$1 10 | kind=$2 11 | 12 | if [ "x$user" == "x" ]; then 13 | echo "Usage: $0 [admin|user|builder]" 14 | fi 15 | 16 | if [ "x$kind" == "xbuilder" ]; then 17 | echo "Add builder $user" 18 | echo "INSERT INTO users (name, status, usertype) VALUES ('${user}', 0, 1);" | $psql 19 | echo "INSERT INTO host (id, user_id, name) SELECT nextval('host_id_seq'), users.id, '${user}' FROM users WHERE name = '${user}';" | $psql 20 | echo "INSERT INTO host_config (host_id, arches, creator_id) SELECT id, 'x86_64', 1 FROM host WHERE name = '${user}';" | $psql 21 | echo "INSERT INTO host_channels (host_id, channel_id, creator_id) SELECT (SELECT id FROM host WHERE name = '${user}') as host_id, channels.id, 1 FROM channels WHERE name in ('default', 'createrepo', 'maven');" | $psql 22 | else 23 | echo "Add user ${user}" 24 | echo "INSERT INTO users (name, status, usertype) VALUES ('${user}', 0, 0);" | $psql 25 | fi 26 | 27 | if [ "x$kind" == "xadmin" ]; then 28 | uid=$(echo "select id from users where name = '${user}'" | $psql | tail -3 | head -1) 29 | echo "Assigning admin privileges to: ${user} with uid: ${uid}" 30 | echo "INSERT INTO user_perms (user_id, perm_id, creator_id) VALUES (${uid}, 1, ${uid});" | $psql 31 | fi 32 | 33 | cd /etc/pki/koji 34 | 35 | #if you change your certificate authority name to something else you will need to change the caname value to reflect the change. 36 | caname="koji" 37 | 38 | # user is equal to parameter one or the first argument when you actually run the script 39 | user=$1 40 | password="mypassword" 41 | conf=confs/${user}-ssl.cnf 42 | 43 | openssl genrsa -out private/${user}.key 2048 44 | cp ssl.cnf $conf 45 | 46 | openssl req -config $conf -new -nodes -out certs/${user}.csr -key private/${user}.key \ 47 | -subj "/C=US/ST=Drunken/L=Bed/O=IT/CN=${user}/emailAddress=${user}@kojihub.local" 48 | 49 | openssl ca -config $conf -batch -keyfile private/${caname}_ca_cert.key -cert ${caname}_ca_cert.crt \ 50 | -out certs/${user}-crtonly.crt -outdir certs -infiles certs/${user}.csr 51 | 52 | openssl pkcs12 -export -inkey private/${user}.key -passout "pass:${password}" -in certs/${user}-crtonly.crt -certfile ${caname}_ca_cert.crt -CAfile ${caname}_ca_cert.crt -chain -clcerts \ 53 | -out certs/${user}_browser_cert.p12 54 | 55 | openssl pkcs12 -clcerts -passin "pass:${password}" -passout "pass:${password}" -in certs/${user}_browser_cert.p12 -inkey private/${user}.key -out certs/${user}.pem 56 | 57 | cat certs/${user}-crtonly.crt private/${user}.key > certs/${user}.crt 58 | 59 | client=/opt/koji-clients/${user} 60 | 61 | rm -rf $client 62 | mkdir -p $client 63 | cp /etc/pki/koji/certs/${user}.crt $client/client.crt # NOTE: It is IMPORTANT you use the aggregated form 64 | cp /etc/pki/koji/certs/${user}.pem $client/client.pem 65 | cp /etc/pki/koji/certs/${user}_browser_cert.p12 $client/client_browser_cert.p12 66 | cp /etc/pki/koji/koji_ca_cert.crt $client/clientca.crt 67 | cp /etc/pki/koji/koji_ca_cert.crt $client/serverca.crt 68 | 69 | cat < $client/config 70 | [koji] 71 | server = https://${IP}/kojihub 72 | authtype = ssl 73 | cert = ${client}/client.crt 74 | ca = ${client}/clientca.crt 75 | serverca = ${client}/serverca.crt 76 | weburl = https://${IP}/koji 77 | topurl = https://${IP}/kojifiles 78 | EOF 79 | 80 | cat < $client/config.json 81 | { 82 | "url": "https://${IP}/kojihub", 83 | "crt-url": "https://${IP}/koji-clients/${user}/client.crt", 84 | "pem-url": "https://${IP}/koji-clients/${user}/client.pem", 85 | "ca-url": "https://${IP}/koji-clients/${user}/clientca.crt", 86 | "serverca-url": "https://${IP}/koji-clients/${user}/serverca.crt", 87 | "crt": "${client}/client.crt", 88 | "pem": "${client}/client.pem", 89 | "ca": "${client}/clientca.crt", 90 | "serverca": "${client}/serverca.crt" 91 | } 92 | EOF 93 | 94 | chown -R nobody:nobody ${client} 95 | -------------------------------------------------------------------------------- /research/build-init/relevant-koji-snippets.py: -------------------------------------------------------------------------------- 1 | kojid@1455: 2 | 3 | # upload the build output 4 | for filepath in logs: 5 | self.uploadFile(os.path.join(outputdir, filepath), 6 | relPath=os.path.dirname(filepath)) 7 | for relpath, files in output_files.iteritems(): 8 | for filename in files: 9 | self.uploadFile(os.path.join(outputdir, relpath, filename), 10 | relPath=relpath) 11 | 12 | # Should only find log files in the mock result directory. 13 | # Don't upload these log files, they've already been streamed 14 | # the hub. 15 | for filename in os.listdir(buildroot.resultdir()): 16 | root, ext = os.path.splitext(filename) 17 | if ext == '.log': 18 | filepath = os.path.join(buildroot.resultdir(), filename) 19 | if os.path.isfile(filepath) and os.stat(filepath).st_size > 0: 20 | # only files with content get uploaded to the hub 21 | logs.append(filename) 22 | 23 | return {'maven_info': maven_info, 24 | 'buildroot_id': buildroot.id, 25 | 'logs': logs, 26 | 'files': output_files} 27 | 28 | 29 | 30 | kojid@1214: 31 | 32 | self.build_task_id = self.session.host.subtask(method='buildMaven', 33 | arglist=[url, build_tag, build_opts], 34 | label='build', 35 | parent=self.id, 36 | arch='noarch') 37 | maven_results = self.wait(self.build_task_id)[self.build_task_id] 38 | maven_results['task_id'] = self.build_task_id 39 | 40 | build_info = None 41 | if not self.opts.get('scratch'): 42 | maven_info = maven_results['maven_info'] 43 | if maven_info['version'].endswith('-SNAPSHOT'): 44 | raise koji.BuildError, '-SNAPSHOT versions are only supported in scratch builds' 45 | build_info = koji.maven_info_to_nvr(maven_info) 46 | 47 | if not self.opts.get('skip_tag'): 48 | dest_cfg = self.session.getPackageConfig(dest_tag['id'], build_info['name']) 49 | # Make sure package is on the list for this tag 50 | if dest_cfg is None: 51 | raise koji.BuildError, "package %s not in list for tag %s" \ 52 | % (build_info['name'], dest_tag['name']) 53 | elif dest_cfg['blocked']: 54 | raise koji.BuildError, "package %s is blocked for tag %s" \ 55 | % (build_info['name'], dest_tag['name']) 56 | 57 | build_info = self.session.host.initMavenBuild(self.id, build_info, maven_info) 58 | self.build_id = build_info['id'] 59 | 60 | try: 61 | rpm_results = None 62 | spec_url = self.opts.get('specfile') 63 | if spec_url: 64 | rpm_results = self.buildWrapperRPM(spec_url, self.build_task_id, target_info, build_info, repo_id) 65 | 66 | if self.opts.get('scratch'): 67 | self.session.host.moveMavenBuildToScratch(self.id, maven_results, rpm_results) 68 | else: 69 | self.session.host.completeMavenBuild(self.id, self.build_id, maven_results, rpm_results) 70 | except (SystemExit, ServerExit, KeyboardInterrupt): 71 | # we do not trap these 72 | raise 73 | except: 74 | if not self.opts.get('scratch'): 75 | #scratch builds do not get imported 76 | self.session.host.failBuild(self.id, self.build_id) 77 | # reraise the exception 78 | raise 79 | 80 | if not self.opts.get('scratch') and not self.opts.get('skip_tag'): 81 | tag_task_id = self.session.host.subtask(method='tagBuild', 82 | arglist=[dest_tag['id'], self.build_id, False, None, True], 83 | label='tag', 84 | parent=self.id, 85 | arch='noarch') 86 | self.wait(tag_task_id) 87 | 88 | koji/__init__.py@1040: 89 | 90 | nvr = {'name': maveninfo['group_id'] + '-' + maveninfo['artifact_id'], 91 | 'version': maveninfo['version'].replace('-', '_'), 92 | 'release': None, 93 | 'epoch': None} 94 | # for backwards-compatibility 95 | nvr['package_name'] = nvr['name'] 96 | 97 | kojihub.py@8182: 98 | 99 | def makeTask(self,*args,**opts): 100 | #this is mainly for debugging 101 | #only an admin can make arbitrary tasks 102 | context.session.assertPerm('admin') 103 | return make_task(*args,**opts) 104 | 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Koji Dojo 2 | 3 | Koji dojo is a suite of Docker images that are designed to enable automated testing for applications that need to integrate with Koji. 4 | 5 | ## Images 6 | 7 | Currently, there are three images: 8 | 9 | * `hub/` - This is a simplistic build of the Koji hub service, configured to enable SSL authentication. It is based on the CentOS 7 Docker image 10 | * `client/` - This is a minimal CentOS 7 image that mounts volumes from the hub container and installs the Koji client RPM from it. It will also link and configure scripts for each of the users generated in the hub setup. 11 | * `builder/` - This is a simplistic build of the Koji builder service connected to hub. It is based on the CentOS 7 Docker image 12 | 13 | ## Docker Maintenance Scripts 14 | 15 | * `build-all.sh` - Runs `docker build ...` for building the images in this repository 16 | * `*/docker-scripts/build.sh` - Runs `docker build ...` for just the given image (hub, client, etc.) 17 | * `*/docker-scripts/run.sh` - Starts any containers needed to support the given image, then starts a new container for the given image itself, monitoring system in/out. When you use CTL-C to escape the container, the supporting containers are stopped and removed as well. 18 | * `*/docker-scripts/start.sh` - Starts supporting container and a new container for the given image. Each container is started in daemon mode. 19 | * `*/docker-scripts/stop.sh` - Stops and removes the container for the given image and any supporting containers. 20 | 21 | ## Docker Compose 22 | 23 | * Build all images: `docker-compose build` 24 | * Start all containers: `docker-compose up` (Use -d for detached mode) 25 | * Stop and remove all containers: `docker-compose down` 26 | 27 | * koji-hub's IP address must be added to local /etc/hosts such as: 28 | `172.17.0.3 koji-hub` 29 | 30 | ## Hub Image Notes 31 | 32 | When the hub initializes, it checks out Koji sources from Git, builds them, and installs the koji-hub* RPMs. The sources are cloned into `/opt/koji`, which is exposed as a Docker volume. This enables the `client` image to install the client RPM that was built in conjunction with the hub RPMs. 33 | 34 | The Koji hub image generates three users on initialization: 35 | 36 | * kojiadmin 37 | * testadmin 38 | * testuser 39 | 40 | The Koji hub image generates one host on initialization: 41 | 42 | * kojibuilder 43 | 44 | The PKCS#12 SSL certificates for these each use the password 'mypassword'. The certificates, CA cert files, and a basic Koji configuration file is stored for each user under `/opt/koji-clients/`. Along with these, a basic JSON file is stored for each user that gives the URL and SSL file references in a way that's easy to use for non-Koji clients. The `/opt/koji-clients` directory is exposed as a volume in the Docker container, so it can be mounted in other containers via the `--volumes-from` Docker run option. 45 | 46 | User accounts and source build aside, this hub image also exposes the `/opt/koji-clients` and `/var/log` directories via HTTP. This enables an application's tests to use simple HTTP to retrieve the generated SSL certificates, and to download the relevant log files for the webserver and Koji hub in case a test fails. 47 | 48 | You can also use the hub container from your localhost by using a manual volume mount from the localhost directory structure into the hub container: 49 | 50 | ``` 51 | docker run -d --name=koji=hub -v /opt/koji-clients:/opt/koji-clients docker.io/buildchimp/koji-hub 52 | ``` 53 | 54 | Now, if you have the Koji client RPM installed locally, you can start using the hub container by simply using one of the generated configs: 55 | 56 | ``` 57 | koji -c /opt/koji-clients/testuser/config hello 58 | ``` 59 | 60 | ``` 61 | koji -c /opt/koji-clients/testuser/config list-tags 62 | ``` 63 | 64 | 65 | ## Client Image Notes 66 | 67 | The client container mounts all exposed volumes from the hub. During initialization, it installs the Koji client RPM in `/opt/koji/noarch` (built by the hub during its own initialization), then uses the SSL configurations under `/opt/koji-clients` to generate `/root/.koji/config` with headings like `koji-testuser`. Next it symlinks `/usr/local/koji` to corresponding script names (eg. `koji-testuser`) under `/root/bin`. 68 | 69 | Finally, the client container sets up SSH host keys and starts the SSHd daemon. It prints the IP address and port for this SSHd instance to the docker log. 70 | 71 | ## Web Browser Certificates 72 | 73 | Certificates for the various accounts created during hub container 74 | initialization can be found at `/opt/koji-clients`. 75 | 76 | Install the corresponding cert in your browser to enable logins. 77 | In Chrome, for example, this can be done via UI or via the command line 78 | by using pk12util: 79 | 80 | `pk12util -d sql:$HOME/.pki/nssdb -i /opt/koji-clients/testuser/client_browser_cert.p12 -W mypassword` 81 | 82 | ## Builder notes 83 | 84 | To use builder please use builder/docker-scripts/build-all.sh to build hub image instead of hub/docker-scripts/build.sh (please see comments inside the script for explanation) 85 | 86 | To start koji-db, koji-hub and koji-builder at once use builder/docker-scripts/run-all.sh 87 | 88 | To start only koji-builder use builder/docker-scripts/run.sh 89 | 90 | 91 | Example of builder bootstrap: 92 | 93 | ~~~~ 94 | # build builder 95 | ./builder/docker-scripts/build.sh 96 | 97 | # start builder (includes start of koji-db and koji-hub) 98 | # please note koji hub will be started using different options tham specified in ./hub/docker-scripts/run.sh 99 | # because additional volume /opt/koji-files is mapped to be shared with builder 100 | ./builder/docker-scripts/run.sh 101 | 102 | # create alias for to allow use local koji installation 103 | # optionally you can use koji-client container 104 | alias kojitest="koji -c /opt/koji-clients/kojiadmin/config" 105 | 106 | # verify koji is running 107 | kojitest hello 108 | 109 | # show cert info 110 | pk12util -d sql:$HOME/.pki/nssdb -l /opt/koji-clients/kojiadmin/client_browser_cert.p12 -W mypassword 111 | # show certificates 112 | certutil -d sql:$HOME/.pki/nssdb -L 113 | 114 | # delete 115 | certutil -d sql:$HOME/.pki/nssdb -D -n "kojiadmin - IT" 116 | certutil -d sql:$HOME/.pki/nssdb -D -n "koji-hub - IT" 117 | 118 | # import admin certificate 119 | pk12util -d sql:$HOME/.pki/nssdb -i /opt/koji-clients/kojiadmin/client_browser_cert.p12 -W mypassword 120 | 121 | # https://chromium.googlesource.com/chromium/src/+/master/docs/linux_cert_management.md 122 | 123 | # open koji-hub in browser and login with certificate 124 | # hit CTRL+R to reload in case of deleting of existing certificate to login with new one 125 | google-chrome https://172.17.0.3/koji/login & 126 | 127 | # noarch because of maven repo for maven build 128 | kojitest add-tag destination-tag --maven-support --include-all --arches="noarch" 129 | 130 | # arch x86_64 because of rpm repo used to create build environment 131 | kojitest add-tag build-tag --maven-support --include-all --arches="x86_64" 132 | 133 | # create build target 134 | kojitest add-target build-target build-tag destination-tag 135 | 136 | # create build group "maven-build" 137 | kojitest add-group build-tag maven-build 138 | 139 | # populate the "maven-build" group with packages that will be installed into the build environment (buildroot) 140 | kojitest add-group-pkg build-tag maven-build bash coreutils git java-1.8.0-openjdk-devel maven3 shadow-utils 141 | 142 | # add external repo to download rpms into build environment (buildroot) 143 | kojitest add-external-repo -t build-tag buil-external-repo http://myorg.com/rpm-repo/\$arch/ 144 | # or 145 | # import packages ... 146 | 147 | 148 | # example of importing external archives 149 | # resolve imported dependency (all artifacts for given gav) 150 | mvn org.apache.maven.plugins:maven-dependency-plugin:2.7:get -DrepoUrl=http://central.maven.org/maven2/ -Dartifact=org.apache.maven.plugins:maven-enforcer-plugin:1.4:pom 151 | mvn org.apache.maven.plugins:maven-dependency-plugin:2.7:get -DrepoUrl=http://central.maven.org/maven2/ -Dartifact=org.apache.maven.plugins:maven-enforcer-plugin:1.4:jar 152 | mvn org.apache.maven.plugins:maven-dependency-plugin:2.7:get -DrepoUrl=http://central.maven.org/maven2/ -Dartifact=org.apache.maven.plugins:maven-enforcer-plugin:1.4:jar:javadoc 153 | mvn org.apache.maven.plugins:maven-dependency-plugin:2.7:get -DrepoUrl=http://central.maven.org/maven2/ -Dartifact=org.apache.maven.plugins:maven-enforcer-plugin:1.4:jar:sources 154 | 155 | # import maven artifacts of imported package 156 | kojitest import-archive --create-build --type maven --type-info ~/.m2/repository/org/apache/maven/plugins/maven-enforcer-plugin/1.4/maven-enforcer-plugin-1.4.pom org.apache.maven.plugins-maven-enforcer-plugin-1.4-1 ~/.m2/repository/org/apache/maven/plugins/maven-enforcer-plugin/1.4/maven-enforcer-plugin-1.4.jar ~/.m2/repository/org/apache/maven/plugins/maven-enforcer-plugin/1.4/maven-enforcer-plugin-1.4-javadoc.jar ~/.m2/repository/org/apache/maven/plugins/maven-enforcer-plugin/1.4/maven-enforcer-plugin-1.4.pom ~/.m2/repository/org/apache/maven/plugins/maven-enforcer-plugin/1.4/maven-enforcer-plugin-1.4-sources.jar 157 | 158 | # add package 159 | kojitest add-pkg --owner kojiadmin build-tag org.apache.maven.plugins-maven-enforcer-plugin 160 | 161 | # tag package build 162 | kojitest tag-pkg build-tag org.apache.maven.plugins-maven-enforcer-plugin-1.4-1 163 | 164 | 165 | # make sure followin list is not empty othewise maven repo is not generated 166 | kojitest list-tagged --inherit build-tag 167 | 168 | # repo regen 169 | kojitest regen-repo build-tag 170 | 171 | # verify maven repo was generated - maven repo root should be displayed 172 | google-chrome https://172.17.0.3/kojifiles/repos/build-tag/latest/maven/ & 173 | 174 | # add package 175 | kojitest add-pkg --owner=kojiadmin destination-tag myproject 176 | # submit maven build 177 | kojitest maven-build build-target https://www.github.com/myorg/myproject 178 | 179 | ~~~~ 180 | -------------------------------------------------------------------------------- /hub/etc/httpd/conf.d/ssl.conf: -------------------------------------------------------------------------------- 1 | # 2 | # This is the Apache server configuration file providing SSL support. 3 | # It contains the configuration directives to instruct the server how to 4 | # serve pages over an https connection. For detailing information about these 5 | # directives see 6 | # 7 | # Do NOT simply read the instructions in here without understanding 8 | # what they do. They're here only as hints or reminders. If you are unsure 9 | # consult the online docs. You have been warned. 10 | # 11 | 12 | LoadModule ssl_module modules/mod_ssl.so 13 | 14 | # 15 | # When we also provide SSL we have to listen to the 16 | # the HTTPS port in addition. 17 | # 18 | Listen 443 19 | 20 | ## 21 | ## SSL Global Context 22 | ## 23 | ## All SSL configuration in this context applies both to 24 | ## the main server and all SSL-enabled virtual hosts. 25 | ## 26 | 27 | # Pass Phrase Dialog: 28 | # Configure the pass phrase gathering process. 29 | # The filtering dialog program (`builtin' is a internal 30 | # terminal dialog) has to provide the pass phrase on stdout. 31 | SSLPassPhraseDialog builtin 32 | 33 | # Inter-Process Session Cache: 34 | # Configure the SSL Session Cache: First the mechanism 35 | # to use and second the expiring timeout (in seconds). 36 | SSLSessionCache shmcb:/var/cache/mod_ssl/scache(512000) 37 | SSLSessionCacheTimeout 300 38 | 39 | # Semaphore: 40 | # Configure the path to the mutual exclusion semaphore the 41 | # SSL engine uses internally for inter-process synchronization. 42 | #SSLMutex default 43 | 44 | # Pseudo Random Number Generator (PRNG): 45 | # Configure one or more sources to seed the PRNG of the 46 | # SSL library. The seed data should be of good random quality. 47 | # WARNING! On some platforms /dev/random blocks if not enough entropy 48 | # is available. This means you then cannot use the /dev/random device 49 | # because it would lead to very long connection times (as long as 50 | # it requires to make more entropy available). But usually those 51 | # platforms additionally provide a /dev/urandom device which doesn't 52 | # block. So, if available, use this one instead. Read the mod_ssl User 53 | # Manual for more details. 54 | SSLRandomSeed startup file:/dev/urandom 256 55 | SSLRandomSeed connect builtin 56 | #SSLRandomSeed startup file:/dev/random 512 57 | #SSLRandomSeed connect file:/dev/random 512 58 | #SSLRandomSeed connect file:/dev/urandom 512 59 | 60 | # 61 | # Use "SSLCryptoDevice" to enable any supported hardware 62 | # accelerators. Use "openssl engine -v" to list supported 63 | # engine names. NOTE: If you enable an accelerator and the 64 | # server does not start, consult the error logs and ensure 65 | # your accelerator is functioning properly. 66 | # 67 | SSLCryptoDevice builtin 68 | #SSLCryptoDevice ubsec 69 | 70 | ## 71 | ## SSL Virtual Host Context 72 | ## 73 | 74 | 75 | 76 | # General setup for the virtual host, inherited from global configuration 77 | #DocumentRoot "/var/www/html" 78 | #ServerName www.example.com:443 79 | 80 | # Use separate log files for the SSL virtual host; note that LogLevel 81 | # is not inherited from httpd.conf. 82 | ErrorLog logs/ssl_error_log 83 | TransferLog logs/ssl_access_log 84 | LogLevel debug 85 | 86 | # SSL Engine Switch: 87 | # Enable/Disable SSL for this virtual host. 88 | SSLEngine on 89 | 90 | # SSL Protocol support: 91 | # List the enable protocol levels with which clients will be able to 92 | # connect. Disable SSLv2 access by default: 93 | SSLProtocol all -SSLv2 94 | 95 | # SSL Cipher Suite: 96 | # List the ciphers that the client is permitted to negotiate. 97 | # See the mod_ssl documentation for a complete list. 98 | SSLCipherSuite DEFAULT:!EXP:!SSLv2:!DES:!IDEA:!SEED:+3DES 99 | 100 | # Server Certificate: 101 | # Point SSLCertificateFile at a PEM encoded certificate. If 102 | # the certificate is encrypted, then you will be prompted for a 103 | # pass phrase. Note that a kill -HUP will prompt again. A new 104 | # certificate can be generated using the genkey(1) command. 105 | #SSLCertificateFile /etc/pki/tls/certs/localhost.crt 106 | 107 | # Server Private Key: 108 | # If the key is not combined with the certificate, use this 109 | # directive to point at the key file. Keep in mind that if 110 | # you've both a RSA and a DSA private key you can configure 111 | # both in parallel (to also allow the use of DSA ciphers, etc.) 112 | #SSLCertificateKeyFile /etc/pki/tls/private/localhost.key 113 | 114 | # Server Certificate Chain: 115 | # Point SSLCertificateChainFile at a file containing the 116 | # concatenation of PEM encoded CA certificates which form the 117 | # certificate chain for the server certificate. Alternatively 118 | # the referenced file can be the same as SSLCertificateFile 119 | # when the CA certificates are directly appended to the server 120 | # certificate for convinience. 121 | #SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt 122 | 123 | # Certificate Authority (CA): 124 | # Set the CA certificate verification path where to find CA 125 | # certificates for client authentication or alternatively one 126 | # huge file containing all of them (file must be PEM encoded) 127 | #SSLCACertificateFile /etc/pki/tls/certs/ca-bundle.crt 128 | 129 | # Client Authentication (Type): 130 | # Client certificate verification type and depth. Types are 131 | # none, optional, require and optional_no_ca. Depth is a 132 | # number which specifies how deeply to verify the certificate 133 | # issuer chain before deciding the certificate is not valid. 134 | #SSLVerifyClient require 135 | #SSLVerifyDepth 10 136 | 137 | ServerName kojihub.local:443 138 | SSLCertificateFile /etc/pki/koji/certs/kojihub.crt 139 | SSLCertificateKeyFile /etc/pki/koji/private/kojihub.key 140 | SSLCertificateChainFile /etc/pki/koji/koji_ca_cert.crt 141 | SSLCACertificateFile /etc/pki/koji/koji_ca_cert.crt 142 | SSLVerifyDepth 1 143 | 144 | # Access Control: 145 | # With SSLRequire you can do per-directory access control based 146 | # on arbitrary complex boolean expressions containing server 147 | # variable checks and other lookup directives. The syntax is a 148 | # mixture between C and Perl. See the mod_ssl documentation 149 | # for more details. 150 | # 151 | #SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \ 152 | # and %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \ 153 | # and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} \ 154 | # and %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 \ 155 | # and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20 ) \ 156 | # or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/ 157 | # 158 | 159 | # SSL Engine Options: 160 | # Set various options for the SSL engine. 161 | # o FakeBasicAuth: 162 | # Translate the client X.509 into a Basic Authorisation. This means that 163 | # the standard Auth/DBMAuth methods can be used for access control. The 164 | # user name is the `one line' version of the client's X.509 certificate. 165 | # Note that no password is obtained from the user. Every entry in the user 166 | # file needs this password: `xxj31ZMTZzkVA'. 167 | # o ExportCertData: 168 | # This exports two additional environment variables: SSL_CLIENT_CERT and 169 | # SSL_SERVER_CERT. These contain the PEM-encoded certificates of the 170 | # server (always existing) and the client (only existing when client 171 | # authentication is used). This can be used to import the certificates 172 | # into CGI scripts. 173 | # o StdEnvVars: 174 | # This exports the standard SSL/TLS related `SSL_*' environment variables. 175 | # Per default this exportation is switched off for performance reasons, 176 | # because the extraction step is an expensive operation and is usually 177 | # useless for serving static content. So one usually enables the 178 | # exportation for CGI and SSI requests only. 179 | # o StrictRequire: 180 | # This denies access when "SSLRequireSSL" or "SSLRequire" applied even 181 | # under a "Satisfy any" situation, i.e. when it applies access is denied 182 | # and no other module can change it. 183 | # o OptRenegotiate: 184 | # This enables optimized SSL connection renegotiation handling when SSL 185 | # directives are used in per-directory context. 186 | #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire 187 | 188 | SSLOptions +StdEnvVars 189 | 190 | 191 | SSLOptions +StdEnvVars 192 | 193 | 194 | # SSL Protocol Adjustments: 195 | # The safe and default but still SSL/TLS standard compliant shutdown 196 | # approach is that mod_ssl sends the close notify alert but doesn't wait for 197 | # the close notify alert from client. When you need a different shutdown 198 | # approach you can use one of the following variables: 199 | # o ssl-unclean-shutdown: 200 | # This forces an unclean shutdown when the connection is closed, i.e. no 201 | # SSL close notify alert is send or allowed to received. This violates 202 | # the SSL/TLS standard but is needed for some brain-dead browsers. Use 203 | # this when you receive I/O errors because of the standard approach where 204 | # mod_ssl sends the close notify alert. 205 | # o ssl-accurate-shutdown: 206 | # This forces an accurate shutdown when the connection is closed, i.e. a 207 | # SSL close notify alert is send and mod_ssl waits for the close notify 208 | # alert of the client. This is 100% SSL/TLS standard compliant, but in 209 | # practice often causes hanging connections with brain-dead browsers. Use 210 | # this only for browsers where you know that their SSL implementation 211 | # works correctly. 212 | # Notice: Most problems of broken clients are also related to the HTTP 213 | # keep-alive facility, so you usually additionally want to disable 214 | # keep-alive for those clients, too. Use variable "nokeepalive" for this. 215 | # Similarly, one has to force some clients to use HTTP/1.0 to workaround 216 | # their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and 217 | # "force-response-1.0" for this. 218 | SetEnvIf User-Agent ".*MSIE.*" \ 219 | nokeepalive ssl-unclean-shutdown \ 220 | downgrade-1.0 force-response-1.0 221 | 222 | # Per-Server Logging: 223 | # The home of a custom SSL log file. Use this when you want a 224 | # compact non-error SSL logfile on a virtual host basis. 225 | CustomLog logs/ssl_request_log \ 226 | "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" 227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /openshift/koji.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Template 3 | metadata: 4 | creationTimestamp: null 5 | name: koji 6 | objects: 7 | - apiVersion: v1 8 | kind: ConfigMap 9 | metadata: 10 | name: koji-dojo-config 11 | data: 12 | koji.git.url: https://pagure.io/koji.git 13 | koji.git.branch: master 14 | - apiVersion: v1 15 | kind: PersistentVolumeClaim 16 | metadata: 17 | name: hub-koji-claim 18 | spec: 19 | accessModes: 20 | - ReadWriteMany 21 | resources: 22 | requests: 23 | storage: 1Gi 24 | volumeName: koji-volume 25 | - apiVersion: v1 26 | kind: PersistentVolumeClaim 27 | metadata: 28 | name: hub-koji-clients-claim 29 | spec: 30 | accessModes: 31 | - ReadWriteMany 32 | resources: 33 | requests: 34 | storage: 1Gi 35 | volumeName: koji-clients-volume 36 | - apiVersion: v1 37 | kind: BuildConfig 38 | metadata: 39 | creationTimestamp: null 40 | name: builder 41 | spec: 42 | nodeSelector: null 43 | output: 44 | to: 45 | kind: ImageStreamTag 46 | name: koji-builder:latest 47 | postCommit: {} 48 | resources: {} 49 | runPolicy: Serial 50 | source: 51 | contextDir: builder 52 | git: 53 | ref: ${KOJI_DOJO_BRANCH} 54 | uri: ${KOJI_DOJO_REMOTE} 55 | type: Git 56 | strategy: 57 | dockerStrategy: 58 | noCache: true 59 | type: Docker 60 | triggers: [] 61 | status: 62 | lastVersion: 0 63 | - apiVersion: v1 64 | kind: BuildConfig 65 | metadata: 66 | creationTimestamp: null 67 | name: client 68 | spec: 69 | nodeSelector: null 70 | output: 71 | to: 72 | kind: ImageStreamTag 73 | name: koji-client:latest 74 | postCommit: {} 75 | resources: {} 76 | runPolicy: Serial 77 | source: 78 | contextDir: client 79 | git: 80 | ref: ${KOJI_DOJO_BRANCH} 81 | uri: ${KOJI_DOJO_REMOTE} 82 | type: Git 83 | strategy: 84 | dockerStrategy: 85 | noCache: true 86 | type: Docker 87 | triggers: [] 88 | status: 89 | lastVersion: 0 90 | - apiVersion: v1 91 | kind: BuildConfig 92 | metadata: 93 | creationTimestamp: null 94 | name: hub 95 | spec: 96 | nodeSelector: null 97 | output: 98 | to: 99 | kind: ImageStreamTag 100 | name: koji-hub:latest 101 | postCommit: {} 102 | resources: {} 103 | runPolicy: Serial 104 | source: 105 | contextDir: hub 106 | git: 107 | ref: ${KOJI_DOJO_BRANCH} 108 | uri: ${KOJI_DOJO_REMOTE} 109 | type: Git 110 | strategy: 111 | dockerStrategy: 112 | noCache: true 113 | type: Docker 114 | triggers: [] 115 | status: 116 | lastVersion: 0 117 | - apiVersion: v1 118 | kind: ImageStream 119 | metadata: 120 | creationTimestamp: null 121 | generation: 1 122 | name: koji-builder 123 | - apiVersion: v1 124 | kind: ImageStream 125 | metadata: 126 | creationTimestamp: null 127 | generation: 1 128 | name: koji-client 129 | - apiVersion: v1 130 | kind: ImageStream 131 | metadata: 132 | creationTimestamp: null 133 | generation: 1 134 | name: koji-hub 135 | - apiVersion: v1 136 | kind: DeploymentConfig 137 | metadata: 138 | creationTimestamp: null 139 | generation: 1 140 | name: koji-builder 141 | spec: 142 | replicas: 1 143 | selector: 144 | name: builder 145 | strategy: 146 | resources: {} 147 | rollingParams: 148 | intervalSeconds: 1 149 | maxSurge: 25% 150 | maxUnavailable: 25% 151 | timeoutSeconds: 600 152 | updatePeriodSeconds: 1 153 | type: Rolling 154 | template: 155 | metadata: 156 | creationTimestamp: null 157 | labels: 158 | name: builder 159 | spec: 160 | containers: 161 | - image: koji-builder 162 | imagePullPolicy: IfNotPresent 163 | name: koji-builder 164 | resources: {} 165 | securityContext: 166 | runAsUser: 0 167 | terminationMessagePath: /dev/termination-log 168 | volumeMounts: 169 | - mountPath: /opt/koji 170 | name: koji-volume 171 | - mountPath: /opt/koji-clients 172 | name: koji-clients 173 | dnsPolicy: ClusterFirst 174 | restartPolicy: Always 175 | securityContext: {} 176 | serviceAccount: koji-sa 177 | serviceAccountName: koji-sa 178 | terminationGracePeriodSeconds: 30 179 | volumes: 180 | - name: koji-volume 181 | persistentVolumeClaim: 182 | claimName: hub-koji-claim 183 | - name: koji-clients 184 | persistentVolumeClaim: 185 | claimName: hub-koji-clients-claim 186 | test: false 187 | triggers: 188 | - type: ConfigChange 189 | - imageChangeParams: 190 | automatic: true 191 | containerNames: 192 | - koji-builder 193 | from: 194 | kind: ImageStreamTag 195 | name: koji-builder:latest 196 | namespace: koji 197 | type: ImageChange 198 | - apiVersion: v1 199 | kind: DeploymentConfig 200 | metadata: 201 | creationTimestamp: null 202 | generation: 1 203 | name: koji-client 204 | spec: 205 | replicas: 1 206 | selector: 207 | name: client 208 | strategy: 209 | resources: {} 210 | rollingParams: 211 | intervalSeconds: 1 212 | maxSurge: 25% 213 | maxUnavailable: 25% 214 | timeoutSeconds: 600 215 | updatePeriodSeconds: 1 216 | type: Rolling 217 | template: 218 | metadata: 219 | creationTimestamp: null 220 | labels: 221 | name: client 222 | spec: 223 | containers: 224 | - image: koji-client 225 | imagePullPolicy: IfNotPresent 226 | name: koji-client 227 | resources: {} 228 | securityContext: 229 | runAsUser: 0 230 | terminationMessagePath: /dev/termination-log 231 | volumeMounts: 232 | - mountPath: /opt/koji 233 | name: koji-volume 234 | - mountPath: /opt/koji-clients 235 | name: koji-clients 236 | dnsPolicy: ClusterFirst 237 | restartPolicy: Always 238 | securityContext: {} 239 | serviceAccount: koji-sa 240 | serviceAccountName: koji-sa 241 | terminationGracePeriodSeconds: 30 242 | volumes: 243 | - name: koji-volume 244 | persistentVolumeClaim: 245 | claimName: hub-koji-claim 246 | - name: koji-clients 247 | persistentVolumeClaim: 248 | claimName: hub-koji-clients-claim 249 | test: false 250 | triggers: 251 | - type: ConfigChange 252 | - imageChangeParams: 253 | automatic: true 254 | containerNames: 255 | - koji-client 256 | from: 257 | kind: ImageStreamTag 258 | name: koji-client:latest 259 | namespace: koji 260 | type: ImageChange 261 | status: {} 262 | - apiVersion: v1 263 | kind: DeploymentConfig 264 | metadata: 265 | creationTimestamp: null 266 | generation: 1 267 | name: koji-db 268 | spec: 269 | replicas: 1 270 | selector: 271 | name: db 272 | strategy: 273 | resources: {} 274 | rollingParams: 275 | intervalSeconds: 1 276 | maxSurge: 25% 277 | maxUnavailable: 25% 278 | timeoutSeconds: 600 279 | updatePeriodSeconds: 1 280 | type: Rolling 281 | template: 282 | metadata: 283 | creationTimestamp: null 284 | labels: 285 | name: db 286 | spec: 287 | containers: 288 | - env: 289 | - name: POSTGRESQL_DATABASE 290 | value: koji 291 | - name: POSTGRESQL_USER 292 | value: koji 293 | - name: POSTGRESQL_PASSWORD 294 | value: mypassword 295 | image: openshift/postgresql-92-centos7 296 | imagePullPolicy: IfNotPresent 297 | name: koji-db 298 | ports: 299 | - containerPort: 5432 300 | protocol: TCP 301 | resources: {} 302 | terminationMessagePath: /dev/termination-log 303 | dnsPolicy: ClusterFirst 304 | restartPolicy: Always 305 | securityContext: {} 306 | terminationGracePeriodSeconds: 30 307 | test: false 308 | triggers: 309 | - type: ConfigChange 310 | status: {} 311 | - apiVersion: v1 312 | kind: DeploymentConfig 313 | metadata: 314 | creationTimestamp: null 315 | generation: 1 316 | name: hub 317 | spec: 318 | replicas: 1 319 | selector: 320 | name: hub 321 | strategy: 322 | resources: {} 323 | rollingParams: 324 | intervalSeconds: 1 325 | maxSurge: 25% 326 | maxUnavailable: 25% 327 | timeoutSeconds: 600 328 | updatePeriodSeconds: 1 329 | type: Rolling 330 | template: 331 | metadata: 332 | creationTimestamp: null 333 | labels: 334 | name: hub 335 | spec: 336 | containers: 337 | - env: 338 | - name: ADDITIONAL_SAN 339 | value: ${PUBLIC_IP} 340 | image: koji-hub 341 | imagePullPolicy: IfNotPresent 342 | name: koji-hub 343 | ports: 344 | - containerPort: 22 345 | protocol: TCP 346 | - containerPort: 80 347 | protocol: TCP 348 | - containerPort: 443 349 | protocol: TCP 350 | resources: {} 351 | securityContext: 352 | runAsUser: 0 353 | terminationMessagePath: /dev/termination-log 354 | volumeMounts: 355 | - mountPath: /opt/koji 356 | name: koji-volume 357 | - mountPath: /opt/koji-clients 358 | name: koji-clients 359 | dnsPolicy: ClusterFirst 360 | restartPolicy: Always 361 | securityContext: {} 362 | serviceAccount: koji-sa 363 | serviceAccountName: koji-sa 364 | terminationGracePeriodSeconds: 30 365 | volumes: 366 | - name: koji-volume 367 | persistentVolumeClaim: 368 | claimName: hub-koji-claim 369 | - name: koji-clients 370 | persistentVolumeClaim: 371 | claimName: hub-koji-clients-claim 372 | test: false 373 | triggers: 374 | - type: ConfigChange 375 | - imageChangeParams: 376 | automatic: true 377 | containerNames: 378 | - koji-hub 379 | from: 380 | kind: ImageStreamTag 381 | name: koji-hub:latest 382 | namespace: koji 383 | type: ImageChange 384 | status: {} 385 | - apiVersion: v1 386 | kind: Service 387 | metadata: 388 | creationTimestamp: null 389 | name: koji-db 390 | spec: 391 | ports: 392 | - port: 5432 393 | protocol: TCP 394 | targetPort: 5432 395 | selector: 396 | name: db 397 | sessionAffinity: None 398 | type: ClusterIP 399 | status: 400 | loadBalancer: {} 401 | - apiVersion: v1 402 | kind: Service 403 | metadata: 404 | creationTimestamp: null 405 | name: koji-hub 406 | spec: 407 | ports: 408 | - name: http 409 | port: 80 410 | protocol: TCP 411 | targetPort: 80 412 | - name: https 413 | port: 443 414 | protocol: TCP 415 | targetPort: 443 416 | selector: 417 | name: hub 418 | sessionAffinity: None 419 | type: ClusterIP 420 | - apiVersion: v1 421 | kind: Route 422 | metadata: 423 | creationTimestamp: null 424 | name: https 425 | spec: 426 | host: ${PUBLIC_IP}.xip.io 427 | port: 428 | targetPort: https 429 | tls: 430 | termination: passthrough 431 | to: 432 | kind: Service 433 | name: koji-hub 434 | weight: 100 435 | wildcardPolicy: None 436 | parameters: 437 | - name: PUBLIC_IP 438 | displayName: Public IP of openshift router 439 | value: 10.3.9.64 440 | required: true 441 | - name: NAMESPACE 442 | displayName: Current namespace 443 | value: koji 444 | required: true 445 | - name: KOJI_DOJO_REMOTE 446 | displayName: Remote for koji dojo 447 | value: https://github.com/release-engineering/koji-dojo 448 | required: true 449 | - name: KOJI_DOJO_BRANCH 450 | displayName: koji dojo branch 451 | value: master 452 | required: true 453 | --------------------------------------------------------------------------------