Merge branch 'ansible-haproxy' into develop
diff --git a/dev-tools/ansible/airavata.yml b/dev-tools/ansible/airavata.yml
index 5969528..cc55664 100644
--- a/dev-tools/ansible/airavata.yml
+++ b/dev-tools/ansible/airavata.yml
@@ -43,6 +43,7 @@
     - role: common
       become: yes
       become_user: "{{user}}"
+    - letsencrypt
     - role: api-orch
       become: yes
       become_user: "{{user}}"
diff --git a/dev-tools/ansible/apiserver.yml b/dev-tools/ansible/apiserver.yml
index 8a22961..505d73a 100644
--- a/dev-tools/ansible/apiserver.yml
+++ b/dev-tools/ansible/apiserver.yml
@@ -31,6 +31,7 @@
     - role: common
       become: yes
       become_user: "{{user}}"
+    - letsencrypt
     - role: api-orch
       become: yes
       become_user: "{{user}}"
diff --git a/dev-tools/ansible/inventories/scigap/develop/group_vars/all/vars.yml b/dev-tools/ansible/inventories/scigap/develop/group_vars/all/vars.yml
index 97a388a..4d88f8b 100644
--- a/dev-tools/ansible/inventories/scigap/develop/group_vars/all/vars.yml
+++ b/dev-tools/ansible/inventories/scigap/develop/group_vars/all/vars.yml
@@ -71,6 +71,7 @@
 api_server_name: "apiserver-node0"
 api_server_host: "{{ groups['api-orch'][0] }}"
 api_server_port: "8930"
+api_server_public_hostname: "apidev.scigap.org"
 api_secured: "true"
 tls_enable: "true"
 api_server_tls_port: "9930"
diff --git a/dev-tools/ansible/roles/api-orch/defaults/main.yml b/dev-tools/ansible/roles/api-orch/defaults/main.yml
index e1c0476..1c7c2f9 100644
--- a/dev-tools/ansible/roles/api-orch/defaults/main.yml
+++ b/dev-tools/ansible/roles/api-orch/defaults/main.yml
@@ -47,3 +47,8 @@
 
 thrift_client_pool_abandoned_removal_enabled: false
 thrift_client_pool_abandoned_removal_logged: false
+
+api_server_public_hostname: "localhost"
+haproxy_conf_destination: "/etc/haproxy/haproxy.cfg"
+haproxy_api_server_ssl_cert: "/etc/ssl/{{ api_server_public_hostname }}/{{ api_server_public_hostname }}.pem"
+api_server_letsencrypt_ssl_cert: "/etc/letsencrypt/live/{{ api_server_public_hostname }}/cert.pem"
diff --git a/dev-tools/ansible/roles/api-orch/files/prepareLetsEncryptCertificates.sh b/dev-tools/ansible/roles/api-orch/files/prepareLetsEncryptCertificates.sh
new file mode 100644
index 0000000..ba99e0a
--- /dev/null
+++ b/dev-tools/ansible/roles/api-orch/files/prepareLetsEncryptCertificates.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+umask 077
+
+# Loop through all Let's Encrypt certificates
+for CERTIFICATE in `find /etc/letsencrypt/live/* -type d`; do
+
+  CERTIFICATE=`basename $CERTIFICATE`
+
+  # Combine certificate and private key to single file
+  cat /etc/letsencrypt/live/$CERTIFICATE/fullchain.pem /etc/letsencrypt/live/$CERTIFICATE/privkey.pem > /etc/ssl/$CERTIFICATE/$CERTIFICATE.pem
+
+done
diff --git a/dev-tools/ansible/roles/api-orch/handlers/main.yml b/dev-tools/ansible/roles/api-orch/handlers/main.yml
new file mode 100644
index 0000000..5ab12f6
--- /dev/null
+++ b/dev-tools/ansible/roles/api-orch/handlers/main.yml
@@ -0,0 +1,24 @@
+#
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+---
+- name: restart haproxy
+  service: name=haproxy state=reloaded enabled=yes
+  become: yes
diff --git a/dev-tools/ansible/roles/api-orch/tasks/haproxy/install_deps_Centos_7.yml b/dev-tools/ansible/roles/api-orch/tasks/haproxy/install_deps_Centos_7.yml
new file mode 100644
index 0000000..ee115be
--- /dev/null
+++ b/dev-tools/ansible/roles/api-orch/tasks/haproxy/install_deps_Centos_7.yml
@@ -0,0 +1,28 @@
+#
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+---
+
+- name: yum install haproxy18 (Centos 7)
+  yum: name=haproxy18 state=present
+  become: true
+
+
+...
diff --git a/dev-tools/ansible/roles/api-orch/tasks/haproxy/install_deps_Rocky_8.yml b/dev-tools/ansible/roles/api-orch/tasks/haproxy/install_deps_Rocky_8.yml
new file mode 100644
index 0000000..8d48c47
--- /dev/null
+++ b/dev-tools/ansible/roles/api-orch/tasks/haproxy/install_deps_Rocky_8.yml
@@ -0,0 +1,32 @@
+#
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+---
+
+- name: dnf install haproxy (Rocky 8)
+  dnf: name={{ package }} state=latest
+  loop:
+    - haproxy
+  loop_control:
+    loop_var: package
+  become: true
+  become_user: root
+
+...
diff --git a/dev-tools/ansible/roles/api-orch/tasks/main.yml b/dev-tools/ansible/roles/api-orch/tasks/main.yml
index f121fcb..19f1cb3 100644
--- a/dev-tools/ansible/roles/api-orch/tasks/main.yml
+++ b/dev-tools/ansible/roles/api-orch/tasks/main.yml
@@ -82,6 +82,64 @@
           owner={{ user }}
           group={{ group }}
 
+# Create a SSL certificate for the api server
+
+- name: allow http for Let's Encrypt certificate renewal
+  firewalld:
+    zone: public
+    permanent: yes
+    state: enabled
+    immediate: yes
+    service: http
+  become_user: root
+
+- name: copy prepareLetsEncryptCertificates.sh script
+  copy:
+    src: prepareLetsEncryptCertificates.sh
+    dest: "/etc/haproxy/"
+    mode: 755
+  become_user: root
+
+# - name: copy cron job to renew certificates
+#   copy:
+#     src: renewLetsEncryptCertificates.sh
+#     dest: /etc/cron.monthly/
+#     mode: 0755
+#   become_user: root
+
+- name: check if SSL certificate exists
+  stat:
+    path: "{{ api_server_letsencrypt_ssl_cert }}"
+  register: stat_api_server_ssl_cert_result
+  become: yes
+
+- name: generate certificate if it doesn't exist
+  command: certbot --standalone --non-interactive --agree-tos --email "{{ letsencrypt_email }}" -d {{ api_server_public_hostname }} certonly
+  become: yes
+  when: not stat_api_server_ssl_cert_result.stat.exists
+
+- name: set certificate renewal post-hook
+  command: certbot renew --installer null --standalone --post-hook "/etc/haproxy/prepareLetsEncryptCertificates.sh && systemctl reload haproxy.service" --quiet
+  become: yes
+
+# Use HAProxy to proxy SSL port to non-SSL port
+
+- name: Install HAProxy
+  include_tasks: install_deps_{{ ansible_distribution }}_{{ ansible_distribution_major_version }}.yml
+
+- name: Copy HAProxy config file
+  template: src=haproxy.cfg.j2
+            dest={{ haproxy_conf_destination }}
+            backup=true
+  become_user: root
+  notify:
+    - restart haproxy
+
+- name: start haproxy
+  service: name=haproxy state=started enabled=yes daemon_reload=yes
+  become: true
+  become_user: root
+
 - name: allow only selected networks to access Airavata Sharing Registry
   firewalld:
     zone: public
diff --git a/dev-tools/ansible/roles/api-orch/templates/haproxy.cfg.j2 b/dev-tools/ansible/roles/api-orch/templates/haproxy.cfg.j2
new file mode 100644
index 0000000..b3b5d81
--- /dev/null
+++ b/dev-tools/ansible/roles/api-orch/templates/haproxy.cfg.j2
@@ -0,0 +1,75 @@
+#---------------------------------------------------------------------
+# Example configuration for a possible web application.  See the
+# full configuration options online.
+#
+#   https://www.haproxy.org/download/1.8/doc/configuration.txt
+#
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+# Global settings
+#---------------------------------------------------------------------
+global
+    # to have these messages end up in /var/log/haproxy.log you will
+    # need to:
+    #
+    # 1) configure syslog to accept network log events.  This is done
+    #    by adding the '-r' option to the SYSLOGD_OPTIONS in
+    #    /etc/sysconfig/syslog
+    #
+    # 2) configure local2 events to go to the /var/log/haproxy.log
+    #   file. A line like the following can be added to
+    #   /etc/sysconfig/syslog
+    #
+    #    local2.*                       /var/log/haproxy.log
+    #
+    log         127.0.0.1 local2
+
+    chroot      /var/lib/haproxy
+    pidfile     /var/run/haproxy.pid
+    maxconn     4000
+    user        haproxy
+    group       haproxy
+    daemon
+
+    # turn on stats unix socket
+    stats socket /var/lib/haproxy/stats
+
+    # utilize system-wide crypto-policies
+    ssl-default-bind-ciphers PROFILE=SYSTEM
+    ssl-default-server-ciphers PROFILE=SYSTEM
+
+#---------------------------------------------------------------------
+# common defaults that all the 'listen' and 'backend' sections will
+# use if not designated in their block
+#---------------------------------------------------------------------
+defaults
+    log                     global
+    option                  httplog
+    option                  dontlognull
+    option http-server-close
+    option forwardfor       except 127.0.0.0/8
+    option                  redispatch
+
+#---------------------------------------------------------------------
+# main frontend which proxys to the backends
+#---------------------------------------------------------------------
+frontend main
+  mode tcp
+  log global
+  option tcplog
+  bind *:{{ api_server_tls_port }} ssl crt {{ haproxy_api_server_ssl_cert }}
+  default_backend fix-backend
+
+#---------------------------------------------------------------------
+# static backend for serving up images, stylesheets and such
+#---------------------------------------------------------------------
+
+#---------------------------------------------------------------------
+# round robin balancing between the various backends
+#---------------------------------------------------------------------
+backend fix-backend
+  mode tcp
+  log global
+  option tcplog
+  server quickfix {{ airavata_api_host }}:{{ api_server_port }} check