diff --git a/playbooks/openshift-apps/fasjson.yml b/playbooks/openshift-apps/fasjson.yml new file mode 100644 index 0000000000..f2f05862c9 --- /dev/null +++ b/playbooks/openshift-apps/fasjson.yml @@ -0,0 +1,109 @@ +- name: make the app be real + hosts: os_masters[0]:os_masters_stg[0] + user: root + gather_facts: False + + vars_files: + - /srv/web/infra/ansible/vars/global.yml + - "/srv/private/ansible/vars.yml" + - /srv/web/infra/ansible/vars/{{ ansible_distribution }}.yml + + vars: + + roles: + - role: openshift/project + app: fasjson + description: "Accounts API" + appowners: + - abompard + - pingou + tags: + - apply-appowners + when: env == "production" + - role: openshift/project + app: fasjson + description: "Accounts API" + appowners: + - abompard + - pingou + - nils + - ryanlerch + tags: + - apply-appowners + when: env == "staging" + + # Declare the service in IPA + - role: ipa/service + host: "fasjson{{ env_suffix }}.fedoraproject.org" + service: HTTP + + # Setup kerberos delegation + - role: ipa/servicedelegationtarget + name: ipa-http + members: + - host: {{ ipa_server }} + service: HTTP + - role: ipa/servicedelegationrule + name: fasjson + members: + - host: "fasjson{{ env_suffix }}.fedoraproject.org" + service: HTTP + targets: + - ipa-http + - ipa-ldap + # The ipa-ldap delegation target is declared during IPA installation + + # Keytabs + - role: openshift/keytab + app: fasjson + key: host + secret_name: fasjson-keytab-host + service: host + host: "fasjson{{ env_suffix }}.fedoraproject.org" + - role: openshift/keytab + app: fasjson + key: http + secret_name: fasjson-keytab-http + service: HTTP + host: "fasjson{{ env_suffix }}.fedoraproject.org" + + - role: openshift/imagestream + app: fasjson + imagename: fasjson + + - role: openshift/object + app: fasjson + template: buildconfig.yml + objectname: buildconfig.yml + + - role: openshift/object + app: fasjson + template: configmap.yml + objectname: configmap.yml + + - role: openshift/ipa-client + app: fasjson + + - role: openshift/object + app: fasjson + file: service.yml + objectname: service.yml + + - role: openshift/object + app: fasjson + template: route.yml + objectname: route.yml + + - role: openshift/object + app: fasjson + template: secret-webhook.yml + objectname: secret-webhook.yml + + - role: openshift/object + app: fasjson + template: deploymentconfig.yml + objectname: deploymentconfig.yml + + - role: openshift/start-build + app: fasjson + buildname: fasjson diff --git a/roles/ipa/servicedelegationrule/defaults/main.yml b/roles/ipa/servicedelegationrule/defaults/main.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/roles/ipa/servicedelegationrule/tasks/main.yml b/roles/ipa/servicedelegationrule/tasks/main.yml new file mode 100644 index 0000000000..562e772203 --- /dev/null +++ b/roles/ipa/servicedelegationrule/tasks/main.yml @@ -0,0 +1,52 @@ +--- +- name: Get admin ticket + delegate_to: "{{ ipa_server }}" + shell: echo "{{ipa_admin_password}}" | kinit admin + check_mode: no + changed_when: "1 != 1" + tags: + - config + - krb5 + + +- name: Create servicedelegationrule entry + delegate_to: "{{ ipa_server }}" + command: ipa servicedelegationrule-add {{name}}-delegation + register: add_result + check_mode: no + changed_when: "'Added service delegation rule' in add_result.stdout" + failed_when: "not ('Added service delegation rule' in add_result.stdout or 'already exists' in add_result.stderr)" + tags: + - config + - krb5 + +- name: Add servicedelegationrule members + delegate_to: "{{ ipa_server }}" + command: ipa servicedelegationrule-add-member {{name}}-delegation --principals={{item.service}}/{{item.host}}@{{ipa_realm}} + loop: "{{ members }}" + register: add_member_result + check_mode: no + changed_when: "'Number of members added 1' in add_member_result.stdout" + failed_when: "not ('Number of members added 1' in add_member_result.stdout or 'Number of members added 0' in add_member_result.stderr)" + tags: + - config + - krb5 + +- name: Add servicedelegationrule targets + delegate_to: "{{ ipa_server }}" + command: ipa servicedelegationrule-add-target {{name}}-delegation --servicedelegationtargets={{item}}-delegation-targets + loop: "{{ targets }}" + register: add_target_result + check_mode: no + changed_when: "'Number of members added 1' in add_target_result.stdout" + failed_when: "not ('Number of members added 1' in add_target_result.stdout or 'Number of members added 0' in add_target_result.stderr)" + tags: + - config + - krb5 + +- name: Destroy admin ticket + delegate_to: "{{ ipa_server }}" + command: kdestroy -A + tags: + - config + - krb5 diff --git a/roles/ipa/servicedelegationtarget/defaults/main.yml b/roles/ipa/servicedelegationtarget/defaults/main.yml new file mode 100644 index 0000000000..d58c011d07 --- /dev/null +++ b/roles/ipa/servicedelegationtarget/defaults/main.yml @@ -0,0 +1 @@ +name: "{{ host }}-{{ service|lower }}" diff --git a/roles/ipa/servicedelegationtarget/tasks/main.yml b/roles/ipa/servicedelegationtarget/tasks/main.yml new file mode 100644 index 0000000000..53cd1adc2e --- /dev/null +++ b/roles/ipa/servicedelegationtarget/tasks/main.yml @@ -0,0 +1,40 @@ +--- +- name: Get admin ticket + delegate_to: "{{ ipa_server }}" + shell: echo "{{ipa_admin_password}}" | kinit admin + check_mode: no + changed_when: "1 != 1" + tags: + - config + - krb5 + + +- name: Create servicedelegationtarget entry + delegate_to: "{{ ipa_server }}" + command: ipa servicedelegationtarget-add {{name}}-delegation-targets + register: add_result + check_mode: no + changed_when: "'Added service delegation target' in add_result.stdout" + failed_when: "not ('Added service delegation target' in add_result.stdout or 'already exists' in add_result.stderr)" + tags: + - config + - krb5 + +- name: Add servicedelegationtarget members + delegate_to: "{{ ipa_server }}" + command: ipa servicedelegationtarget-add-member {{name}}-delegation-targets --principals={{item.service}}/{{item.host}}@{{ipa_realm}} + loop: "{{ members }}" + register: add_member_result + check_mode: no + changed_when: "'Number of members added 1' in add_member_result.stdout" + failed_when: "not ('Number of members added 1' in add_member_result.stdout or 'Number of members added 0' in add_member_result.stderr)" + tags: + - config + - krb5 + +- name: Destroy admin ticket + delegate_to: "{{ ipa_server }}" + command: kdestroy -A + tags: + - config + - krb5 diff --git a/roles/openshift-apps/fasjson/files/service.yml b/roles/openshift-apps/fasjson/files/service.yml new file mode 100644 index 0000000000..7f99e5b13c --- /dev/null +++ b/roles/openshift-apps/fasjson/files/service.yml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: fasjson-web + labels: + app: fasjson +spec: + ports: + - name: web + port: 8080 + targetPort: 8080 + selector: + app: fasjson + deploymentconfig: fasjson diff --git a/roles/openshift-apps/fasjson/templates/Dockerfile b/roles/openshift-apps/fasjson/templates/Dockerfile new file mode 100644 index 0000000000..ab9d8699da --- /dev/null +++ b/roles/openshift-apps/fasjson/templates/Dockerfile @@ -0,0 +1,43 @@ +FROM fedora:32 +LABEL \ + name="fasjson" \ + vendor="Fedora Infrastructure" \ + license="GPLv3+" +ENV HOME=/tmp +RUN dnf install -y \ + openldap-clients \ + vim \ + git \ + python3-pip \ + python3-setuptools \ + ipa-client \ + gcc \ + python-devel \ + krb5-devel \ + openldap-devel \ + httpd \ + mod_auth_gssapi \ + mod_session \ + policycoreutils-python-utils \ + python3-mod_wsgi \ + python3-dns \ + python3-flask \ + python3-gssapi \ + python3-ldap \ + python3-pip \ + python3-wheel && \ + dnf autoremove -y && \ + dnf clean all -y +RUN git clone https://github.com/fedora-infra/fasjson.git && \ + pushd fasjson && \ + git checkout {{ (env == 'production')|ternary('stable', 'staging') }} && \ + pip-3 install . && \ + mkdir -p /usr/share/fasjson && \ + cp ansible/roles/fasjson/files/fasjson.wsgi /usr/share/fasjson && \ + popd && \ + rm -rf fasjson +RUN rm -f /etc/krb5.conf && ln -sf /etc/krb5/krb5.conf /etc/krb5.conf && \ + ln -sf /etc/keytabs/host /etc/krb5.keytab && \ + rm -f /etc/openldap/ldap.conf && ln -sf /etc/ipa/ldap.conf /etc/openldap/ldap.conf +EXPOSE 8080 +ENTRYPOINT bash /etc/fasjson/start.sh diff --git a/roles/openshift-apps/fasjson/templates/buildconfig.yml b/roles/openshift-apps/fasjson/templates/buildconfig.yml new file mode 100644 index 0000000000..fa2fc4e9da --- /dev/null +++ b/roles/openshift-apps/fasjson/templates/buildconfig.yml @@ -0,0 +1,30 @@ +{% macro load_file(filename) %}{% include filename %}{%- endmacro -%} +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + name: fasjson + labels: + app: fasjson + build: fasjson +spec: + runPolicy: Serial + source: + type: Dockerfile + dockerfile: |- + {{ load_file('Dockerfile') | indent(6) }} + strategy: + type: Docker + output: + to: + kind: ImageStreamTag + name: fasjson:latest + triggers: + - type: ImageChange + - type: GitHub +{% if fasjson_stg_github_secret is defined and env == 'staging' %} + github: + secret: "{{ fasjson_stg_github_secret }}" +{% elif fasjson_github_secret is defined and env == 'production' %} + github: + secret: "{{ fasjson_github_secret }}" +{% endif %} diff --git a/roles/openshift-apps/fasjson/templates/configmap.yml b/roles/openshift-apps/fasjson/templates/configmap.yml new file mode 100644 index 0000000000..4b3beacbda --- /dev/null +++ b/roles/openshift-apps/fasjson/templates/configmap.yml @@ -0,0 +1,26 @@ +{% macro load_file(filename) %}{% include filename %}{%- endmacro -%} +--- +apiVersion: v1 +kind: List +metadata: {} +items: +- apiVersion: v1 + kind: ConfigMap + metadata: + name: fasjson-config + labels: + app: fasjson + data: + start.sh: |- + {{ load_file('start.sh') | indent(6) }} + httpd.conf: |- + {{ load_file('httpd.conf') | indent(6) }} +- apiVersion: v1 + kind: ConfigMap + metadata: + name: krb5-config + labels: + app: fasjson + data: + krb5.conf: |- + {{ load_file('krb5.conf') | indent(6) }} diff --git a/roles/openshift-apps/fasjson/templates/deploymentconfig.yml b/roles/openshift-apps/fasjson/templates/deploymentconfig.yml new file mode 100644 index 0000000000..51886453ba --- /dev/null +++ b/roles/openshift-apps/fasjson/templates/deploymentconfig.yml @@ -0,0 +1,94 @@ +apiVersion: apps.openshift.io/v1 +kind: DeploymentConfig +metadata: + name: fasjson + labels: + app: fasjson +spec: + replicas: 1 + selector: + app: fasjson + deploymentconfig: fasjson + strategy: + type: Rolling + activeDeadlineSeconds: 21600 + rollingParams: + intervalSeconds: 1 + maxSurge: 25% + maxUnavailable: 25% + timeoutSeconds: 600 + updatePeriodSeconds: 1 + template: + metadata: + creationTimestamp: null + labels: + app: fasjson + deploymentconfig: fasjson + spec: + containers: + - name: fasjson + imagePullPolicy: Always + ports: + - containerPort: 8080 + volumeMounts: + - name: keytab-host-volume + mountPath: /etc/keytabs/host + subPath: host + readOnly: true + - name: keytab-http-volume + mountPath: /etc/keytabs/http + subPath: http + readOnly: true + - name: krb-config-volume + mountPath: /etc/krb5 + readOnly: true + - name: fasjson-config-volume + mountPath: /etc/fasjson + readOnly: true + - name: ipa-config-volume + mountPath: /etc/ipa + readOnly: true + - name: httpdir + mountPath: /httpdir + livenessProbe: + timeoutSeconds: 10 + initialDelaySeconds: 10 + periodSeconds: 60 + httpGet: + path: /healthz/live + port: 8080 + readinessProbe: + timeoutSeconds: 10 + initialDelaySeconds: 5 + periodSeconds: 60 + httpGet: + path: /healthz/ready + port: 8080 + volumes: + - name: fasjson-config-volume + configMap: + name: fasjson-config + - name: keytab-volume-host + secret: + secretName: fasjson-keytab-host + - name: keytab-volume-http + secret: + secretName: fasjson-keytab-http + - name: krb-config-volume + configMap: + name: krb5-config + - name: ipa-config-volume + configMap: + name: ipa-config + - name: httpdir + emptyDir: {} + triggers: + - imageChangeParams: + automatic: true + containerNames: + - fasjson + from: + kind: ImageStreamTag + name: fasjson:latest + type: ImageChange + - type: ConfigChange diff --git a/roles/openshift-apps/fasjson/templates/httpd.conf b/roles/openshift-apps/fasjson/templates/httpd.conf new file mode 100644 index 0000000000..c953f5f1b8 --- /dev/null +++ b/roles/openshift-apps/fasjson/templates/httpd.conf @@ -0,0 +1,95 @@ +Listen 0.0.0.0:8080 +ServerRoot "/httpdir" +PidFile "/httpdir/httpd.pid" +LoadModule authn_file_module modules/mod_authn_file.so +LoadModule authn_anon_module modules/mod_authn_anon.so +LoadModule authz_user_module modules/mod_authz_user.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule include_module modules/mod_include.so +LoadModule log_config_module modules/mod_log_config.so +LoadModule env_module modules/mod_env.so +LoadModule ext_filter_module modules/mod_ext_filter.so +LoadModule expires_module modules/mod_expires.so +LoadModule headers_module modules/mod_headers.so +LoadModule mime_module modules/mod_mime.so +LoadModule status_module modules/mod_status.so +LoadModule negotiation_module modules/mod_negotiation.so +LoadModule dir_module modules/mod_dir.so +LoadModule alias_module modules/mod_alias.so +LoadModule rewrite_module modules/mod_rewrite.so +LoadModule version_module modules/mod_version.so +LoadModule wsgi_module modules/mod_wsgi_python3.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule unixd_module modules/mod_unixd.so +LoadModule mpm_event_module modules/mod_mpm_event.so +LoadModule request_module modules/mod_request.so +LoadModule auth_gssapi_module modules/mod_auth_gssapi.so +LoadModule session_module modules/mod_session.so +LoadModule session_cookie_module modules/mod_session_cookie.so +LoadModule session_dbd_module modules/mod_session_dbd.so +LoadModule auth_form_module modules/mod_auth_form.so +LoadModule setenvif_module modules/mod_setenvif.so + +StartServers 20 +ServerLimit 100 +MaxRequestsPerChild 2000 +MaxRequestWorkers 100 +TypesConfig /etc/mime.types +AddDefaultCharset UTF-8 +CoreDumpDirectory /tmp + +# Logging. Don't log OpenShift's probes +SetEnvIf Request_URI "^/healthz/" dontlog +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +CustomLog /httpdir/access.log combined env=!dontlog +ErrorLog /httpdir/error.log +LogLevel info + +WSGISocketPrefix run/wsgi +WSGIDaemonProcess fasjson processes=4 threads=1 maximum-requests=500 \ + display-name=%{GROUP} socket-timeout=2147483647 \ + lang=C.UTF-8 locale=C.UTF-8 home=/httpdir +WSGIImportScript /usr/share/fasjson/fasjson.wsgi \ + process-group=fasjson application-group=fasjson +WSGIScriptAlias / /usr/share/fasjson/fasjson.wsgi +WSGIScriptReloading Off +WSGIRestrictStdout Off +WSGIRestrictSignal Off +#WSGIPythonOptimize 1 # This causes the ldap module to fail + + + WSGIProcessGroup fasjson + WSGIApplicationGroup fasjson + + Require all granted + ErrorDocument 401 /errors/401 + ErrorDocument 403 /errors/403 + ErrorDocument 404 /errors/404 + ErrorDocument 500 /errors/500 + + + + AuthType GSSAPI + AuthName "Kerberos Login" + GssapiUseSessions On + Session On + SessionCookieName ipa_session path=/;httponly;secure; + SessionHeader IPASESSION + GssapiSessionKey file:/httpdir/run/session.key + + GssapiCredStore keytab:/etc/keytabs/httpd + GssapiImpersonate On + GssapiDelegCcacheDir /httpdir/run/ccaches + GssapiDelegCcachePerms mode:0660 + GssapiUseS4U2Proxy on + GssapiAllowedMech krb5 + + Require valid-user + + Header always append X-Frame-Options DENY + Header always append Content-Security-Policy "frame-ancestors 'none'" + Header unset Set-Cookie + Header unset ETag + FileETag None + diff --git a/roles/openshift-apps/fasjson/templates/krb5.conf b/roles/openshift-apps/fasjson/templates/krb5.conf new file mode 100644 index 0000000000..fc1d24dcdc --- /dev/null +++ b/roles/openshift-apps/fasjson/templates/krb5.conf @@ -0,0 +1,29 @@ +includedir /etc/krb5.conf.d/ + +[libdefaults] + default_realm = {{ ipa_realm }} + dns_lookup_realm = false + dns_lookup_kdc = false + rdns = false + dns_canonicalize_hostname = false + ticket_lifetime = 24h + forwardable = true + udp_preference_limit = 0 + default_ccache_name = KEYRING:persistent:%{uid} + +[realms] + {{ ipa_realm }} = { + kdc = {{ ipa_server }}:88 + master_kdc = {{ ipa_server }}:88 + admin_server = {{ ipa_server }}:749 + kpasswd_server = {{ ipa_server }}:464 + default_domain = {{ ipa_realm | lower }} + pkinit_anchors = FILE:/etc/ipa/ca.crt + pkinit_pool = FILE:/etc/ipa/ca.crt + } + +[domain_realm] + {{ env_suffix }}.fedoraproject.org = {{ ipa_realm }} + {{ ipa_realm | lower }} = {{ ipa_realm }} + {{ inventory_hostname }} = {{ ipa_realm }} + fasjson{{ env_suffix }}.fedoraproject.org = {{ ipa_realm }} diff --git a/roles/openshift-apps/fasjson/templates/route.yml b/roles/openshift-apps/fasjson/templates/route.yml new file mode 100644 index 0000000000..2172d3276f --- /dev/null +++ b/roles/openshift-apps/fasjson/templates/route.yml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Route +metadata: + name: fasjson-web + labels: + app: fasjson +spec: + host: fasjson{{ env_suffix }}.fedoraproject.org + port: + targetPort: web + to: + kind: Service + name: fasjson-web + tls: + termination: edge + insecureEdgeTerminationPolicy: Redirect diff --git a/roles/openshift-apps/fasjson/templates/secret-webhook.yml b/roles/openshift-apps/fasjson/templates/secret-webhook.yml new file mode 100644 index 0000000000..da524a1662 --- /dev/null +++ b/roles/openshift-apps/fasjson/templates/secret-webhook.yml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: fasjson-github-webhook-secret +data: + WebHookSecretKey: "{{ (env == 'production')|ternary(fasjson_github_secret, fasjson_stg_github_secret) }}" +type: Opaque diff --git a/roles/openshift-apps/fasjson/templates/start.sh b/roles/openshift-apps/fasjson/templates/start.sh new file mode 100644 index 0000000000..f52aa8b5af --- /dev/null +++ b/roles/openshift-apps/fasjson/templates/start.sh @@ -0,0 +1,7 @@ +#!/bin/sh +rm -rf /httpdir/* +mkdir /httpdir/run/ /httpdir/run/ccaches/ +ln -s /etc/httpd/modules /httpdir/modules +truncate --size=0 /httpdir/access.log /httpdir/error.log +tail -qf /httpdir/access.log /httpdir/error.log & +exec httpd -f /etc/fasjson/httpd.conf -DFOREGROUND -DNO_DETACH