From 112b538b74ea02ad3c7ac879873ed6429af0b608 Mon Sep 17 00:00:00 2001 From: Bas Zoetekouw Date: Tue, 8 Apr 2025 11:02:49 +0200 Subject: [PATCH 01/15] Add engineblock parameters for SBS integration --- roles/engine/defaults/main.yml | 9 +++++++++ roles/engine/templates/parameters.yml.j2 | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/roles/engine/defaults/main.yml b/roles/engine/defaults/main.yml index cdc888cd5..3721e2f54 100644 --- a/roles/engine/defaults/main.yml +++ b/roles/engine/defaults/main.yml @@ -17,6 +17,7 @@ engine_api_feature_consent_remove: 0 engine_api_feature_metadata_api: 1 engine_api_feature_deprovision: 1 engine_feature_send_user_attributes: 0 +engine_feature_enable_sbs_interrupt: 0 # Cutoff point for showing unfiltered IdPs on the WAYF engine_wayf_cutoff_point_for_showing_unfiltered_idps: 50 @@ -76,6 +77,14 @@ engine_stepup_gateway_sfo_entity_id: "https://{{ engine_stepup_gateway_domain }} # The single sign-on endpoint used for Stepup Gateway SFO callouts engine_stepup_gateway_sfo_sso_location: "https://{{ engine_stepup_gateway_domain }}/second-factor-only/single-sign-on" +# SBS interrupt settings +engine_sbs_base_url: "sbs.{{ base_domain }}" +engine_sbs_attributes_allowed: + - 'urn:mace:dir:attribute-def:eduPersonEntitlement' + - 'urn:mace:dir:attribute-def:uid' + - 'urn:mace:dir:attribute-def:eduPersonPrincipalName' + - 'urn:oid:1.3.6.1.4.1.24552.500.1.1.1.13' + ## The minimum priority of messages that will be logged engine_logging_passthru_level: NOTICE diff --git a/roles/engine/templates/parameters.yml.j2 b/roles/engine/templates/parameters.yml.j2 index 77903de1e..9b416fe66 100644 --- a/roles/engine/templates/parameters.yml.j2 +++ b/roles/engine/templates/parameters.yml.j2 @@ -228,6 +228,7 @@ parameters: feature_stepup_sfo_override_engine_entityid: {{ engine_feature_stepup_override_entityid | bool | to_json }} feature_enable_idp_initiated_flow: {{ engine_feature_idp_initiated_flow | bool | to_json }} feature_stepup_send_user_attributes: {{ engine_feature_send_user_attributes | bool | to_json }} + feature_enable_sram_interrupt: { { engine_feature_enable_sbs_interrupt | bool | to_json } } ########################################################################################## ## PROFILE SETTINGS ########################################################################################## @@ -310,3 +311,16 @@ parameters: # used in the authentication log record. The attributeName will be searched in the response attributes and if present # the log data will be enriched. The values of the response attributes are the final values after ARP and Attribute Manipulation. auth.log.attributes: {{ engine_log_attributes }} + + + ########################################################################################## + ## SBS external authorization/attribute enrichtment + ########################################################################################## + sram.api_token: "{{ engine_sbs_api_token | default('') }}" + sram.base_url: "https://{{ engine_sbs_base_url }}/api/users/" + sram.authz_location: "authz_eb" + sram.attributes_location: "authz_eb" + sram.attributes_location: "attributes_eb" + sram.interrupt_location: "interrupt" + sram.verify_peer: true + sram.allowed_attributes: {{ engine_sbs_attributes_allowed }} From 165e8263238fbf359b0daa7c76a9053a1a063f52 Mon Sep 17 00:00:00 2001 From: Bas Zoetekouw Date: Wed, 8 Apr 2026 15:19:04 +0200 Subject: [PATCH 02/15] Fix duplicate keys --- roles/engine/templates/parameters.yml.j2 | 1 - 1 file changed, 1 deletion(-) diff --git a/roles/engine/templates/parameters.yml.j2 b/roles/engine/templates/parameters.yml.j2 index 9b416fe66..edd6d80e6 100644 --- a/roles/engine/templates/parameters.yml.j2 +++ b/roles/engine/templates/parameters.yml.j2 @@ -319,7 +319,6 @@ parameters: sram.api_token: "{{ engine_sbs_api_token | default('') }}" sram.base_url: "https://{{ engine_sbs_base_url }}/api/users/" sram.authz_location: "authz_eb" - sram.attributes_location: "authz_eb" sram.attributes_location: "attributes_eb" sram.interrupt_location: "interrupt" sram.verify_peer: true From 2f16f085c611a270cf538217c272c6a1d244ef2e Mon Sep 17 00:00:00 2001 From: Bas Zoetekouw Date: Wed, 8 Apr 2026 15:37:15 +0200 Subject: [PATCH 03/15] Fix jinja template --- roles/engine/templates/parameters.yml.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/engine/templates/parameters.yml.j2 b/roles/engine/templates/parameters.yml.j2 index edd6d80e6..526104405 100644 --- a/roles/engine/templates/parameters.yml.j2 +++ b/roles/engine/templates/parameters.yml.j2 @@ -228,7 +228,7 @@ parameters: feature_stepup_sfo_override_engine_entityid: {{ engine_feature_stepup_override_entityid | bool | to_json }} feature_enable_idp_initiated_flow: {{ engine_feature_idp_initiated_flow | bool | to_json }} feature_stepup_send_user_attributes: {{ engine_feature_send_user_attributes | bool | to_json }} - feature_enable_sram_interrupt: { { engine_feature_enable_sbs_interrupt | bool | to_json } } + feature_enable_sram_interrupt: {{ engine_feature_enable_sbs_interrupt | bool | to_json }} ########################################################################################## ## PROFILE SETTINGS ########################################################################################## From 1083bc3b30558200e65b6a5d7d3ac827dd2c287b Mon Sep 17 00:00:00 2001 From: Martin van Es Date: Thu, 19 Mar 2026 16:48:37 +0100 Subject: [PATCH 04/15] Add ldap role --- .../haproxy/templates/haproxy_backend.cfg.j2 | 16 + .../haproxy/templates/haproxy_frontend.cfg.j2 | 46 ++- roles/ldap/defaults/main.yml | 41 ++ roles/ldap/files/eduMember.ldif | 27 ++ roles/ldap/files/eduPerson.ldif | 83 ++++ roles/ldap/files/groupOfMembers.ldif | 19 + roles/ldap/files/ldap-add | 51 +++ roles/ldap/files/ldapPublicKey.ldif | 21 + roles/ldap/files/logrotate_slapd | 13 + roles/ldap/files/rsyslog_slapd.conf | 2 + roles/ldap/files/sczGroup.ldif | 23 ++ roles/ldap/files/sramPerson.ldif | 23 ++ roles/ldap/files/voPerson.ldif | 44 +++ roles/ldap/handlers/main.yml | 24 ++ roles/ldap/tasks/admins.yml | 82 ++++ roles/ldap/tasks/main.yml | 369 ++++++++++++++++++ roles/ldap/templates/ldap-backup.sh.j2 | 19 + roles/ldap/templates/ldap.conf.j2 | 16 + roles/ldap/templates/slapd.service.j2 | 20 + roles/ldap/vars/main.yml | 1 + roles/redis/defaults/main.yml | 11 + roles/redis/handlers/main.yml | 6 + roles/redis/tasks/main.yml | 61 +++ roles/redis/templates/redis.conf.j2 | 3 + roles/redis/vars/main.yml | 1 + roles/sbs/defaults/main.yml | 166 ++++++++ roles/sbs/files/yarn.gpg | 243 ++++++++++++ roles/sbs/handlers/main.yml | 9 + roles/sbs/tasks/main.yml | 179 +++++++++ roles/sbs/templates/alembic.ini.j2 | 72 ++++ roles/sbs/templates/config.yml.j2 | 264 +++++++++++++ roles/sbs/templates/disclaimer.css.j2 | 6 + .../templates/saml_advanced_settings.json.j2 | 35 ++ roles/sbs/templates/saml_settings.json.j2 | 22 ++ roles/sbs/templates/sbs-apache.conf.j2 | 30 ++ roles/sbs/templates/sbs.service.j2 | 32 ++ roles/sbs/vars/main.yml | 1 + 37 files changed, 2066 insertions(+), 15 deletions(-) create mode 100644 roles/ldap/defaults/main.yml create mode 100644 roles/ldap/files/eduMember.ldif create mode 100644 roles/ldap/files/eduPerson.ldif create mode 100644 roles/ldap/files/groupOfMembers.ldif create mode 100644 roles/ldap/files/ldap-add create mode 100644 roles/ldap/files/ldapPublicKey.ldif create mode 100644 roles/ldap/files/logrotate_slapd create mode 100644 roles/ldap/files/rsyslog_slapd.conf create mode 100644 roles/ldap/files/sczGroup.ldif create mode 100644 roles/ldap/files/sramPerson.ldif create mode 100644 roles/ldap/files/voPerson.ldif create mode 100644 roles/ldap/handlers/main.yml create mode 100644 roles/ldap/tasks/admins.yml create mode 100644 roles/ldap/tasks/main.yml create mode 100644 roles/ldap/templates/ldap-backup.sh.j2 create mode 100644 roles/ldap/templates/ldap.conf.j2 create mode 100644 roles/ldap/templates/slapd.service.j2 create mode 100644 roles/ldap/vars/main.yml create mode 100644 roles/redis/defaults/main.yml create mode 100644 roles/redis/handlers/main.yml create mode 100644 roles/redis/tasks/main.yml create mode 100644 roles/redis/templates/redis.conf.j2 create mode 100644 roles/redis/vars/main.yml create mode 100644 roles/sbs/defaults/main.yml create mode 100644 roles/sbs/files/yarn.gpg create mode 100644 roles/sbs/handlers/main.yml create mode 100644 roles/sbs/tasks/main.yml create mode 100644 roles/sbs/templates/alembic.ini.j2 create mode 100644 roles/sbs/templates/config.yml.j2 create mode 100644 roles/sbs/templates/disclaimer.css.j2 create mode 100644 roles/sbs/templates/saml_advanced_settings.json.j2 create mode 100644 roles/sbs/templates/saml_settings.json.j2 create mode 100644 roles/sbs/templates/sbs-apache.conf.j2 create mode 100644 roles/sbs/templates/sbs.service.j2 create mode 100644 roles/sbs/vars/main.yml diff --git a/roles/haproxy/templates/haproxy_backend.cfg.j2 b/roles/haproxy/templates/haproxy_backend.cfg.j2 index d2387c033..ea52ac5b0 100644 --- a/roles/haproxy/templates/haproxy_backend.cfg.j2 +++ b/roles/haproxy/templates/haproxy_backend.cfg.j2 @@ -67,3 +67,19 @@ {% endfor %} {% endif %} {% endfor %} + +{% if haproxy_ldap_servers is defined %} +#--------------------------------------------------------------------- +# ldap backend +#--------------------------------------------------------------------- +backend ldap_servers + mode tcp + option tcpka + + option ldap-check + + {% for server in haproxy_ldap_servers -%} + server {{server.label}} {{server.ip}}:{{server.port}} ssl verify none check weight 10 {% if loop.index==1 %}on-marked-up shutdown-backup-sessions{% else %}backup{% endif %} + {% endfor %} +{% endfor %} +{% endig %} diff --git a/roles/haproxy/templates/haproxy_frontend.cfg.j2 b/roles/haproxy/templates/haproxy_frontend.cfg.j2 index 6082e9c03..4909a0074 100644 --- a/roles/haproxy/templates/haproxy_frontend.cfg.j2 +++ b/roles/haproxy/templates/haproxy_frontend.cfg.j2 @@ -12,8 +12,8 @@ frontend stats # ------------------------------------------------------------------- frontend internet_ip - bind {{ haproxy_sni_ip.ipv4 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent - bind {{ haproxy_sni_ip.ipv6 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent + bind {{ haproxy_sni_ip.ipv4 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent + bind {{ haproxy_sni_ip.ipv6 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent bind {{ haproxy_sni_ip.ipv4 }}:80 transparent bind {{ haproxy_sni_ip.ipv6 }}:80 transparent # Logging is done in the local_ip backend, otherwise all requests are logged twice @@ -30,7 +30,7 @@ frontend internet_ip http-request redirect scheme https code 301 if !{ ssl_fc } # Log the user agent in the httplogs capture request header User-agent len 256 - # Put the useragent header in a variable, shared between request and response. + # Put the useragent header in a variable, shared between request and response. http-request set-var(txn.useragent) req.fhdr(User-Agent) # The ACL below makes sure only supported http methods are allowed acl valid_method method {{ haproxy_supported_http_methods }} @@ -51,7 +51,7 @@ frontend internet_ip http-response replace-header Set-Cookie (?i)(^(?!.*samesite).*$) \1;\ SameSite=None if !no_same_site_uas # Remove an already present SameSite cookie attribute for unsupported browsers http-response replace-value Set-Cookie (^.*)(?i);\ *SameSite=(Lax|Strict|None)(.*$) \1\3 if no_same_site_uas - # Log whether the no_same_site_uas ACL has been hit + # Log whether the no_same_site_uas ACL has been hit http-request set-header samesitesupport samesite_notsupported if no_same_site_uas http-request set-header samesitesupport samesite_supported if !no_same_site_uas # We need a dummy backend in order to be able to rewrite the loadbalancer cookies @@ -66,7 +66,7 @@ frontend local_ip acl valid_vhost hdr(host) -f /etc/haproxy/acls/validvhostsunrestricted.acl acl staging req.cook(staging) -m str true acl staging src -f /etc/haproxy/acls/stagingips.acl - acl stagingvhost hdr(host) -i -M -f /etc/haproxy/maps/backendsstaging.map + acl stagingvhost hdr(host) -i -M -f /etc/haproxy/maps/backendsstaging.map use_backend %[req.hdr(host),lower,map(/etc/haproxy/maps/backendsstaging.map)] if stagingvhost staging use_backend %[req.hdr(host),lower,map(/etc/haproxy/maps/backends.map)] option httplog @@ -82,7 +82,7 @@ frontend local_ip http-request capture sc_http_req_rate(0) len 4 # Create an ACL when the request rate exceeds {{ haproxy_max_request_rate }} per 10s acl exceeds_max_request_rate_per_ip sc_http_req_rate(0) gt {{ haproxy_max_request_rate }} - # Measure and log the request rate per path and ip + # Measure and log the request rate per path and ip http-request track-sc1 base32+src table st_httpreqs_per_ip_and_path http-request capture sc_http_req_rate(1) len 4 # Some paths allow for a higher ratelimit. These are in a seperate mapfile @@ -96,7 +96,7 @@ frontend local_ip http-request deny if ! valid_vhost # Deny the request when the request rate exceeds {{ haproxy_max_request_rate }} per 10s http-request deny deny_status 429 if exceeds_max_request_rate_per_ip !allowlist - # Deny the request when the request rate per host header url path and src ip exceeds {{ haproxy_max_request_rate_ip_path }} per 1 m + # Deny the request when the request rate per host header url path and src ip exceeds {{ haproxy_max_request_rate_ip_path }} per 1 m http-request deny deny_status 429 if exceeds_max_request_rate_per_ip_and_path !allowlist # Create some http redirects {% if haproxy_securitytxt_target_url is defined %} @@ -111,8 +111,8 @@ frontend local_ip ## ------------------------------------------------------------------- frontend internet_restricted_ip - bind {{ haproxy_sni_ip_restricted.ipv4 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent - bind {{ haproxy_sni_ip_restricted.ipv6 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent + bind {{ haproxy_sni_ip_restricted.ipv4 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent + bind {{ haproxy_sni_ip_restricted.ipv6 }}:443 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 alpn h2,http/1.1 transparent bind {{ haproxy_sni_ip_restricted.ipv4 }}:80 transparent bind {{ haproxy_sni_ip_restricted.ipv6 }}:80 transparent # Logging is done in the local_ip_restriced backend, otherwise all requests are logged twice @@ -128,8 +128,8 @@ frontend internet_restricted_ip # We redirect all port 80 to port 443 http-request redirect scheme https code 301 if !{ ssl_fc } # Log the user agent in the httplogs - capture request header User-agent len 256 - # Put the useragent header in a variable, shared between request and response. + capture request header User-agent len 256 + # Put the useragent header in a variable, shared between request and response. http-request set-var(txn.useragent) req.fhdr(User-Agent) # The ACL below makes sure only supported http methods are allowed acl valid_method method {{ haproxy_supported_http_methods }} @@ -155,12 +155,12 @@ frontend internet_restricted_ip # frontend restricted ip addresses localhost # traffic coming back from the dummy backend ends up here # ------------------------------------------------------------------- -frontend localhost_restricted +frontend localhost_restricted bind 127.0.0.1:82 accept-proxy acl valid_vhost hdr(host) -f /etc/haproxy/acls/validvhostsrestricted.acl acl staging req.cook(staging) -m str true acl staging src -f /etc/haproxy/acls/stagingips.acl - acl stagingvhost hdr(host) -i -M -f /etc/haproxy/maps/backendsstaging.map + acl stagingvhost hdr(host) -i -M -f /etc/haproxy/maps/backendsstaging.map use_backend %[req.hdr(host),lower,map(/etc/haproxy/maps/backendsstaging.map)] if stagingvhost staging use_backend %[req.hdr(host),lower,map(/etc/haproxy/maps/backends.map)] option httplog @@ -177,7 +177,7 @@ frontend localhost_restricted # Create an ACL when the request rate exceeds {{ haproxy_max_request_rate }} per 10s acl exceeds_max_request_rate_per_ip sc_http_req_rate(0) gt {{ haproxy_max_request_rate }} http-request deny deny_status 429 if exceeds_max_request_rate_per_ip !allowlist - # Measure and log the request rate per path and ip + # Measure and log the request rate per path and ip http-request track-sc1 base32+src table st_httpreqs_per_ip_and_path http-request capture sc_http_req_rate(1) len 4 # Some paths allow for a higher ratelimit. These are in a seperate mapfile @@ -191,7 +191,7 @@ frontend localhost_restricted http-request deny if ! valid_vhost # Deny the request when the request rate exceeds {{ haproxy_max_request_rate }} per 10s http-request deny deny_status 429 if exceeds_max_request_rate_per_ip !allowlist - # Deny the request when the request rate per host header url path and src ip exceeds {{ haproxy_max_request_rate_ip_path }} per 1 m + # Deny the request when the request rate per host header url path and src ip exceeds {{ haproxy_max_request_rate_ip_path }} per 1 m http-request deny deny_status 429 if exceeds_max_request_rate_per_ip_and_path !allowlist # Create some http redirects {% if haproxy_securitytxt_target_url is defined %} @@ -201,3 +201,19 @@ frontend localhost_restricted http-request redirect location %[base,map_reg(/etc/haproxy/maps/redirects.map)] if { base,map_reg(/etc/haproxy/maps/redirects.map) -m found } {% endif %} + +{% if haproxy_ldap_servers is defined %} +#-------------------------------------------------------------------- +# frontend public ips ldap +# ------------------------------------------------------------------- +listen ldap + mode tcp + no option dontlognull + option tcplog + option logasap + timeout client 900s + timeout server 901s + bind {{ haproxy_sni_ip.ipv4 }}:636 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 transparent + bind {{ haproxy_sni_ip.ipv6 }}:636 ssl crt-list /etc/haproxy/maps/certlist.lst ssl crt /etc/haproxy/certs/ no-sslv3 no-tlsv10 no-tlsv11 transparent + use_backend ldap_servers +{% endif %} diff --git a/roles/ldap/defaults/main.yml b/roles/ldap/defaults/main.yml new file mode 100644 index 000000000..e60ab603c --- /dev/null +++ b/roles/ldap/defaults/main.yml @@ -0,0 +1,41 @@ +--- +ldap: "{{ ldap_defaults | combine(ldap_overrides) }}" + +ldap_defaults: + image: "ghcr.io/surfscz/sram-ldap:main" + conf_dir: "{{ current_release_appdir }}/ldap" + ldif_dir: "{{ ldap_defaults.conf_dir }}/schema" + certs_dir: "{{ ldap_defaults.conf_dir }}/certs" + backup_dir: "{{ ldap_defaults.conf_dir }}/ldap" + data_dir: "{{ ldap_defaults.conf_dir}}/data" + uri: "ldap://localhost/" + + user: "openldap" + group: "openldap" + + # admin_group: "ldap_admin" + admins: + - name: Admin + uid: admin + pw_hash: + sshkey: "" + + loglevel: "stats stats2 filter" + + services_password: secret + monitor_password: secret + ldap_monitor_password: secret + + uri: "ldap://localhost/" + rid_prefix: "ldap://" + + base_domain: "{{ base_domain }}" + base_dn: >- + {{ ((ldap_defaults.base_domain.split('.')|length)*['dc=']) | + zip(ldap_defaults.base_domain.split('.')) | list | map('join', '') | list | join(',') }} + services_dn: + basedn: "dc=services,{{ ldap_defaults.base_dn }}" + o: "Services" + binddn: "cn=admin,{{ ldap_defaults.base_dn }}" + + hosts: {} diff --git a/roles/ldap/files/eduMember.ldif b/roles/ldap/files/eduMember.ldif new file mode 100644 index 000000000..42894d596 --- /dev/null +++ b/roles/ldap/files/eduMember.ldif @@ -0,0 +1,27 @@ +dn: cn=eduMember,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: eduMember +# Internet X.500 Schema for Ldappc +# Includes the eduMember ObjectClass schema +# +# +# An auxiliary object class, "eduMember," is a convenient container +# for an extensible set of attributes concerning group memberships. +# At this time, the only attributes specified as belonging to the +# object class are "isMemberOf" and "hasMember." +# +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.5.1.1 + NAME 'isMemberOf' + DESC 'identifiers for groups to which containing entity belongs' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.5.1.2 + NAME 'hasMember' + DESC 'identifiers for entities that are members of the group' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcObjectClasses: ( 1.3.6.1.4.1.5923.1.5.2.1 + NAME 'eduMember' + AUXILIARY + MAY ( isMemberOf $ hasMember ) + ) diff --git a/roles/ldap/files/eduPerson.ldif b/roles/ldap/files/eduPerson.ldif new file mode 100644 index 000000000..e4f2c96a0 --- /dev/null +++ b/roles/ldap/files/eduPerson.ldif @@ -0,0 +1,83 @@ +dn: cn=eduperson,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: eduperson +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.1 + NAME 'eduPersonAffiliation' + DESC 'eduPerson per Internet2 and EDUCAUSE' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.7 + NAME 'eduPersonEntitlement' + DESC 'eduPerson per Internet2 and EDUCAUSE' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.2 + NAME 'eduPersonNickName' + DESC 'eduPerson per Internet2 and EDUCAUSE' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.3 + NAME 'eduPersonOrgDN' + DESC 'eduPerson per Internet2 and EDUCAUSE' + EQUALITY distinguishedNameMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.4 + NAME 'eduPersonOrgUnitDN' + DESC 'eduPerson per Internet2 and EDUCAUSE' + EQUALITY distinguishedNameMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.5 + NAME 'eduPersonPrimaryAffiliation' + DESC 'eduPerson per Internet2 and EDUCAUSE' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.8 + NAME 'eduPersonPrimaryOrgUnitDN' + DESC 'eduPerson per Internet2 and EDUCAUSE' + EQUALITY distinguishedNameMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.6 + NAME 'eduPersonPrincipalName' + DESC 'eduPerson per Internet2 and EDUCAUSE' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.12 + NAME 'eduPersonPrincipalNamePrior' + DESC 'eduPersonPrincipalNamePrior per Internet2' + EQUALITY caseIgnoreMatch + SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.9 + NAME 'eduPersonScopedAffiliation' + DESC 'eduPerson per Internet2 and EDUCAUSE' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.10 + NAME 'eduPersonTargetedID' + DESC 'eduPerson per Internet2 and EDUCAUSE' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.11 + NAME 'eduPersonAssurance' + DESC 'eduPerson per Internet2 and EDUCAUSE' + EQUALITY caseExactMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.13 + NAME 'eduPersonUniqueId' + DESC 'eduPersonUniqueId per Internet2' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.4.1.5923.1.1.1.16 + NAME 'eduPersonOrcid' + DESC 'ORCID researcher identifiers belonging to the principal' + EQUALITY caseIgnoreMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcObjectClasses: ( 1.3.6.1.4.1.5923.1.1.2 + NAME 'eduPerson' + AUXILIARY + MAY ( + eduPersonAffiliation $ eduPersonNickname $ eduPersonOrgDN $ + eduPersonOrgUnitDN $ eduPersonPrimaryAffiliation $ + eduPersonPrincipalName $ eduPersonEntitlement $ eduPersonPrimaryOrgUnitDN $ + eduPersonScopedAffiliation $ eduPersonTargetedID $ eduPersonAssurance $ + eduPersonPrincipalNamePrior $ eduPersonUniqueId $ eduPersonOrcid ) + ) diff --git a/roles/ldap/files/groupOfMembers.ldif b/roles/ldap/files/groupOfMembers.ldif new file mode 100644 index 000000000..aa10094d3 --- /dev/null +++ b/roles/ldap/files/groupOfMembers.ldif @@ -0,0 +1,19 @@ +# Internet X.500 Schema for Ldappc +# Includes the groupOfMembers ObjectClass schema +# +# Taken from RFC2307bis draft 2 +# https://tools.ietf.org/html/draft-howard-rfc2307bis-02 +# +# An structural object class, "groupOfMembers" is a convenient container +# for an extensible set of attributes concerning group memberships. +# +dn: cn=groupOfMembers,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: groupOfMembers +olcObjectClasses: ( 1.3.6.1.1.1.2.18 SUP top STRUCTURAL + NAME 'groupOfMembers' + DESC 'A group with members (DNs)' + MUST cn + MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ + description $ member ) + ) diff --git a/roles/ldap/files/ldap-add b/roles/ldap/files/ldap-add new file mode 100644 index 000000000..3d0c5e487 --- /dev/null +++ b/roles/ldap/files/ldap-add @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +# Copyright (C) 2015-2019 Maciej Delmanowski +# Copyright (C) 2015-2019 DebOps +# SPDX-License-Identifier: GPL-3.0-only + +# Check if specified LDAP schema file is loaded in the local slapd cn=config +# database. If not, try loading it in the server. + + +set -o nounset -o pipefail -o errexit + +schema_file="${1}" + +if [ -z "${schema_file}" ] ; then + printf "Error: You need to specify schema file to load\\n" && exit 1 +fi + +if [ ! -e "${schema_file}" ] ; then + printf "Error: %s does not exist\\n" "${schema_file}" && exit 1 +fi + +if [ ! -r "${schema_file}" ] ; then + printf "Error: %s is unreadable\\n" "${schema_file}" && exit 1 +fi + +# The schema file is already converted, we can deal with them directly +if [[ "${schema_file}" == *.ldif ]] ; then + + # Get the DN of the schema + schema_dn="$(grep -E '^^dn:\s' "${schema_file}")" + + # Get list of already installed schemas from local LDAP server + schema_list() { + ldapsearch -Y EXTERNAL -H ldapi:/// -LLLQ -b 'cn=schema,cn=config' dn \ + | sed -e '/^$/d' -e 's/{[0-9]\+}//' + } + + if schema_list | grep -q "${schema_dn}" ; then + + # Schema is already installed, do nothing + exit 80 + + else + + # Try installing the schema in the database + ldapadd -Y EXTERNAL -H ldapi:/// -f "${schema_file}" + + fi + +fi diff --git a/roles/ldap/files/ldapPublicKey.ldif b/roles/ldap/files/ldapPublicKey.ldif new file mode 100644 index 000000000..8968b6e96 --- /dev/null +++ b/roles/ldap/files/ldapPublicKey.ldif @@ -0,0 +1,21 @@ +dn: cn=openssh-lpk-openldap,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: openssh-lpk-openldap +# +# LDAP Public Key Patch schema for use with openssh-ldappubkey +# useful with PKA-LDAP also +# +# Author: Eric AUGE +# +# Based on the proposal of : Mark Ruijter +# +# octetString SYNTAX +olcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' + DESC 'MANDATORY: OpenSSH Public key' + EQUALITY octetStringMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) +# printableString SYNTAX yes|no +olcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY + DESC 'MANDATORY: OpenSSH LPK olcObjectClasses:' + MUST ( sshPublicKey $ uid ) + ) diff --git a/roles/ldap/files/logrotate_slapd b/roles/ldap/files/logrotate_slapd new file mode 100644 index 000000000..f225a935f --- /dev/null +++ b/roles/ldap/files/logrotate_slapd @@ -0,0 +1,13 @@ +/var/log/slapd.log +{ + rotate 7 + daily + missingok + notifempty + delaycompress + compress + postrotate + invoke-rc.d rsyslog rotate > /dev/null + endscript +} + diff --git a/roles/ldap/files/rsyslog_slapd.conf b/roles/ldap/files/rsyslog_slapd.conf new file mode 100644 index 000000000..a3435617f --- /dev/null +++ b/roles/ldap/files/rsyslog_slapd.conf @@ -0,0 +1,2 @@ +if $programname == 'slapd' then /var/log/slapd.log +if $programname == 'slapd' then ~ diff --git a/roles/ldap/files/sczGroup.ldif b/roles/ldap/files/sczGroup.ldif new file mode 100644 index 000000000..d1b5cb332 --- /dev/null +++ b/roles/ldap/files/sczGroup.ldif @@ -0,0 +1,23 @@ +# Internet X.500 Schema for Ldappc +# Includes the sczGroup ObjectClass schema +# +# An auxiliary object class, "sczGroup," is a convenient container +# for an extensible set of attributes concerning group memberships. +# At this time, the only attribute specified as belonging to the +# object class is "sczMember." +# +# It is specifically configured to support the memberOf overlay. +# +dn: cn=sczGroup,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: sczGroup +olcAttributeTypes: ( 1.3.6.1.4.1.1076.20.40.50.1.1 + NAME 'sczMember' + DESC 'DN identifiers for entities that are members of the group' + EQUALITY distinguishedNameMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) +olcObjectClasses: ( 1.3.6.1.4.1.1076.20.40.50.1 + NAME 'sczGroup' + AUXILIARY + MAY ( sczMember ) + ) diff --git a/roles/ldap/files/sramPerson.ldif b/roles/ldap/files/sramPerson.ldif new file mode 100644 index 000000000..e194381d1 --- /dev/null +++ b/roles/ldap/files/sramPerson.ldif @@ -0,0 +1,23 @@ +# Internet X.500 Schema for Ldappc +# Includes the sramPerson ObjectClass schema +# +# An auxiliary object class, "sramPerson," is a convenient container +# for an extensible set of attributes concerning sram persons. +# At this time, the only attribute specified as belonging to the +# object class is "sramInactiveDays". +# +dn: cn=sramPerson,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: sramPerson +olcAttributeTypes: ( 1.3.6.1.4.1.1076.20.100.20.2.1 NAME 'sramInactiveDays' + DESC 'Number of days this entity was inactive' + EQUALITY IntegerMatch + ORDERING IntegerOrderingMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + ) +olcObjectClasses: ( 1.3.6.1.4.1.1076.20.100.20.1.1 NAME 'sramPerson' + AUXILIARY + MAY ( + sramInactiveDays + ) + ) diff --git a/roles/ldap/files/voPerson.ldif b/roles/ldap/files/voPerson.ldif new file mode 100644 index 000000000..bdce11ed8 --- /dev/null +++ b/roles/ldap/files/voPerson.ldif @@ -0,0 +1,44 @@ +dn: cn=voperson,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: voperson +olcAttributeTypes: {0}( 1.3.6.1.4.1.34998.3.3.1.1 NAME 'voPersonApplicationUID + ' DESC 'voPerson Application-Specific User Identifier' EQUALITY caseIgnoreMat + ch SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' ) +olcAttributeTypes: {1}( 1.3.6.1.4.1.34998.3.3.1.2 NAME 'voPersonAuthorName' DE + SC 'voPerson Author Name' EQUALITY caseIgnoreMatch SYNTAX '1.3.6.1.4.1.1466.1 + 15.121.1.15' ) +olcAttributeTypes: {2}( 1.3.6.1.4.1.34998.3.3.1.3 NAME 'voPersonCertificateDN' + DESC 'voPerson Certificate Distinguished Name' EQUALITY distinguishedNameMat + ch SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' ) +olcAttributeTypes: {3}( 1.3.6.1.4.1.34998.3.3.1.4 NAME 'voPersonCertificateIss + uerDN' DESC 'voPerson Certificate Issuer DN' EQUALITY distinguishedNameMatch + SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' ) +olcAttributeTypes: {4}( 1.3.6.1.4.1.34998.3.3.1.5 NAME 'voPersonExternalID' DE + SC 'voPerson Scoped External Identifier' EQUALITY caseIgnoreMatch SYNTAX '1.3 + .6.1.4.1.1466.115.121.1.15' ) +olcAttributeTypes: {5}( 1.3.6.1.4.1.34998.3.3.1.6 NAME 'voPersonID' DESC 'voPe + rson Unique Identifier' EQUALITY caseIgnoreMatch SYNTAX '1.3.6.1.4.1.1466.115 + .121.1.15' ) +olcAttributeTypes: {6}( 1.3.6.1.4.1.34998.3.3.1.7 NAME 'voPersonPolicyAgreemen + t' DESC 'voPerson Policy Agreement Indicator' EQUALITY caseIgnoreMatch SYNTAX + '1.3.6.1.4.1.1466.115.121.1.15' ) +olcAttributeTypes: {7}( 1.3.6.1.4.1.34998.3.3.1.8 NAME 'voPersonSoRID' DESC 'v + oPerson External Identifier' EQUALITY caseIgnoreMatch SYNTAX '1.3.6.1.4.1.146 + 6.115.121.1.15' ) +olcAttributeTypes: {8}( 1.3.6.1.4.1.34998.3.3.1.9 NAME 'voPersonStatus' DESC ' + voPerson Status' EQUALITY caseIgnoreMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1. + 15' ) +olcAttributeTypes: {9}( 1.3.6.1.4.1.34998.3.3.1.10 NAME 'voPersonAffiliation' + DESC 'voPerson Affiliation Within Local Scope' EQUALITY caseIgnoreMatch SYNTA + X '1.3.6.1.4.1.1466.115.121.1.15' ) +olcAttributeTypes: {10}( 1.3.6.1.4.1.34998.3.3.1.11 NAME 'voPersonExternalAffi + liation' DESC 'voPerson Scoped External Affiliation' EQUALITY caseIgnoreMatch + SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' ) +olcAttributeTypes: {11}( 1.3.6.1.4.1.34998.3.3.1.12 NAME 'voPersonScopedAffili + ation' DESC 'voPerson Affiliation With Explicit Local Scope' EQUALITY caseIgn + oreMatch SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' ) +olcObjectClasses: {0}( 1.3.6.1.4.1.34998.3.3.1 NAME 'voPerson' AUXILIARY MAY ( + voPersonAffiliation $ voPersonApplicationUID $ voPersonAuthorName $ voPerson + CertificateDN $ voPersonCertificateIssuerDN $ voPersonExternalAffiliation $ v + oPersonExternalID $ voPersonID $ voPersonPolicyAgreement $ voPersonScopedAffi + liation $ voPersonSoRID $ voPersonStatus ) ) diff --git a/roles/ldap/handlers/main.yml b/roles/ldap/handlers/main.yml new file mode 100644 index 000000000..0510176a6 --- /dev/null +++ b/roles/ldap/handlers/main.yml @@ -0,0 +1,24 @@ +--- +- name: restart rsyslog + service: + name: rsyslog + state: restarted + listen: "restart rsyslog" + +- name: systemd daemon-reload + systemd: + name: slapd + daemon_reload: yes + +- name: restart LDAP + systemd: + name: slapd + state: restarted + enabled: true + daemon-reload: true + +- name: Restart the ldap container + community.docker.docker_container: + name: "{{ containers.ldap }}" + restart: true + state: started diff --git a/roles/ldap/tasks/admins.yml b/roles/ldap/tasks/admins.yml new file mode 100644 index 000000000..e00115c04 --- /dev/null +++ b/roles/ldap/tasks/admins.yml @@ -0,0 +1,82 @@ +--- +# - name: Initialize DIT admin +# community.general.ldap_entry: +# dn: "{{ services_ldap.binddn }}" +# objectClass: organizationalRole +# attributes: +# cn: "{{ services_ldap.binddn | regex_replace('^cn=([^,]+).*', '\\1') }}" + +# determine which users need to be admin +# check for each role of each user if it leads to membership of group {{ldap_admin_group}} +# - name: determine ldap admins +# set_fact: +# ldap_admins: "{{ ldap_admins | default([]) + [item.0] }}" +# when: ldap_admin_group in role_to_groups[item.1] or ldap_admin_group in item.0.groups +# loop: "{{ users | subelements('roles') }}" + +- name: determine ldap admins + set_fact: + ldap_admins: "{{ ldap.admins }}" + +# Find existing ldap admins +- name: Initialize admins (I) + community.general.ldap_search: + dn: "{{ ldap.services_dn.basedn }}" + scope: "onelevel" + filter: "(objectClass=organizationalRole)" + attrs: + - "cn" + bind_dn: "{{ ldap.services_dn.binddn }}" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ldap.uri }}" + register: "existing_ldap_admins_result" + +# ansible sucks like this: we need to extract the results from the result +- name: Initialize admins (Ia) + set_fact: + existing_ldap_admins: "{{ existing_ldap_admins_result.results }}" + +- debug: + var: "existing_ldap_admins" +- debug: + var: "ldap_admins" + +# Remove LDAP non-admins +- name: Initialize admins (II) + community.general.ldap_entry: + dn: "cn={{ item.cn }},{{ services_ldap.basedn }}" + state: absent + bind_dn: "{{ ldap.services_dn.binddn }}" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ldap.uri }}" + when: > + item.cn not in ldap_admins | map(attribute='uid') + and item.cn != 'admin' + loop: "{{existing_ldap_admins}}" + +# Insert LDAP admins +- name: Initialize admins (III) + community.general.ldap_entry: + dn: "cn={{ item.uid }},{{ ldap.services_dn.basedn }}" + objectClass: + - simpleSecurityObject + - organizationalRole + attributes: + description: An LDAP administrator + userPassword: "{{ item.pw_hash }}" + bind_dn: "{{ ldap.services_dn.binddn }}" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ldap.uri }}" + loop: "{{ ldap_admins }}" + +# Make sure passwords are updated for existing admins +- name: Initialize admins (IV) + community.general.ldap_attrs: + dn: "cn={{ item.uid }},{{ ldap.services_dn.basedn }}" + attributes: + userPassword: "{{ item.pw_hash }}" + bind_dn: "{{ ldap.services_dn.binddn }}" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ldap.uri }}" + loop: "{{ ldap_admins }}" + diff --git a/roles/ldap/tasks/main.yml b/roles/ldap/tasks/main.yml new file mode 100644 index 000000000..f58c21ee7 --- /dev/null +++ b/roles/ldap/tasks/main.yml @@ -0,0 +1,369 @@ +--- +# playbook to install and configure all components of the LDAP +- name: Install LDAP utils + apt: + state: "present" + name: + - "python3-ldap" # for ansible ldap modules + install_recommends: false + +- name: Ensure that a number of directories exist + file: + path: "{{ item.path }}" + state: "directory" + # owner: "{{ ldap.user }}" + # group: "{{ ldap.group }}" + mode: "{{ item.mode }}" + with_items: + - { path: "{{ldap.ldif_dir}}", mode: "0755" } + - { path: "{{ldap.certs_dir}}", mode: "0755" } + - { path: "{{ldap.data_dir}}", mode: "0777" } + notify: Restart the ldap container + +- name: Copy schemas + copy: + src: "{{ item }}" + dest: "{{ ldap.ldif_dir }}/{{ item }}" + mode: "0644" + with_items: + - sczGroup.ldif + - groupOfMembers.ldif + - eduPerson.ldif + - ldapPublicKey.ldif + - eduMember.ldif + - voPerson.ldif + - sramPerson.ldif + notify: Restart the ldap container + +- name: Copying ldap-add script + copy: + src: "{{ item }}" + dest: "{{ ldap.conf_dir }}/{{ item }}" + mode: "0755" + with_items: + - ldap-add + +# # cert is used for communication between ldap for sync +# # is generated in roles/certificates/tasks/main.yml +# - name: Copy wildcard frontend cert +# copy: +# src: "/etc/ssl/certs/sram-https.pem" # was installed here by update-ca-certificates +# remote_src: true +# dest: "{{ldap.certs_dir}}/frontend.crt" +# mode: "0644" +# when: "is_dev" +# notify: Restart the ldap container + +- name: Setup ldap hosts + vars: + host: + key: "%s.{{ ldap.base_domain }}" + value: "%s" + etc_hosts: {} + set_fact: + etc_hosts: >- + {{ etc_hosts | + combine({ host.key | format(item.key): host.value | format(item.value) }) }} + with_dict: "{{ ldap.hosts }}" + +- name: Create the ldap container + community.docker.docker_container: + name: "ldap" + image: "{{ ldap.image }}" + restart_policy: "always" + state: started + pull: true + ports: + - 0.0.0.0:389:389 + env: + LDAP_ORGANISATION: "{{ env }}" + LDAP_DOMAIN: "{{ ldap.base_domain }}" + LDAP_ROOTPASS: "{{ ldap.services_password }}" + etc_hosts: "{{ etc_hosts }}" + volumes: + # For now the target side /opt/ldap is hard-coded + - "{{ ldap.conf_dir }}:/opt/ldap" + networks: + - name: "loadbalancer" + labels: + traefik.enable: "true" + traefik.tcp.routers.ldap.entrypoints: "ldaps" + traefik.tcp.routers.ldap.rule: "HostSNI(`*`)" + traefik.tcp.routers.ldap.tls: "true" + traefik.tcp.services.ldap.loadbalancer.server.port: "389" + healthcheck: + test: + - "CMD" + - "bash" + - "-c" + - "[[ -S /var/run/slapd/ldapi ]]" + register: "ldap_container" + +- name: Wait for LDAP initialization + ansible.builtin.wait_for: + port: 389 + delay: 5 + +- name: Wait for 5 seconds + ansible.builtin.wait_for: + timeout: 5 + when: "ldap_container is changed" + +- name: Ensure the schemas are added to LDAP + ansible.builtin.shell: + # For now the target side /opt/ldap is hard-coded + cmd: "docker exec ldap /opt/ldap/ldap-add /opt/ldap/schema/{{ item }}" + register: "result" + failed_when: "result.rc not in [0,80]" + changed_when: "result.rc != 80" + become: true + loop: + - "sczGroup.ldif" + - "groupOfMembers.ldif" + - "eduPerson.ldif" + - "ldapPublicKey.ldif" + - "eduMember.ldif" + - "voPerson.ldif" + - "sramPerson.ldif" + +- name: Set indices + community.general.ldap_attrs: + dn: "olcDatabase={1}mdb,cn=config" + attributes: + olcDbIndex: "{{item}}" + state: "present" + bind_dn: "cn=admin,cn=config" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ ldap.uri }}" + with_items: + - "entryUUID eq" + - "o eq" + - "dc eq" + - "entryCSN eq" + +- name: Set olcDatabase={-1}frontend olcSizeLimit + community.general.ldap_attrs: + dn: "olcDatabase={-1}frontend,cn=config" + state: "exact" + attributes: + olcSizeLimit: "unlimited" + bind_dn: "cn=admin,cn=config" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ ldap.uri }}" + +- name: Set config + community.general.ldap_attrs: + dn: "cn=config" + state: "present" + attributes: + olcServerID: "{{ ldap.server_id }}" + olcSizeLimit: "unlimited" + olcLogLevel: "{{ ldap.loglevel }}" + olcAttributeOptions: "time-" + bind_dn: "cn=admin,cn=config" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ ldap.uri }}" + +# # cert is used for communication between ldap for sync +# # is generated in roles/certificates/tasks/main.yml +# - name: Set TLS config +# community.general.ldap_attrs: +# dn: "cn=config" +# state: "exact" +# attributes: +# olcTLSCACertificateFile: "/opt/ldap/certs/frontend.crt" +# bind_dn: "cn=admin,cn=config" +# bind_pw: "{{ ldap.services_password }}" +# server_uri: "{{ ldap.uri }}" + +- name: Setup Modules + community.general.ldap_attrs: + dn: cn=module{0},cn=config + attributes: + olcModuleLoad: + - syncprov + - dynlist.so + bind_dn: "cn=admin,cn=config" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ ldap.uri }}" + +- name: Setup Dynlist + community.general.ldap_entry: + dn: olcOverlay=dynlist,olcDatabase={1}mdb,cn=config + objectClass: + - olcOverlayConfig + - olcDynamicList + attributes: + olcDlAttrSet: "voPerson labeledURI member+memberOf@groupOfMembers" + bind_dn: "cn=admin,cn=config" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ ldap.uri }}" + +- name: Setup Syncprov + community.general.ldap_entry: + dn: olcOverlay=syncprov,olcDatabase={1}mdb,cn=config + objectClass: + - olcOverlayConfig + - olcSyncProvConfig + attributes: + olcSpCheckpoint: 100 10 + olcSpSessionLog: 100 + bind_dn: "cn=admin,cn=config" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ ldap.uri }}" + +# Leave this here in case we do want to build our own +# root database from scratch instead of relying on the +# domain based Debian slapd package setup +# +# - name: Setup main database +# community.general.ldap_attrs: +# dn: olcDatabase={1}mdb,cn=config +# attributes: +# olcSuffix: "{{ services_ldap.basedn }}" +# olcRootDN: "{{ services_ldap.binddn }}" +# olcRootPW: "{{ '%s' | format(services_ldap_password) | slapd_hash }}" +# state: exact +# +# - name: Set root credentials +# community.general.ldap_attrs: +# dn: olcDatabase={0}config,cn=config +# attributes: +# olcAccess: >- +# {0}to * +# by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage +# {% if environment_name=="vm" %} +# by dn.exact=gidNumber=1000+uidNumber=1000,cn=peercred,cn=external,cn=auth manage +# {% endif %} +# by dn.exact="{{ services_ldap.binddn }}" manage +# by * break +# state: exact +# bind_dn: "cn=admin,cn=config" +# bind_pw: "{{ services_ldap_password }}" +# server_uri: "{{ ldap_uri }}" +# +# +# - name: Get uid of openldap user +# ansible.builtin.getent: +# database: "passwd" +# key: "openldap" +# +# # store it in a nice var (so line below doesn't get too long) +# - name: Save gid of openldap user +# ansible.builtin.set_fact: +# openldap_auth: "gidNumber={{ansible_facts.getent_passwd['openldap'][2]}}+\ +# uidNumber={{ansible_facts.getent_passwd['openldap'][1]}}" +# + +- name: Set ACLs + community.general.ldap_attrs: + dn: "olcDatabase={1}mdb,cn=config" + attributes: + olcAccess: + - >- + to dn.regex="(([^,]+),{{ ldap.services_dn.basedn }})$" + by dn.exact="{{ ldap.services_dn.binddn }}" write + by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth write + by dn.exact,expand="cn=admin,$1" read + by * break + - >- + to * + by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage + by dn.regex="cn=[^,]+,{{ ldap.services_dn.basedn }}" read + {% if env=="vm" %} + by dn.exact=gidNumber=1000+uidNumber=1000,cn=peercred,cn=external,cn=auth manage + {% endif %} + by * break + - >- + to attrs=userPassword + by self write + by anonymous auth + by * break + state: exact + ordered: true + bind_dn: "cn=admin,cn=config" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ ldap.uri }}" + +# ldap_rids: +# 101: ldaps://ldap1.scz-vm.net/ +# 102: ldaps://ldap2.scz-vm.net/ + +- name: Set rids + vars: + start: 101 + rid: + key: "%d" + value: "{{ ldap.rid_prefix }}%s.{{ ldap.base_domain }}/" + ldap_rids: {} + set_fact: + ldap_rids: >- + {{ ldap_rids | combine({ rid.key | format(start|int): + rid.value | format(item.key) }) }} + start: "{{ start|int + 1 }}" + with_dict: "{{ ldap.hosts | dict2items | sort(attribute='key') }}" + +# Voor toekomstige Claude gebruikers: onderstaande construct levert aan het eind +# een string representatie van de dict op, die niet meer gebruikt kan worden +# in de hieropvolgende 'Setup rids' task... +# - name: Set rids +# set_fact: +# ldap_rids: >- +# {%- set result = {} %} +# {%- for host in (ldap_hosts | dict2items | sort(attribute='key')) %} +# {%- set _ = result.update({(101 + loop.index0)|string: \ +# ldap_rid_prefix ~ host.key ~ '.' ~ base_domain ~ '/'}) %} +# {%- endfor %} +# {{ result }} + +- name: Setup rids + vars: + rid: >- + rid={} + provider="{}" + searchbase="{{ ldap.services_dn.basedn }}" + type=refreshAndPersist + bindmethod=simple + binddn="{{ ldap.services_dn.binddn }}" + credentials={{ ldap.services_password }} + retry="30 +" + timeout=30 + network-timeout=5 + rids: [] + set_fact: + rids: "{{ rids + [ rid.format(item.key, item.value) ] }}" + with_dict: "{{ dict(ldap_rids) }}" + +- name: Setup Syncrepl + community.general.ldap_attrs: + dn: olcDatabase={1}mdb,cn=config + attributes: + olcSyncrepl: "{{ rids }}" + olcMultiProvider: "TRUE" + bind_dn: "cn=admin,cn=config" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ ldap.uri }}" + +# We now have Syncrepl in place, so only write to primary +- name: Initialize DIT + community.general.ldap_entry: + dn: "{{ ldap.services_dn.basedn }}" + state: "present" + objectClass: + - "top" + - "dcObject" + - "organization" + attributes: + dc: "{{ ldap.services_dn.basedn | regex_replace('^dc=([^,]+).*', '\\1') }}" + o: "{{ ldap.services_dn.o }}" + bind_dn: "{{ ldap.services_dn.binddn }}" + bind_pw: "{{ ldap.services_password }}" + server_uri: "{{ ldap.uri }}" + when: > + inventory_hostname in groups['ldap_primary'] + +# We now have Syncrepl in place, so only write to primary +- name: Add ldap admins + include_tasks: "admins.yml" + when: > + inventory_hostname in groups['ldap_primary'] diff --git a/roles/ldap/templates/ldap-backup.sh.j2 b/roles/ldap/templates/ldap-backup.sh.j2 new file mode 100644 index 000000000..2c6aa9201 --- /dev/null +++ b/roles/ldap/templates/ldap-backup.sh.j2 @@ -0,0 +1,19 @@ +#!/bin/bash +# vim:ft=sh +set -e + +if [ $UID -ne 0 ] +then + echo "Sorry, this script must run as root" + exit 1 +fi + +BACKUP_DIR="{{ldap_backup_dir}}" +BACKUP_FILE="$BACKUP_DIR/ldap_$(/bin/date +%Y-%m-%d_%H:%M)" + +mkdir -p -m 0755 "$BACKUP_DIR" + +/usr/sbin/slapcat -o ldif-wrap=no -n0 | /bin/bzip2 -c6 > "${BACKUP_FILE}.db0.ldif.bz2" +/usr/sbin/slapcat -o ldif-wrap=no -n1 | /bin/bzip2 -c6 > "${BACKUP_FILE}.db1.ldif.bz2" + +exit 0 diff --git a/roles/ldap/templates/ldap.conf.j2 b/roles/ldap/templates/ldap.conf.j2 new file mode 100644 index 000000000..d7fa7c227 --- /dev/null +++ b/roles/ldap/templates/ldap.conf.j2 @@ -0,0 +1,16 @@ +# +# LDAP Defaults +# + +# See ldap.conf(5) for details +# This file should be world readable but not world writable. + +#BASE dc=example,dc=com +#URI ldap://ldap.example.com ldap://ldap-master.example.com:666 + +#SIZELIMIT 12 +#TIMELIMIT 15 +#DEREF never + +# TLS certificates (needed for GnuTLS) +TLS_CACERT {{ ssl_certs_dir }}/{{ internal_base_domain }}.crt diff --git a/roles/ldap/templates/slapd.service.j2 b/roles/ldap/templates/slapd.service.j2 new file mode 100644 index 000000000..7e0f79397 --- /dev/null +++ b/roles/ldap/templates/slapd.service.j2 @@ -0,0 +1,20 @@ +[Unit] +Description = LDAP server + +[Service] +Type = forking +User = root +SupplementaryGroups = ssl-cert +ExecStartPre=-/bin/mkdir -p /var/run/slapd +ExecStartPre=-/bin/chown openldap. /var/run/slapd +ExecStart = /usr/sbin/slapd -F /etc/ldap/slapd.d -u openldap -g openldap -h 'ldapi:/// ldap://localhost/ ldaps://{{inventory_hostname}}/' +Restart = always +RestartSec = 30 +PIDFile = /run/slapd/slapd.pid +# defaults are 1024:524288 which is too small for slapd +# see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=378261 and +# https://www.openldap.org/lists/openldap-software/200802/msg00186.html +LimitNOFILE=4096:524288 + +[Install] +WantedBy = multi-user.target diff --git a/roles/ldap/vars/main.yml b/roles/ldap/vars/main.yml new file mode 100644 index 000000000..761942f7b --- /dev/null +++ b/roles/ldap/vars/main.yml @@ -0,0 +1 @@ +current_release_appdir: /opt/openconext diff --git a/roles/redis/defaults/main.yml b/roles/redis/defaults/main.yml new file mode 100644 index 000000000..d4eb4b182 --- /dev/null +++ b/roles/redis/defaults/main.yml @@ -0,0 +1,11 @@ +--- +redis: "{{ redis_defaults | combine(redis_overrides, recursive=true) }}" +redis_defaults: + image: "docker.io/library/redis:7" + conf_dir: "{{ current_release_appdir }}/redis" + data_dir: "{{ current_release_appdir }}/redis/data" + user: redis + group: redis + redis_user: default + redis_password: changethispassword + max_memory: 100mb diff --git a/roles/redis/handlers/main.yml b/roles/redis/handlers/main.yml new file mode 100644 index 000000000..5ed78e133 --- /dev/null +++ b/roles/redis/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: Restart redis container + community.docker.docker_container: + name: redis + state: started + restart: true diff --git a/roles/redis/tasks/main.yml b/roles/redis/tasks/main.yml new file mode 100644 index 000000000..65e7392ef --- /dev/null +++ b/roles/redis/tasks/main.yml @@ -0,0 +1,61 @@ +--- +- name: "Create redis group" + group: + name: "{{ redis.group }}" + state: "present" + register: "result" + +- name: "Save redis group gid" + set_fact: + redis_group_gid: "{{ result.gid }}" + +- name: "Create redis user" + user: + name: "{{ redis.user }}" + group: "{{ redis.group }}" + comment: "User to run SRAM Redis service" + shell: "/bin/false" + password: "!" + home: "{{ redis.conf_dir }}" + create_home: false + state: "present" + register: "result" + +- name: "Save redis user uid" + set_fact: + redis_user_uid: "{{ result.uid }}" + +- name: "Create directories" + file: + path: "{{item.path}}" + state: "directory" + owner: "{{ redis.user }}" + group: "{{ redis.group }}" + mode: "{{item.mode}}" + with_items: + - { path: "{{redis.conf_dir}}", mode: "0755" } + - { path: "{{redis.data_dir}}", mode: "0755" } + +- name: "Create redis config" + template: + src: "redis.conf.j2" + dest: "{{ redis.conf_dir }}/redis.conf" + owner: "{{ redis.user }}" + group: "{{ redis.group }}" + mode: "0644" + notify: "Restart redis container" + +- name: "Create redis container" + community.docker.docker_container: + name: "redis" + image: "{{ redis.image }}" + restart_policy: "always" + state: "started" + user: "{{ redis_user_uid }}:{{ redis_group_gid }}" + command: | + redis-server /usr/local/etc/redis/redis.conf + volumes: + - "{{ redis.conf_dir }}:/usr/local/etc/redis" + - "{{ redis.data_dir }}:/data" + networks: + - name: loadbalancer diff --git a/roles/redis/templates/redis.conf.j2 b/roles/redis/templates/redis.conf.j2 new file mode 100644 index 000000000..ba231dc58 --- /dev/null +++ b/roles/redis/templates/redis.conf.j2 @@ -0,0 +1,3 @@ +user {{redis.redis_user}} on +@all ~* &* >{{redis.redis_password}} +maxmemory {{ redis.max_memory }} +maxmemory-policy allkeys-lru diff --git a/roles/redis/vars/main.yml b/roles/redis/vars/main.yml new file mode 100644 index 000000000..761942f7b --- /dev/null +++ b/roles/redis/vars/main.yml @@ -0,0 +1 @@ +current_release_appdir: /opt/openconext diff --git a/roles/sbs/defaults/main.yml b/roles/sbs/defaults/main.yml new file mode 100644 index 000000000..9bcd62c87 --- /dev/null +++ b/roles/sbs/defaults/main.yml @@ -0,0 +1,166 @@ +--- +sbs: "{{ sbs_defaults | combine(sbs_overrides) }}" + +sbs_defaults: + base_domain: "test2.sram.surf.nl" + ansible_nolog: true + base_url: "https://{{ sbs_defaults.base_domain }}" + server_image: "ghcr.io/surfscz/sram-sbs-server:main" + client_image: "ghcr.io/surfscz/sram-sbs-client:main" + + openidc_timeout: 86400 + sram_conf_dir: "{{ current_release_appdir }}/sram" + + work_dir: "{{ sbs_defaults.sram_conf_dir }}/sbs" + git_dir: "{{ sbs_defaults.work_dir }}/sbs" + env_dir: "{{ sbs_defaults.work_dir }}/sbs-env" + conf_dir: "{{ sbs_defaults.work_dir }}/config" + log_dir: "{{ sbs_defaults.work_dir }}/log" + cert_dir: "{{ sbs_defaults.work_dir }}/cert" + apache_conf: "{{ sbs_defaults.work_dir }}/sbs_defaults.conf" + nginx_conf: "{{ sbs_defaults.work_dir }}/nginx.conf" + + db_name: "sbs" + db_user: "sbsrw" + # dbbackup_user: "sbs_backupper" + migration_user: "sbsmigrate" + + db_connection: "\ + mysql+mysqldb://%s:%s@{{ mariadb_host }}/{{ sbs_defaults.db_name }}\ + ?ssl=true&charset=utf8mb4" + db_connection_sbs: "{{ sbs_defaults.db_connection | format(sbs_defaults.db_user, mysql_passwords.sbs) }}" + db_connection_migration: "\ + {{ sbs_defaults.db_connection | format(sbs_defaults.migration_user, mysql_passwords.sbsmigrate) }}" + + db_secret: secret + secret_key_suffix: suffix + encryption_key: encryption_key + + redis_host: redis + redis_port: 6379 + redis_ssl: false + redis_user: default + + mail_host: "host.docker.internal" + mail_port: 25 + + user: "sbs" + group: "sbs" + + session_lifetime: 1440 + secret_key_suffix: "" + + oidc_crypto_password: "CHANGEME" + uid_attribute: "sub" + + disclaimer_color: "#a29c13" + disclaimer_label: wsgi + + urn_namespace: "urn:example:sbs" + eppn_scope: "sbs.example.edu" + restricted_co_default_org: "example.org" + + mail_sender_name: "SURF" + mail_sender_email: "no-reply@localhost" + exceptions_mail: "root@localhost" + + support_email: "sram-support@localhost" + admin_email: "sram-beheer@localhost" + ticket_email: "sram-support@surf.nl" + eduteams_email: "eduteams@localhost" + + wiki_link: "https://www.example.org/wiki" + + cron_hour_of_day: 4 + seed_allowed: True + api_keys_enabled: True + feedback_enabled: True + audit_trail_notifications_enabled: True + send_exceptions: False + send_js_exceptions: False + second_factor_authentication_required: True + totp_token_name: "SRAM-example" + notifications_enabled: True + invitation_reminders_enabled: True + invitation_expirations_enabled: True + open_requests_enabled: True + scim_sweep: False + impersonation_allowed: True + admin_platform_backdoor_totp: True + past_dates_allowed: True + mock_scim_enabled: True + log_to_stdout: True + + delete_orphaned: True + suspension_inactive_days: 365 + suspension_reminder_days: 14 + suspension_notify_admin: False + + oidc_config_url: "http://localhost/.well-known/openid-configuration" + oidc_authz_endpoint: "http://localhost/OIDC/authorization" + oidc_token_endpoint: "http://localhost/OIDC/token" + oidc_userinfo_endpoint: "http://localhost/OIDC/userinfo" + oidc_jwks_endpoint: "http://localhost/OIDC/jwks.json" + oidc_redirect_uri: "https://{{sbs_defaults.base_domain}}/api/users/resume-session" + mfa_idp_allowed: false + eduteams_continue_endpoint: "https://localhost/continue" + eb_continue_endpoint: "https://engine.(.*)surfconext.nl(.*)" + oidc_jwt_audience: "https://localhost" + continue_eduteams_redirect_uri: "https://localhost/continue" + oidc_verify_peer: False + oidc_scopes: + - openid + + manage_base_enabled: False + manage_base_url: "https://manage.{{base_domain}}" + manage_sram_rp_entity_id: "sbs.{{sbs_defaults.base_domain}}" + manage_verify_peer: False + + idp_metadata_url: "https://metadata.surfconext.nl/signed/2023/edugain-downstream-idp.xml " + # backup_dir: "{{backup_base}}/sbs" + + swagger_enabled: true + + ssid_identity_providers: [] + surf_secure_id: + environment: "unknown.example.org" + sp_entity_id: "https://sbs.{{sbs_defaults.base_domain}}" + acs_url: "https://{{sbs_defaults.base_domain}}/api/users/acs" + sa_gw_environment: "sa-gw.unknown.example.org" + sa_idp_certificate: | + -----BEGIN CERTIFICATE----- + 12345 + -----END CERTIFICATE----- + priv: | + -----BEGIN RSA PRIVATE KEY----- + abcde + -----END RSA PRIVATE KEY----- + pub: | + -----BEGIN CERTIFICATE----- + 12345 + -----END CERTIFICATE----- + + ssid_authncontext: "\ + http://{{ sbs_defaults.surf_secure_id.environment }}/assurance/sfo-level2" + ssid_entityid: "\ + https://{{ sbs_defaults.surf_secure_id.sa_gw_environment }}/second-factor-only/metadata" + ssid_sso_endpoint: "\ + https://{{ sbs_defaults.surf_secure_id.sa_gw_environment }}/second-factor-only/single-sign-on" + + mfa_sso_minutes: 10 + mfa_fallback_enabled: true + + ldap_url: "ldap://ldap.example.com/dc=example,dc=com" + ldap_bind_account: "cn=admin,dc=entity_id,dc=services,dc=sram-tst,dc=surf,dc=nl" + + csp_style_hashes: + - 'sha256-0+ANsgYUJdh56RK8gGvTF2vnriYqvFHfWqtA8xXa+bA=' + - 'sha256-3SnfHQolDHbZMbDAPmhrZf1keHiXfj/KJyh2phhFAAY=' + - 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' + - 'sha256-Ng6y+QCkPChG4Q49SIfXB5ToIDcDhITtQNFkDBPpCTw=' + - 'sha256-orBPipbqpMvkNi+Z+m6qEn0XS6ymmAQE6+FwCNs1FbQ=' + - 'sha256-vFt3L2qLqpJmRpcXGbYr2UVSmgSp9VCUzz2lnqWIATw=' + - 'sha256-SU3XCwbQ/8qgzoGOWCYdkwIr3xRrl5rsvdFcpw8NSiE=' # on /new-service-request + - 'sha256-WTC9gHKjIpzl5ub1eg/YrRy/k+jlzeyRojah9dxAApc=' # on /new-service-request + + engine_block_api_token: secret diff --git a/roles/sbs/files/yarn.gpg b/roles/sbs/files/yarn.gpg new file mode 100644 index 000000000..3e9e7d155 --- /dev/null +++ b/roles/sbs/files/yarn.gpg @@ -0,0 +1,243 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +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 +=oMDY +-----END PGP PUBLIC KEY BLOCK----- diff --git a/roles/sbs/handlers/main.yml b/roles/sbs/handlers/main.yml new file mode 100644 index 000000000..2d7710d43 --- /dev/null +++ b/roles/sbs/handlers/main.yml @@ -0,0 +1,9 @@ +--- +- name: Restart sbs containers + community.docker.docker_container: + name: "{{ item }}" + state: started + restart: true + loop: + - sbs-client + - sbs-server diff --git a/roles/sbs/tasks/main.yml b/roles/sbs/tasks/main.yml new file mode 100644 index 000000000..560191f8a --- /dev/null +++ b/roles/sbs/tasks/main.yml @@ -0,0 +1,179 @@ +--- +# - name: "Initialize database" +# throttle: 1 +# import_tasks: "database_init.yml" +# when: "is_dev" + +- name: "Create SBS group" + group: + name: "{{ sbs.group }}" + state: "present" + register: "result" + +- name: "Save SBS group gid" + set_fact: + sbs_group_gid: "{{ result.gid }}" + +- name: "Create SBS user" + user: + name: "{{ sbs.user }}" + group: "{{ sbs.group }}" + comment: "User to run SBS service" + shell: "/bin/false" + password: "!" + home: "{{ sbs.conf_dir }}" + create_home: false + state: "present" + register: "result" + +- name: "Save sbs user uid" + set_fact: + sbs_user_uid: "{{ result.uid }}" + +- name: "Create directories" + file: + path: "{{item.path}}" + state: "directory" + owner: "{{sbs_user_uid}}" + group: "{{sbs_group_gid}}" + mode: "{{item.mode}}" + with_items: + - { path: "{{sbs.work_dir}}", mode: "0755" } + - { path: "{{sbs.conf_dir}}", mode: "0755" } + - { path: "{{sbs.conf_dir}}/saml", mode: "0755" } + - { path: "{{sbs.log_dir}}", mode: "0775" } + - { path: "{{sbs.cert_dir}}", mode: "0755" } + +- name: "Fix file permissions" + file: + path: "{{sbs.log_dir}}/{{item}}" + owner: "{{sbs_user_uid}}" + group: "{{sbs_group_gid}}" + mode: "0664" + state: "touch" + modification_time: "preserve" + access_time: "preserve" + with_items: + - "sbs.log" + - "sbs.debug.log" + +# Create dummy file in certs dir to pacify container pre-init script +# https://github.com/SURFscz/SBS/pull/2312 +- name: "Touch file in {{ sbs.cert_dir }}" + ansible.builtin.file: + path: "{{sbs.cert_dir}}/dummy" + state: file + +- name: "Create SBS config files" + template: + src: "{{item.name}}.j2" + dest: "{{ sbs.conf_dir }}/{{item.name}}" + owner: "{{sbs_user_uid}}" + group: "{{sbs_group_gid}}" + mode: "{{item.mode}}" + with_items: + - { name: "config.yml", mode: "0644" } + - { name: "alembic.ini", mode: "0644" } + - { name: "disclaimer.css", mode: "0644" } + - { name: "sbs-apache.conf", mode: "0644" } + no_log: "{{sbs.ansible_nolog}}" + notify: "Restart sbs containers" + +- name: "Pull sbs image" + community.docker.docker_image_pull: + name: "{{ item }}" + with_items: + - "{{ sbs.client_image }}" + - "{{ sbs.server_image }}" + register: "sbs_image" + +# We need to remove sram-static so it gets repopulated +# with new SBS image static content +- name: "Clean up old containers" + block: + - name: "Stop and remove sbs and sbs-server containers" + community.docker.docker_container: + name: "{{ item }}" + state: "absent" + with_items: + - "sbs-client" + - "sbs-server" + + when: "sbs_image is changed" + +- name: "Run SBS migrations" + throttle: 1 + community.docker.docker_container: + name: "sbs-migration" + image: "{{ sbs.server_image }}" + pull: "never" + state: "started" + restart_policy: "no" + detach: false + env: + RUNAS_UID: "{{ sbs_user_uid | string }}" + RUNAS_GID: "{{ sbs_group_gid | string }}" + MIGRATIONS_ONLY: "1" + # don't actually run the server + command: "/bin/true" + volumes: + - "{{ sbs.conf_dir }}:/sbs-config" + - "{{ sbs.cert_dir }}:/sbs-config/cert:ro" + - "{{ sbs.log_dir }}:/opt/sbs/log" + networks: + - name: "loadbalancer" + register: "result" + failed_when: "'container' not in result or result.container.State.ExitCode != 0" + changed_when: "'[alembic.runtime.migration] Running upgrade' in result.container.Output" + notify: "Restart sbs containers" + +# Remove the migration container; we can do that with auto_remove, because if we use that, ansible +# will not save the output in result +- name: "Remove migration container" + community.docker.docker_container: + name: "sbs-migration" + state: "absent" + # TODO: fix this by only running this if "sbs_image is changed" + changed_when: false + +- name: "Start sbs client container" + community.docker.docker_container: + name: "sbs-client" + image: "{{ sbs.client_image }}" + pull: "never" + restart_policy: "always" + state: "started" + volumes: + - "{{ sbs.conf_dir }}/sbs-apache.conf:/etc/apache2/sites-enabled/sbs.conf:ro" + - "{{ sbs.conf_dir }}/disclaimer.css:/opt/sbs/client/dist/disclaimer.css:ro" + networks: + - name: "loadbalancer" + labels: + traefik.http.routers.sbsclient.rule: "Host(`{{ sbs.base_domain }}`)" + traefik.http.routers.sbsclient.tls: "true" + traefik.enable: "true" + +- name: "Start SBS server container" + community.docker.docker_container: + name: "sbs-server" + image: "{{ sbs.server_image }}" + restart_policy: "always" + state: "started" + env: + RUNAS_UID: "{{ sbs_user_uid | string }}" + RUNAS_GID: "{{ sbs_group_gid | string }}" + CONFIG: "/opt/sbs/server/config/config.yml" + REQUESTS_CA_BUNDLE: "/etc/ssl/certs/ca-certificates.crt" + RUN_MIGRATIONS: "0" + pull: "never" + volumes: + - "{{ sbs.conf_dir }}:/sbs-config" + - "{{ sbs.cert_dir }}:/sbs-config/cert:ro" + - "{{ sbs.log_dir }}:/opt/sbs/log" + - "/tmp/ci-runner:/tmp/ci-runner" + networks: + - name: "loadbalancer" + # TODO: fix this: this is only for dev + etc_hosts: + oidc-op.scz-vm.net: "172.20.1.24" + host.docker.internal: host-gateway diff --git a/roles/sbs/templates/alembic.ini.j2 b/roles/sbs/templates/alembic.ini.j2 new file mode 100644 index 000000000..9ccd51979 --- /dev/null +++ b/roles/sbs/templates/alembic.ini.j2 @@ -0,0 +1,72 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = migrations + +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# timezone to use when rendering the date +# within the migration file as well as the filename. +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +#truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; this defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path +# version_locations = %(here)s/bar %(here)s/bat alembic/versions + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = {{ sbs.db_connection_migration }} + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = NOTSET +handlers = console + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = DEBUG +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/roles/sbs/templates/config.yml.j2 b/roles/sbs/templates/config.yml.j2 new file mode 100644 index 000000000..d052565c2 --- /dev/null +++ b/roles/sbs/templates/config.yml.j2 @@ -0,0 +1,264 @@ +--- +database: + uri: {{ sbs.db_connection_sbs }} + +redis: +{% if env == 'test2' %} + uri: "redis://{{ sbs.redis_user }}:{{ sbs.redis_password }}@{{sbs.redis_host}}/" +{% else %} + uri: "redis{% if sbs.redis_ssl %}s{% endif %}://{{ sbs.redis_user }}:{{ sbs.redis_password }}@{{ sbs.redis_host }}:{{ sbs.redis_port }}/" +{% endif %} + +# add a per-release suffix here to invalidate sessions on new releases +secret_key: {{ sbs.db_secret }}{{sbs.secret_key_suffix}} +# Must be a base64 encoded key of 128, 192, or 256 bits. +# Generate: base64.b64encode(os.urandom(256 // 8)).decode() +encryption_key: {{ sbs.encryption_key }} + +# Lifetime of session in minutes (one day is 60 * 24) +permanent_session_lifetime: {{ sbs.session_lifetime }} + +logging: + log_to_stdout: {{ sbs.log_to_stdout }} + +# Valid scopes are "READ" and "WRITE" +api_users: +{% for name, user in sbs.api_users.items() %} + - name: "{{ name }}" + password: "{{ user.password }}" + scopes: "[ {{ user.scopes | join(', ') }} ]" +{% endfor %} + +oidc: + client_id: "{{ sbs.oidc_client_id }}" + client_secret: "{{ sbs.oidc_client_secret }}" + audience: "{{ sbs.oidc_jwt_audience }}" + verify_peer: {{ sbs.oidc_verify_peer }} + authorization_endpoint: "{{ sbs.oidc_authz_endpoint}}" + token_endpoint: "{{ sbs.oidc_token_endpoint }}" + userinfo_endpoint: "{{ sbs.oidc_userinfo_endpoint }}" + jwks_endpoint: "{{ sbs.oidc_jwks_endpoint }}" + #Note that the paths for these uri's is hardcoded and only domain and port differ per environment + redirect_uri: "{{ sbs.oidc_redirect_uri }}" + continue_eduteams_redirect_uri: "{{ sbs.eduteams_continue_endpoint }}" + continue_eb_redirect_uri: "{{ sbs.eb_continue_endpoint }}" + second_factor_authentication_required: {{ sbs.second_factor_authentication_required }} + totp_token_name: "{{ sbs.totp_token_name }}" + # The service_id in the proxy_authz endpoint when logging into SBS. Most likely to equal the oidc.client_id + sram_service_entity_id: "{{ sbs.oidc_client_id }}" + scopes: {{ sbs.oidc_scopes }} + +base_scope: "{{ base_domain }}" +entitlement_group_namespace: "{{ sbs.urn_namespace }}" +eppn_scope: " {{ sbs.eppn_scope }}" +scim_schema_sram: "urn:mace:surf.nl:sram:scim:extension" +collaboration_creation_allowed_entitlement: "urn:mace:surf.nl:sram:allow-create-co" + +{% if env == "prd" %} +environment_disclaimer: "" +{% else %} +environment_disclaimer: "{{ sbs.disclaimer_label }}" +{% endif %} + +# All services in the white list can be requested in the create-restricted-co API +# The default organisation is a fallback for when the administrator has no schac_home_org +restricted_co: + services_white_list: [ "https://cloud" ] + default_organisation: "{{ sbs.restricted_co_default_org }}" + +mail: + host: {{ sbs.mail_host }} + port: {{ sbs.mail_port }} + sender_name: {{ sbs.mail_sender_name }} + sender_email: {{ sbs.mail_sender_email }} + suppress_sending_mails: False + info_email: {{ sbs.support_email }} + beheer_email: {{ sbs.admin_email }} + ticket_email: {{ sbs.ticket_email }} + eduteams_email: {{ sbs.eduteams_email }} + # Do we mail a summary of new Organizations and Services to the beheer_email? + audit_trail_notifications_enabled: {{ sbs.audit_trail_notifications_enabled }} + account_deletion_notifications_enabled: True + send_exceptions: {{ sbs.send_exceptions }} + send_js_exceptions: {{ sbs.send_js_exceptions }} + send_exceptions_recipients: [ "{{ sbs.exceptions_mail }}" ] + environment: "{{ base_domain }}" + +manage: + enabled: {{ sbs.manage_base_enabled }} + # The entity_id of the SRAM RP in Manage for API retrieval, e.g "sbs.test2.sram.surf.nl" + sram_rp_entity_id: "{{ sbs.manage_sram_rp_entity_id }}" + base_url: "{{ sbs.manage_base_url }}" + user: "{{ sbs.manage_user }}" + password: "{{ sbs.manage_password }}" + verify_peer: {{ sbs.manage_verify_peer }} + +aup: + version: 1 + url_aup_en: "https://edu.nl/6wb63" + url_aup_nl: "https://edu.nl/6wb63" + +base_url: {{ sbs.base_url }} +socket_url: {{ sbs.base_url }} +base_server_url: {{ sbs.base_url }} +wiki_link: {{ sbs.wiki_link }} + +admin_users: +{% for admin_user in sbs.admin_users %} + - uid: "{{ admin_user.uid }}" +{% endfor %} + +organisation_categories: + - "HBO" + - "MBO" + - "UMC" + - "University" + - "Research" + - "SURF" + +feature: + seed_allowed: {{ sbs.seed_allowed }} + api_keys_enabled: {{ sbs.api_keys_enabled }} + feedback_enabled: {{ sbs.feedback_enabled }} + impersonation_allowed: {{ sbs.impersonation_allowed }} + sbs_swagger_enabled: {{ sbs.swagger_enabled }} + admin_platform_backdoor_totp: {{ sbs.admin_platform_backdoor_totp }} + past_dates_allowed: {{ sbs.past_dates_allowed }} + mock_scim_enabled: {{ sbs.mock_scim_enabled }} + +metadata: + idp_url: "{{sbs.idp_metadata_url}}" + parse_at_startup: True + # No need for environment specific values + scope_override: + knaw.nl: "Koninklijke Nederlandse Akademie van Wetenschappen (KNAW)" + +platform_admin_notifications: + # Do we daily check for CO join_requests and CO requests and send a summary mail to beheer_email? + enabled: False + cron_hour_of_day: {{ sbs.cron_hour_of_day }} + # How long before we include open join_requests in the summary + outstanding_join_request_days_threshold: 7 + # How long before we include open CO requests in the summary + outstanding_coll_request_days_threshold: 7 + +user_requests_retention: + # Do we daily check for CO join_requests and CO requests and delete approved and denied? + enabled: {{ sbs.notifications_enabled }} + cron_hour_of_day: {{ sbs.cron_hour_of_day }} + # How long before we delete approved / denied join_requests + outstanding_join_request_days_threshold: 90 + # How long before we delete approved / denied CO requests + outstanding_coll_request_days_threshold: 90 + +# The retention config determines how long users may be inactive, how long the reminder email is valid and when do we resent the magic link +retention: + cron_hour_of_day: {{ sbs.cron_hour_of_day }} + # how many days of inactivity before a user is suspended + # 0 allows for any last_login_date in the past to trigger suspension notification + allowed_inactive_period_days: {{ sbs.suspension_inactive_days }} + # how many days before suspension do we send a warning + # -1 will suspend notified users on second suspension cron + reminder_suspend_period_days: {{ sbs.suspension_reminder_days }} + # how many days after suspension do we delete the account + remove_suspended_users_period_days: 90 + # how many days before deletion do we send a reminder + reminder_expiry_period_days: 7 + # whether to send a notification of the result of the retention process to the beheer_email + admin_notification_mail: {{ sbs.suspension_notify_admin }} + +collaboration_expiration: + # Do we daily check for CO's that will be deleted because they have been expired? + enabled: {{ sbs.notifications_enabled }} + cron_hour_of_day: {{ sbs.cron_hour_of_day }} + # How long after expiration do we actually delete expired collaborations + expired_collaborations_days_threshold: 90 + # How many days before actual expiration do we mail the organisation members + expired_warning_mail_days_threshold: 10 + +collaboration_suspension: + # Do we daily check for CO's that will be suspended because of inactivity? + enabled: {{ sbs.notifications_enabled }} + cron_hour_of_day: {{ sbs.cron_hour_of_day }} + # After how many days of inactivity do we suspend collaborations + collaboration_inactivity_days_threshold: 365 + # How many days before actual suspension do we mail the organisation members + inactivity_warning_mail_days_threshold: 10 + # After how many days after suspension do we actually delete the collaboration + collaboration_deletion_days_threshold: 90 + +membership_expiration: + # Do we daily check for memberships that will be deleted because they have been expired? + enabled: {{ sbs.notifications_enabled }} + cron_hour_of_day: {{ sbs.cron_hour_of_day }} + # How long after expiration do we actually delete expired memberships + expired_memberships_days_threshold: 90 + # How many days before actual expiration do we mail the co admin and member + expired_warning_mail_days_threshold: 10 + +invitation_reminders: + # Do we daily check for invitations that need a reminder? + enabled: {{ sbs.invitation_reminders_enabled }} + cron_hour_of_day: {{ sbs.cron_hour_of_day }} + # How many days before expiration of an invitation do we remind the user? + invitation_reminders_threshold: 5 + +invitation_expirations: + # Do we daily check for invitations that are expired / accepted and are eligible for deletion ? + enabled: {{ sbs.invitation_expirations_enabled }} + cron_hour_of_day: {{ sbs.cron_hour_of_day }} + # How long after expiration of an invitation do we delete the invitation? + nbr_days_remove_expired_invitations: 10 + # How long after expiration of an API created invitation do we delete the invitation? + nbr_days_remove_api_expired_invitations: 30 + +orphan_users: + # Do we daily check for users that are orphans soo they can be deleted? + enabled: {{ sbs.delete_orphaned }} + cron_hour_of_day: {{ sbs.cron_hour_of_day }} + # How long after created do we delete orphan users + delete_days_threshold: 14 + +open_requests: + # Do we weekly check for all open requests? + enabled: {{ sbs.open_requests_enabled }} + cron_day_of_week: 1 + +scim_sweep: + # Do we enable scim sweeps? + enabled: {{ sbs.scim_sweep }} + # How often do we check if scim sweeps are needed per service + cron_minutes_expression: "*/15" + +ldap: + url: "{{ sbs.ldap_url }}" + bind_account: "{{ sbs.ldap_bind_account }}" + +# A MFA login in a different flow is valid for X minutes +mfa_sso_time_in_minutes: {{sbs.mfa_sso_minutes}} + +# whether to fall back to TOTP MFA +mfa_fallback_enabled: {{sbs.mfa_fallback_enabled}} + +# Lower case entity ID's and schac_home allowed skipping MFA. +# Note that for a login directly into SRAM only schac_home can be used as the entity_idp of the IdP is unknown +mfa_idp_allowed: {{sbs.mfa_idp_allowed}} + +# Lower case schachome organisations / entity ID's where SURFSecure ID is used for step-up +ssid_identity_providers: {{sbs.ssid_identity_providers}} + +ssid_config_folder: saml + +pam_web_sso: + session_timeout_seconds: 300 + +rate_limit_totp_guesses_per_30_seconds: 10 + +# The uid's of user that will never be suspended or deleted +excluded_user_accounts: +{% for excluded_user in sbs.excluded_users %} + - uid: "{{ excluded_user.uid }}" +{% endfor %} + +engine_block: + api_token: {{ sbs.engine_block_api_token }} diff --git a/roles/sbs/templates/disclaimer.css.j2 b/roles/sbs/templates/disclaimer.css.j2 new file mode 100644 index 000000000..7922f5e5b --- /dev/null +++ b/roles/sbs/templates/disclaimer.css.j2 @@ -0,0 +1,6 @@ +{% if env!="prd" -%} +body::after { + background: {{ sbs.disclaimer_color }}; + content: "{{ sbs.disclaimer_label }}"; +} +{% endif %} diff --git a/roles/sbs/templates/saml_advanced_settings.json.j2 b/roles/sbs/templates/saml_advanced_settings.json.j2 new file mode 100644 index 000000000..bdde32050 --- /dev/null +++ b/roles/sbs/templates/saml_advanced_settings.json.j2 @@ -0,0 +1,35 @@ +{ + "security": { + "nameIdEncrypted": false, + "authnRequestsSigned": true, + "logoutRequestSigned": false, + "logoutResponseSigned": false, + "signMetadata": false, + "wantMessagesSigned": false, + "wantAssertionsSigned": true, + "wantNameId" : true, + "wantNameIdEncrypted": false, + "wantAttributeStatement": false, + "wantAssertionsEncrypted": false, + "requestedAuthnContext": ["{{sbs_ssid_authncontext}}"], + "requestedAuthnContextComparison": "minimum", + "failOnAuthnContextMismatch": false, + "allowSingleLabelDomains": false, + "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", + "digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256", + "rejectDeprecatedAlgorithm": true + }, + "contactPerson": { + "technical": { + "givenName": "{{ mail.admin_name }}", + "emailAddress": "{{ mail.admin_address }}" + } + }, + "organization": { + "en-US": { + "name": "{{ org.name }}", + "displayname": "{{ org.name }}", + "url": "{{ org.url }}" + } + } +} diff --git a/roles/sbs/templates/saml_settings.json.j2 b/roles/sbs/templates/saml_settings.json.j2 new file mode 100644 index 000000000..bb5788e97 --- /dev/null +++ b/roles/sbs/templates/saml_settings.json.j2 @@ -0,0 +1,22 @@ +{ + "strict": true, + "debug": true, + "sp": { + "entityId": "{{ sbs_surf_secure_id.sp_entity_id }}", + "assertionConsumerService": { + "url": "{{ sbs_surf_secure_id.acs_url }}", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", + "x509cert": "{{ sbs_surf_secure_id.pub | barepem }}", + "privateKey": "{{ sbs_surf_secure_id.priv | barepem }}" + }, + "idp": { + "entityId": "{{ sbs_ssid_entityid }}", + "singleSignOnService": { + "url": "{{ sbs_ssid_sso_endpoint }}", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "x509cert": "{{ sbs_surf_secure_id.sa_idp_certificate | barepem }}" + } +} diff --git a/roles/sbs/templates/sbs-apache.conf.j2 b/roles/sbs/templates/sbs-apache.conf.j2 new file mode 100644 index 000000000..0743c2ddb --- /dev/null +++ b/roles/sbs/templates/sbs-apache.conf.j2 @@ -0,0 +1,30 @@ +ServerName {{ sbs.base_domain }} +#ErrorLog /proc/self/fd/2 +#CustomLog /proc/self/fd/1 common +DocumentRoot /opt/sbs/client/dist + +Header set Content-Security-Policy "default-src 'self'; base-uri 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-src 'none'; form-action 'self' https://*.{{ base_domain }}; frame-ancestors 'none'; block-all-mixed-content;" +Header set Permissions-Policy "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(self), gamepad=(), speaker-selection=()" + +RewriteEngine On +RewriteCond %{REQUEST_URI} !^/(api|pam-weblogin|flasgger_static|swagger|health|config|info|socket.io) +RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f +RewriteRule ^/(.*)$ /index.html [L] + +ProxyRequests off +ProxyPassMatch ^/(api|pam-weblogin|flasgger_static|swagger|health|config|info) http://sbs-server:8080/ +ProxyPassReverse / http://sbs-server:8080/ +ProxyPass /socket.io/ ws://sbs-server:8080/socket.io/ +ProxyPassReverse /socket.io/ ws://sbs-server:8080/socket.io/ + + + Header set Cache-Control: "public, max-age=31536000, immutable" + + + Header set Cache-Control: "no-cache, private" + + + + Require all granted + Options -Indexes + diff --git a/roles/sbs/templates/sbs.service.j2 b/roles/sbs/templates/sbs.service.j2 new file mode 100644 index 000000000..2920ddc8d --- /dev/null +++ b/roles/sbs/templates/sbs.service.j2 @@ -0,0 +1,32 @@ +[Unit] +Description=SBS +After=network.target + +[Service] +DynamicUser=true +User=_sram_sbs +Group=_sram_sbs +SupplementaryGroups={{sbs_group}} + +WorkingDirectory={{sbs_git_dir}} +ReadWritePaths={{sbs_log_dir}} +NoNewPrivileges=true +PrivateTmp=true + +Environment="CONFIG=config/config.yml" +Environment="PROFILE=log_to_stdout" +# the python requests module uses the CAs provided by the certifi package by default +# we'll just take the OS-provided CAs, thankyouverymuch +Environment="REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt" + +Type=notify +ExecStart={{sbs_env_dir}}/bin/gunicorn --worker-class eventlet --workers {{sbs_num_workers}} --bind 127.0.0.1:8080 server.__main__:app + +Restart=on-failure +RestartSec=10 + +KillMode=mixed +TimeoutStopSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/roles/sbs/vars/main.yml b/roles/sbs/vars/main.yml new file mode 100644 index 000000000..761942f7b --- /dev/null +++ b/roles/sbs/vars/main.yml @@ -0,0 +1 @@ +current_release_appdir: /opt/openconext From ae9ebeeee7c995317ea9e8baa64cac3d5ca804a3 Mon Sep 17 00:00:00 2001 From: Martin van Es Date: Fri, 27 Mar 2026 14:55:12 +0100 Subject: [PATCH 05/15] Add plsc and mailpit roles --- roles/ldap/defaults/main.yml | 2 +- roles/mailpit/defaults/main.yml | 8 +++++++ roles/mailpit/tasks/main.yml | 37 +++++++++++++++++++++++++++++++ roles/plsc/defaults/main.yml | 15 +++++++++++++ roles/plsc/handlers/main.yml | 18 +++++++++++++++ roles/plsc/tasks/main.yml | 28 +++++++++++++++++++++++ roles/plsc/templates/plsc.yml.j2 | 25 +++++++++++++++++++++ roles/plsc/vars/main.yml | 1 + roles/sbs/defaults/main.yml | 4 +++- roles/sbs/templates/config.yml.j2 | 2 +- 10 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 roles/mailpit/defaults/main.yml create mode 100644 roles/mailpit/tasks/main.yml create mode 100644 roles/plsc/defaults/main.yml create mode 100644 roles/plsc/handlers/main.yml create mode 100644 roles/plsc/tasks/main.yml create mode 100644 roles/plsc/templates/plsc.yml.j2 create mode 100644 roles/plsc/vars/main.yml diff --git a/roles/ldap/defaults/main.yml b/roles/ldap/defaults/main.yml index e60ab603c..7d02ebe4a 100644 --- a/roles/ldap/defaults/main.yml +++ b/roles/ldap/defaults/main.yml @@ -1,5 +1,5 @@ --- -ldap: "{{ ldap_defaults | combine(ldap_overrides) }}" +ldap: "{{ ldap_defaults | combine(ldap_overrides, recursive=true) }}" ldap_defaults: image: "ghcr.io/surfscz/sram-ldap:main" diff --git a/roles/mailpit/defaults/main.yml b/roles/mailpit/defaults/main.yml new file mode 100644 index 000000000..7647de9dc --- /dev/null +++ b/roles/mailpit/defaults/main.yml @@ -0,0 +1,8 @@ +--- +mailpit: "{{ mailpit_defaults | combine(mailpit_overrides, recursive=true) }}" + +mailpit_defaults: + image: "axllent/mailpit" + hostname: "mailpit.{{ base_domain }}" + user: "mailpit" + group: "mailpit" diff --git a/roles/mailpit/tasks/main.yml b/roles/mailpit/tasks/main.yml new file mode 100644 index 000000000..7fb32d8a7 --- /dev/null +++ b/roles/mailpit/tasks/main.yml @@ -0,0 +1,37 @@ +--- +- name: "Create mailpit group" + group: + name: "{{ mailpit.group }}" + state: "present" + register: "result" + +- name: "Create mailpit user" + user: + name: "{{ mailpit.user }}" + group: "{{ mailpit.group }}" + comment: "User to run Mailpit service" + shell: "/bin/false" + password: "!" + create_home: false + state: "present" + register: "result" + +- name: "Save mailpit user uid" + set_fact: + mailpit_user_uid: "{{ result.uid }}" + +- name: "Create mailpit container" + docker_container: + name: "mailpit" + image: "{{ mailpit.image }}" + restart_policy: "always" + state: "started" + user: "{{ mailpit_user_uid }}" + ports: + networks: + - name: "loadbalancer" + labels: + traefik.enable: "true" + traefik.http.routers.mailpit.rule: "Host(`{{ mailpit.hostname }}`)" + traefik.http.routers.mailpit.tls: "true" + traefik.http.services.mailpit.loadbalancer.server.port: 8025 diff --git a/roles/plsc/defaults/main.yml b/roles/plsc/defaults/main.yml new file mode 100644 index 000000000..2a3711b0c --- /dev/null +++ b/roles/plsc/defaults/main.yml @@ -0,0 +1,15 @@ +--- +plsc: "{{ plsc_defaults | combine(plsc_overrides, recursive=true) }}" + +plsc_defaults: + image: "ghcr.io/surfscz/sram-plsc:main" + conf_dir: "{{current_release_appdir}}/sram/plsc" + ansible_nolog: false + ldap_uri: "ldap://ldap:389/" + ldap_basedn: "dc=services,dc=vnet" + ldap_binddn: "cn=admin,dc=vnet" + ldap_password: "secret" + sbs_host: "http://sbs-server:8080" + sbs_user: "sysread" + sbs_password: "secret" + retry: 3 diff --git a/roles/plsc/handlers/main.yml b/roles/plsc/handlers/main.yml new file mode 100644 index 000000000..9ce03e899 --- /dev/null +++ b/roles/plsc/handlers/main.yml @@ -0,0 +1,18 @@ +--- +# - name: enable plsc job +# systemd: +# name: "plsc.timer" +# enabled: true +# state: "restarted" +# daemon_reload: true + +# - name: "restart zabbix-agent" +# systemd: +# name: "zabbix-agent2.service" +# state: "restarted" + +- name: Restart the plsc container + community.docker.docker_container: + name: "plsc" + restart: true + state: started diff --git a/roles/plsc/tasks/main.yml b/roles/plsc/tasks/main.yml new file mode 100644 index 000000000..14e7b40b7 --- /dev/null +++ b/roles/plsc/tasks/main.yml @@ -0,0 +1,28 @@ +--- +- name: Make sure clients sync directory exists + file: + path: "{{ plsc.conf_dir }}" + state: directory + mode: "0755" + +- name: "Create plsc.yml source if it doesn't exist" + template: + src: "plsc.yml.j2" + dest: "{{ plsc.conf_dir }}/plsc.yml" + mode: "0640" + no_log: "{{plsc.ansible_nolog}}" + notify: "Restart the plsc container" + +- name: Create the plsc container + community.docker.docker_container: + name: "plsc" + image: "{{ plsc.image }}" + restart_policy: "always" + state: started + pull: true + mounts: + - type: bind + source: "{{ plsc.conf_dir }}/plsc.yml" + target: "/opt/plsc/plsc.yml" + networks: + - name: "loadbalancer" diff --git a/roles/plsc/templates/plsc.yml.j2 b/roles/plsc/templates/plsc.yml.j2 new file mode 100644 index 000000000..a42c00807 --- /dev/null +++ b/roles/plsc/templates/plsc.yml.j2 @@ -0,0 +1,25 @@ +--- +ldap: + src: + uri: "{{ plsc.ldap_uri }}" + basedn: "{{ plsc.ldap_basedn }}" + binddn: "{{ plsc.ldap_binddn }}" + passwd: "{{ plsc.ldap_password }}" + sizelimit: 500 + dst: + uri: "{{ plsc.ldap_uri }}" + basedn: "{{ plsc.ldap_basedn }}" + binddn: "{{ plsc.ldap_binddn }}" + passwd: "{{ plsc.ldap_password }}" + sizelimit: 500 +sbs: + src: + host: "{{ plsc.sbs_host }}" + user: "{{ plsc.sbs_user }}" + passwd: "{{ plsc.sbs_password }}" + verify_ssl: {{ false if env=='vm' else true }} + timeout: 60 + retry: {{ plsc.retry }} +pwd: "{CRYPT}!" +uid: 1000 +gid: 1000 diff --git a/roles/plsc/vars/main.yml b/roles/plsc/vars/main.yml new file mode 100644 index 000000000..761942f7b --- /dev/null +++ b/roles/plsc/vars/main.yml @@ -0,0 +1 @@ +current_release_appdir: /opt/openconext diff --git a/roles/sbs/defaults/main.yml b/roles/sbs/defaults/main.yml index 9bcd62c87..aedcee959 100644 --- a/roles/sbs/defaults/main.yml +++ b/roles/sbs/defaults/main.yml @@ -1,5 +1,5 @@ --- -sbs: "{{ sbs_defaults | combine(sbs_overrides) }}" +sbs: "{{ sbs_defaults | combine(sbs_overrides, recursive=true) }}" sbs_defaults: base_domain: "test2.sram.surf.nl" @@ -69,6 +69,8 @@ sbs_defaults: ticket_email: "sram-support@surf.nl" eduteams_email: "eduteams@localhost" + suppress_mails: False + wiki_link: "https://www.example.org/wiki" cron_hour_of_day: 4 diff --git a/roles/sbs/templates/config.yml.j2 b/roles/sbs/templates/config.yml.j2 index d052565c2..7d4c92bf4 100644 --- a/roles/sbs/templates/config.yml.j2 +++ b/roles/sbs/templates/config.yml.j2 @@ -71,7 +71,7 @@ mail: port: {{ sbs.mail_port }} sender_name: {{ sbs.mail_sender_name }} sender_email: {{ sbs.mail_sender_email }} - suppress_sending_mails: False + suppress_sending_mails: {{ sbs.suppress_mails }} info_email: {{ sbs.support_email }} beheer_email: {{ sbs.admin_email }} ticket_email: {{ sbs.ticket_email }} From 5c63844037bef539e6f8823abbf15e708596ae31 Mon Sep 17 00:00:00 2001 From: Martin van Es Date: Fri, 27 Mar 2026 16:32:42 +0100 Subject: [PATCH 06/15] Add sram-metadata and rename apps+roles --- roles/{ldap => sram-ldap}/defaults/main.yml | 2 +- .../{ldap => sram-ldap}/files/eduMember.ldif | 0 .../{ldap => sram-ldap}/files/eduPerson.ldif | 0 .../files/groupOfMembers.ldif | 0 roles/{ldap => sram-ldap}/files/ldap-add | 0 .../files/ldapPublicKey.ldif | 0 .../{ldap => sram-ldap}/files/logrotate_slapd | 0 .../files/rsyslog_slapd.conf | 0 roles/{ldap => sram-ldap}/files/sczGroup.ldif | 0 .../{ldap => sram-ldap}/files/sramPerson.ldif | 0 roles/{ldap => sram-ldap}/files/voPerson.ldif | 0 roles/{ldap => sram-ldap}/handlers/main.yml | 0 roles/{ldap => sram-ldap}/tasks/admins.yml | 0 roles/{ldap => sram-ldap}/tasks/main.yml | 4 +- .../templates/ldap-backup.sh.j2 | 0 .../templates/ldap.conf.j2 | 0 .../templates/slapd.service.j2 | 0 roles/{ldap => sram-ldap}/vars/main.yml | 0 roles/sram-metadata/defaults/main.yml | 81 +++++++++++++ roles/sram-metadata/files/01_idps.fd | 23 ++++ roles/sram-metadata/files/02_backend.fd | 14 +++ roles/sram-metadata/files/03_frontend.fd | 14 +++ roles/sram-metadata/files/surf.png | Bin 0 -> 16016 bytes roles/sram-metadata/files/surf.svg | 24 ++++ roles/sram-metadata/files/surf_bimi.svg | 15 +++ roles/sram-metadata/files/surfconext.crt | 3 + roles/sram-metadata/files/transform.xslt | 47 ++++++++ .../sram-metadata/files/transform_proxy.xslt | 50 +++++++++ roles/sram-metadata/handlers/main.yml | 19 ++++ roles/sram-metadata/tasks/http.yml | 48 ++++++++ roles/sram-metadata/tasks/main.yml | 49 ++++++++ roles/sram-metadata/tasks/pyff.yml | 106 ++++++++++++++++++ roles/sram-metadata/templates/index.html.j2 | 11 ++ .../templates/pyff-metadata.service.j2 | 12 ++ .../templates/pyff-metadata.timer.j2 | 8 ++ roles/sram-metadata/templates/vhosts.conf.j2 | 15 +++ roles/{plsc => sram-metadata}/vars/main.yml | 0 roles/sram-midproxy/defaults/main.yml | 8 ++ .../files/internal_attributes.yaml | 22 ++++ .../files/plugins/attribute-maps/basic.py | 51 +++++++++ .../plugins/backends/openid_backend.yaml | 14 +++ .../files/plugins/backends/saml2_backend.yaml | 1 + .../plugins/frontends/ping_frontend.yaml | 3 + .../plugins/frontends/saml2_frontend.yaml | 63 +++++++++++ .../microservices/generate_attributes.yaml | 8 ++ .../microservices/regex_attributes.yaml | 10 ++ roles/sram-midproxy/files/proxy_conf.yaml | 74 ++++++++++++ roles/sram-midproxy/tasks/main.yml | 59 ++++++++++ roles/{plsc => sram-plsc}/defaults/main.yml | 0 roles/{plsc => sram-plsc}/handlers/main.yml | 2 +- roles/{plsc => sram-plsc}/tasks/main.yml | 2 +- .../{plsc => sram-plsc}/templates/plsc.yml.j2 | 0 roles/{redis => sram-plsc}/vars/main.yml | 0 roles/{redis => sram-redis}/defaults/main.yml | 4 +- roles/{redis => sram-redis}/handlers/main.yml | 2 +- roles/{redis => sram-redis}/tasks/main.yml | 2 +- .../templates/redis.conf.j2 | 0 roles/{sbs => sram-redis}/vars/main.yml | 0 roles/{sbs => sram-sbs}/defaults/main.yml | 2 +- roles/{sbs => sram-sbs}/files/yarn.gpg | 0 roles/{sbs => sram-sbs}/handlers/main.yml | 4 +- roles/{sbs => sram-sbs}/tasks/main.yml | 14 +-- .../templates/alembic.ini.j2 | 0 .../{sbs => sram-sbs}/templates/config.yml.j2 | 0 .../templates/disclaimer.css.j2 | 0 .../templates/saml_advanced_settings.json.j2 | 0 .../templates/saml_settings.json.j2 | 0 .../templates/sbs-apache.conf.j2 | 8 +- .../templates/sbs.service.j2 | 0 roles/sram-sbs/vars/main.yml | 1 + 70 files changed, 876 insertions(+), 23 deletions(-) rename roles/{ldap => sram-ldap}/defaults/main.yml (95%) rename roles/{ldap => sram-ldap}/files/eduMember.ldif (100%) rename roles/{ldap => sram-ldap}/files/eduPerson.ldif (100%) rename roles/{ldap => sram-ldap}/files/groupOfMembers.ldif (100%) rename roles/{ldap => sram-ldap}/files/ldap-add (100%) rename roles/{ldap => sram-ldap}/files/ldapPublicKey.ldif (100%) rename roles/{ldap => sram-ldap}/files/logrotate_slapd (100%) rename roles/{ldap => sram-ldap}/files/rsyslog_slapd.conf (100%) rename roles/{ldap => sram-ldap}/files/sczGroup.ldif (100%) rename roles/{ldap => sram-ldap}/files/sramPerson.ldif (100%) rename roles/{ldap => sram-ldap}/files/voPerson.ldif (100%) rename roles/{ldap => sram-ldap}/handlers/main.yml (100%) rename roles/{ldap => sram-ldap}/tasks/admins.yml (100%) rename roles/{ldap => sram-ldap}/tasks/main.yml (99%) rename roles/{ldap => sram-ldap}/templates/ldap-backup.sh.j2 (100%) rename roles/{ldap => sram-ldap}/templates/ldap.conf.j2 (100%) rename roles/{ldap => sram-ldap}/templates/slapd.service.j2 (100%) rename roles/{ldap => sram-ldap}/vars/main.yml (100%) create mode 100644 roles/sram-metadata/defaults/main.yml create mode 100644 roles/sram-metadata/files/01_idps.fd create mode 100644 roles/sram-metadata/files/02_backend.fd create mode 100644 roles/sram-metadata/files/03_frontend.fd create mode 100644 roles/sram-metadata/files/surf.png create mode 100644 roles/sram-metadata/files/surf.svg create mode 100644 roles/sram-metadata/files/surf_bimi.svg create mode 100644 roles/sram-metadata/files/surfconext.crt create mode 100644 roles/sram-metadata/files/transform.xslt create mode 100644 roles/sram-metadata/files/transform_proxy.xslt create mode 100644 roles/sram-metadata/handlers/main.yml create mode 100644 roles/sram-metadata/tasks/http.yml create mode 100644 roles/sram-metadata/tasks/main.yml create mode 100644 roles/sram-metadata/tasks/pyff.yml create mode 100644 roles/sram-metadata/templates/index.html.j2 create mode 100644 roles/sram-metadata/templates/pyff-metadata.service.j2 create mode 100644 roles/sram-metadata/templates/pyff-metadata.timer.j2 create mode 100644 roles/sram-metadata/templates/vhosts.conf.j2 rename roles/{plsc => sram-metadata}/vars/main.yml (100%) create mode 100644 roles/sram-midproxy/defaults/main.yml create mode 100644 roles/sram-midproxy/files/internal_attributes.yaml create mode 100644 roles/sram-midproxy/files/plugins/attribute-maps/basic.py create mode 100644 roles/sram-midproxy/files/plugins/backends/openid_backend.yaml create mode 100644 roles/sram-midproxy/files/plugins/backends/saml2_backend.yaml create mode 100644 roles/sram-midproxy/files/plugins/frontends/ping_frontend.yaml create mode 100644 roles/sram-midproxy/files/plugins/frontends/saml2_frontend.yaml create mode 100644 roles/sram-midproxy/files/plugins/microservices/generate_attributes.yaml create mode 100644 roles/sram-midproxy/files/plugins/microservices/regex_attributes.yaml create mode 100644 roles/sram-midproxy/files/proxy_conf.yaml create mode 100644 roles/sram-midproxy/tasks/main.yml rename roles/{plsc => sram-plsc}/defaults/main.yml (100%) rename roles/{plsc => sram-plsc}/handlers/main.yml (94%) rename roles/{plsc => sram-plsc}/tasks/main.yml (96%) rename roles/{plsc => sram-plsc}/templates/plsc.yml.j2 (100%) rename roles/{redis => sram-plsc}/vars/main.yml (100%) rename roles/{redis => sram-redis}/defaults/main.yml (67%) rename roles/{redis => sram-redis}/handlers/main.yml (83%) rename roles/{redis => sram-redis}/tasks/main.yml (98%) rename roles/{redis => sram-redis}/templates/redis.conf.j2 (100%) rename roles/{sbs => sram-redis}/vars/main.yml (100%) rename roles/{sbs => sram-sbs}/defaults/main.yml (99%) rename roles/{sbs => sram-sbs}/files/yarn.gpg (100%) rename roles/{sbs => sram-sbs}/handlers/main.yml (76%) rename roles/{sbs => sram-sbs}/tasks/main.yml (96%) rename roles/{sbs => sram-sbs}/templates/alembic.ini.j2 (100%) rename roles/{sbs => sram-sbs}/templates/config.yml.j2 (100%) rename roles/{sbs => sram-sbs}/templates/disclaimer.css.j2 (100%) rename roles/{sbs => sram-sbs}/templates/saml_advanced_settings.json.j2 (100%) rename roles/{sbs => sram-sbs}/templates/saml_settings.json.j2 (100%) rename roles/{sbs => sram-sbs}/templates/sbs-apache.conf.j2 (88%) rename roles/{sbs => sram-sbs}/templates/sbs.service.j2 (100%) create mode 100644 roles/sram-sbs/vars/main.yml diff --git a/roles/ldap/defaults/main.yml b/roles/sram-ldap/defaults/main.yml similarity index 95% rename from roles/ldap/defaults/main.yml rename to roles/sram-ldap/defaults/main.yml index 7d02ebe4a..e20f4553d 100644 --- a/roles/ldap/defaults/main.yml +++ b/roles/sram-ldap/defaults/main.yml @@ -3,7 +3,7 @@ ldap: "{{ ldap_defaults | combine(ldap_overrides, recursive=true) }}" ldap_defaults: image: "ghcr.io/surfscz/sram-ldap:main" - conf_dir: "{{ current_release_appdir }}/ldap" + conf_dir: "{{ current_release_appdir }}/sram/ldap" ldif_dir: "{{ ldap_defaults.conf_dir }}/schema" certs_dir: "{{ ldap_defaults.conf_dir }}/certs" backup_dir: "{{ ldap_defaults.conf_dir }}/ldap" diff --git a/roles/ldap/files/eduMember.ldif b/roles/sram-ldap/files/eduMember.ldif similarity index 100% rename from roles/ldap/files/eduMember.ldif rename to roles/sram-ldap/files/eduMember.ldif diff --git a/roles/ldap/files/eduPerson.ldif b/roles/sram-ldap/files/eduPerson.ldif similarity index 100% rename from roles/ldap/files/eduPerson.ldif rename to roles/sram-ldap/files/eduPerson.ldif diff --git a/roles/ldap/files/groupOfMembers.ldif b/roles/sram-ldap/files/groupOfMembers.ldif similarity index 100% rename from roles/ldap/files/groupOfMembers.ldif rename to roles/sram-ldap/files/groupOfMembers.ldif diff --git a/roles/ldap/files/ldap-add b/roles/sram-ldap/files/ldap-add similarity index 100% rename from roles/ldap/files/ldap-add rename to roles/sram-ldap/files/ldap-add diff --git a/roles/ldap/files/ldapPublicKey.ldif b/roles/sram-ldap/files/ldapPublicKey.ldif similarity index 100% rename from roles/ldap/files/ldapPublicKey.ldif rename to roles/sram-ldap/files/ldapPublicKey.ldif diff --git a/roles/ldap/files/logrotate_slapd b/roles/sram-ldap/files/logrotate_slapd similarity index 100% rename from roles/ldap/files/logrotate_slapd rename to roles/sram-ldap/files/logrotate_slapd diff --git a/roles/ldap/files/rsyslog_slapd.conf b/roles/sram-ldap/files/rsyslog_slapd.conf similarity index 100% rename from roles/ldap/files/rsyslog_slapd.conf rename to roles/sram-ldap/files/rsyslog_slapd.conf diff --git a/roles/ldap/files/sczGroup.ldif b/roles/sram-ldap/files/sczGroup.ldif similarity index 100% rename from roles/ldap/files/sczGroup.ldif rename to roles/sram-ldap/files/sczGroup.ldif diff --git a/roles/ldap/files/sramPerson.ldif b/roles/sram-ldap/files/sramPerson.ldif similarity index 100% rename from roles/ldap/files/sramPerson.ldif rename to roles/sram-ldap/files/sramPerson.ldif diff --git a/roles/ldap/files/voPerson.ldif b/roles/sram-ldap/files/voPerson.ldif similarity index 100% rename from roles/ldap/files/voPerson.ldif rename to roles/sram-ldap/files/voPerson.ldif diff --git a/roles/ldap/handlers/main.yml b/roles/sram-ldap/handlers/main.yml similarity index 100% rename from roles/ldap/handlers/main.yml rename to roles/sram-ldap/handlers/main.yml diff --git a/roles/ldap/tasks/admins.yml b/roles/sram-ldap/tasks/admins.yml similarity index 100% rename from roles/ldap/tasks/admins.yml rename to roles/sram-ldap/tasks/admins.yml diff --git a/roles/ldap/tasks/main.yml b/roles/sram-ldap/tasks/main.yml similarity index 99% rename from roles/ldap/tasks/main.yml rename to roles/sram-ldap/tasks/main.yml index f58c21ee7..97977e06a 100644 --- a/roles/ldap/tasks/main.yml +++ b/roles/sram-ldap/tasks/main.yml @@ -68,7 +68,7 @@ - name: Create the ldap container community.docker.docker_container: - name: "ldap" + name: "sram-ldap" image: "{{ ldap.image }}" restart_policy: "always" state: started @@ -112,7 +112,7 @@ - name: Ensure the schemas are added to LDAP ansible.builtin.shell: # For now the target side /opt/ldap is hard-coded - cmd: "docker exec ldap /opt/ldap/ldap-add /opt/ldap/schema/{{ item }}" + cmd: "docker exec sram-ldap /opt/ldap/ldap-add /opt/ldap/schema/{{ item }}" register: "result" failed_when: "result.rc not in [0,80]" changed_when: "result.rc != 80" diff --git a/roles/ldap/templates/ldap-backup.sh.j2 b/roles/sram-ldap/templates/ldap-backup.sh.j2 similarity index 100% rename from roles/ldap/templates/ldap-backup.sh.j2 rename to roles/sram-ldap/templates/ldap-backup.sh.j2 diff --git a/roles/ldap/templates/ldap.conf.j2 b/roles/sram-ldap/templates/ldap.conf.j2 similarity index 100% rename from roles/ldap/templates/ldap.conf.j2 rename to roles/sram-ldap/templates/ldap.conf.j2 diff --git a/roles/ldap/templates/slapd.service.j2 b/roles/sram-ldap/templates/slapd.service.j2 similarity index 100% rename from roles/ldap/templates/slapd.service.j2 rename to roles/sram-ldap/templates/slapd.service.j2 diff --git a/roles/ldap/vars/main.yml b/roles/sram-ldap/vars/main.yml similarity index 100% rename from roles/ldap/vars/main.yml rename to roles/sram-ldap/vars/main.yml diff --git a/roles/sram-metadata/defaults/main.yml b/roles/sram-metadata/defaults/main.yml new file mode 100644 index 000000000..5355989bc --- /dev/null +++ b/roles/sram-metadata/defaults/main.yml @@ -0,0 +1,81 @@ +--- +metadata: "{{ metadata_defaults | combine(metadata_overrides, recursive=true) }}" + +metadata_defaults: + image_server: "ghcr.io/openconext/openconext-basecontainers/apache2:latest" + image_pyff: "ghcr.io/surfscz/sram-pyff:main" + hostname: "meta.{{ base_domain }}" + basedir: "{{current_release_appdir}}/sram/metadata" + + # server_name: "metadata-server" + + user: "sram-metadata" + group: "sram-metadata" + +# idps_source: "https://metadata.surfconext.nl/idps-metadata.xml" +# idps_cert: | +# -----BEGIN CERTIFICATE----- +# MIIEKjCCAhICEG12w6QqayYAWntxDN59dU0wDQYJKoZIhvcNAQELBQAwPDELMAkG +# A1UEBhMCTkwxEDAOBgNVBAoMB1NVUkZuZXQxGzAZBgNVBAMMElNVUkZjb25leHQg +# Um9vdCBDQTAeFw0xOTAxMTQxNjM5MDVaFw0yNDAxMTgxNjM5MDVaMGsxCzAJBgNV +# BAYTAk5MMRAwDgYDVQQIDAdVdHJlY2h0MRAwDgYDVQQKDAdTVVJGbmV0MRMwEQYD +# VQQLDApTVVJGY29uZXh0MSMwIQYDVQQDDBpTVVJGY29uZXh0IG1ldGFkYXRhIHNp +# Z25lcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMckFyqXzW7dbMt4 +# wDdSLaAjFAbNziUgQaivu4dl9Uf/cZ4f36a9DfQBUSraNoIR76ruwK3TPfFalemp +# xmWTsoVSQpb3AOsWbU+i0YKS1cmcqMUC1fef2j1IbuK4B4nEu9S5saGNVGNvUJ+Y +# jDUpC5vyyp7boW9E1md2jIBI6Mw+ZhlmkPucqaphxurWnm0KbxTZrYLOBZ1IXj6r +# yrRoFwwtjEH+CW8cRn8OATK0q4yb0BVr2gY2tp/lTpASHZ3WVWBK0prwK0KkusY6 +# ck+/vvlk46IdEr803NB0Dm3ECh3i65mfCaWzVTtd/md874paK+65f1JeVyd5I5al +# M2KEpvkCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAjvJXXkxOqh3K0k2NdDG5EOTy +# bA+koRbAqhdY/qJoSnqTzwBxJc6aPs+L4q2PIoLo0gNJj1Nm1taLusaaK+CBx3ar +# 1kxEika5FM0dqFjD3i7Y5U0FMeDB5cReo8TNdo31VGoY7CbRjtqHLRTuKzNmIfEm +# ahLnHIBtarE82b7Mpg0aLxjrRR+t8wSCriy+e9AEPzC5bWxtPJA+OhU8U9hMuOs5 +# SzKmHwYue4WY3q1rRaDpK3fqgXRDRfznNn9/RDDbBos7CRMSAPEmAO28qLKBW/1z +# a2TKQLddZ3uoCurFNbToSTueKYVEnveQNO2P5X6uy4rcYkjeSiwbmHo7jYuHAxx4 +# uGzHMpoqoGNx+2iYjtUo3dJUXzcZai3X+RuuMKXXvqGzrxJsoKayNVAE1dWoUHJl +# RouPhDLTdZq/pblORhFS8r10rKhSScgrNuN9LTTV7EPFeVr8trocNwl8IruH+eNL +# 6/7b5Y7fb7rvpxeHjWrTz8a9BXAIAv+bgyrg4OHGRcNIQb0XF438HD9r8Zb92B6Z +# VCR3aVS5496+1td+8aN/Blzo59LhKPiHyGZCPHFV/oBqG7nxp603kcWmJOcG+AgB +# 9bFiAimF5LLk/LnMfplK9w0vvxWVcdQkDgVPYvEGNtttj0QC7/jM4ZeihGb6Oyzy +# DZA6aeg73/ygOATQ13A= +# -----END CERTIFICATE----- + idps_filters: [] + + idps_files: + - name: "dummy-idp" + metadata: | + + + + + + + SRAM VM Dummy IdP + SRAM VM Dummy IdP + https://test-idp.sram.example.org/ + + + Administrator + mailto:sinterklaas@example.nl + + + +# idps_xrd: "{{metadata_defaults.basedir}}/certs/surfconext.xrd" +# idps_source_dir: "/opt/metadata-src" +# idps_feed: "{{ metadata_defaults.basedir }}/idps_feed.fd" +# idps_file: "idps.xml" +## +# proxy_frontend_source: "https://satosa.local/frontend.xml" +# proxy_frontend_feed: "{{ metadata_defaults.basedir }}/frontend_feed.fd" +# proxy_frontend_file: "proxy_idp.xml" +# +# proxy_backend_source: "https://satosa.local/metadata/backend.xml" +# proxy_backend_feed: "{{ metadata_defaults.basedir }}/backend_feed.fd" +# proxy_backend_file: "proxy_sp.xml" diff --git a/roles/sram-metadata/files/01_idps.fd b/roles/sram-metadata/files/01_idps.fd new file mode 100644 index 000000000..46d58b663 --- /dev/null +++ b/roles/sram-metadata/files/01_idps.fd @@ -0,0 +1,23 @@ +- load fail_on_error True: + #- "https://metadata.test.surfconext.nl/idps-metadata.xml verify certs/surfconext.crt" + - "https://metadata.test.surfconext.nl/idps-metadata.xml" + - "src/" +- select: + - "https://idp.diy.surfconext.nl/saml2/idp/metadata.php" + - "http://mock-idp" + - "https://login.test.eduid.nl" + - "https://idp-acc.surfnet.nl" + - "https://login.uaccess-a.leidenuniv.nl/nidp/saml2/metadata" + - "test-idp.lab.surf.nl" + - "https://test-idp.sram.surf.nl/saml/saml2/idp/metadata.php" + - "https://idp.ci-runner.sram.surf.nl/saml/saml2/idp/metadata.php" +- xslt: + stylesheet: "xslt/transform.xslt" +- finalize: + cacheDuration: P7D + validUntil: P14D +- sign: + key: "certs/signing.key" + cert: "certs/signing.crt" +- publish: "out/idps.xml.new" +- stats diff --git a/roles/sram-metadata/files/02_backend.fd b/roles/sram-metadata/files/02_backend.fd new file mode 100644 index 000000000..698d615a4 --- /dev/null +++ b/roles/sram-metadata/files/02_backend.fd @@ -0,0 +1,14 @@ +- load fail_on_error True: + - "https://proxy.acc.sram.eduteams.org/metadata/backend.xml" +- select +- xslt: + stylesheet: "xslt/transform_proxy.xslt" +- finalize: + cacheDuration: P7D + validUntil: P14D + name: "SURF Research Access Management" +- sign: + key: "certs/signing.key" + cert: "certs/signing.crt" +- publish: "out/proxy_sp.xml.new" +- stats diff --git a/roles/sram-metadata/files/03_frontend.fd b/roles/sram-metadata/files/03_frontend.fd new file mode 100644 index 000000000..252206d42 --- /dev/null +++ b/roles/sram-metadata/files/03_frontend.fd @@ -0,0 +1,14 @@ +- load fail_on_error True: + - "https://proxy.acc.sram.eduteams.org/metadata/frontend.xml" +- select +- xslt: + stylesheet: "xslt/transform_proxy.xslt" +- finalize: + cacheDuration: P7D + validUntil: P14D + name: "SURF Research Access Management" +- sign: + key: "certs/signing.key" + cert: "certs/signing.crt" +- publish: "out/proxy_idp.xml.new" +- stats diff --git a/roles/sram-metadata/files/surf.png b/roles/sram-metadata/files/surf.png new file mode 100644 index 0000000000000000000000000000000000000000..e2bc4a3c2b61cabcd3babac9f9b67ef8c860dfbc GIT binary patch literal 16016 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 + + + + + + + + + + + + + diff --git a/roles/sram-metadata/files/surf_bimi.svg b/roles/sram-metadata/files/surf_bimi.svg new file mode 100644 index 000000000..f49b7a035 --- /dev/null +++ b/roles/sram-metadata/files/surf_bimi.svg @@ -0,0 +1,15 @@ + + + surf + + + + + + + + + + + + diff --git a/roles/sram-metadata/files/surfconext.crt b/roles/sram-metadata/files/surfconext.crt new file mode 100644 index 000000000..0e8f074e8 --- /dev/null +++ b/roles/sram-metadata/files/surfconext.crt @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIIFbjCCA1agAwIBAgIQagXJvtKqIRRO8zD41OktRjANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJOTDEQMA4GA1UEBwwHVXRyZWNodDEQMA4GA1UECAwHVXRyZWNodDESMBAGA1UECgwJU1VSRiBCLlYuMRMwEQYDVQQLDApTVVJGY29uZXh0MSAwHgYDVQQDDBdTVVJGY29uZXh0IENBIDIwMjMgVEVTVDAeFw0yMzA2MDcxMTQxNDRaFw0yNTA2MDYxMTQxNDRaMIGVMQswCQYDVQQGEwJOTDEQMA4GA1UEBwwHVXRyZWNodDEQMA4GA1UECAwHVXRyZWNodDESMBAGA1UECgwJU1VSRiBCLlYuMRMwEQYDVQQLDApTVVJGY29uZXh0MTkwNwYDVQQDDDBTVVJGY29uZXh0IHRlc3QgZW52aXJvbm1lbnQgbWV0YWRhdGEgc2lnbmVyIDIwMjMwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC1Wj1MYwzY646Wa9td4zUZb5W27+cbARhNbIZsteUIPV6unxoO6tHCLJhRxC4pBTQsdrhfhh3+s5rwm8mjhJs2rciQkCdPiTl860jqihhWi5bFXyGX5o1U5mZgomUT+o7+nUj0et1l/kbFJ0GqIKtf0uPj7R/zqTpqeT0c6VFxchU6LA8GOI9w5XIISEGi/IWlDKyM69I3DmbCip/rm8u6kIQ0qqXh58lNNOsZw8WYokCXP0IPFQWpPkKC1VGYtivwKLzzvNxSGcuvp39ui+37hrdjqiTxK68Z48vJ6l+KsJP+jpDXYBYE/NsSVYez3vbVTB/l664yvBfKyGIHHDdTq5akLCQDgYQzjeNOU1oSZbcsub0k+osp7MFGkslYRhLb0V9tX0Xu+7jXzGthPUWicN0XdlHS0JOlSgOBftPn8kcqYNMF0IZVe6V/AVgfj4/4iDk3OKl9FRctFp3kSa8GzLIbjqmYXpGGIEse6U2gfqHS9WHu4odfKH7rhD3hZssCAwEAAaNSMFAwHQYDVR0OBBYEFNclSgPTrGp4QJQZGjFu6VEBTX4PMB8GA1UdIwQYMBaAFI5kmzwW92s2rRY2B5NNjSYI2oj1MA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAORNL7FGBkeq6u/rmcNf+jZZz27vw86COPOiN6ygTyxaBq5fmJ4JZlDnlfO4C/4iek2QjKdgPlpvATGUUMXJdO6a7A3/vXNuoIGu3Ug9GW4vpTVPulaYZedPHC8zBsxwRKwxpSTda7ubWDxH3vUxHz/zDOD2O71O6KFj6Ph8JXwa3TLH0xRN5CXa0UMKX0S+ck8MahCYnMtd99EBL/uOr0+D4q2HwxDRDpL4I9yRwyWxCafoR+6OfzO/vc/SGcjEk/9s0DrMKDkDTJlE9eZbaaWFFCkAkg3LHHLMYjykcTvjDEV75OohYcEC5/6uKHcB/ZQjHwkPBqv9pUF897yZ7sxS66GEJmqqVIC+ayWRvC8N+UmvMGWAdohrY7r7CPeTE+iVHaeB7xGTSI9BhTEv3yMNHhqzqIOvgr8h5iCv7B5hQL+V7MRqD7e7X9uRR7wbyGmwT4p4VFbz5VqthCOFobsMxam9Axt+saebRyH6Mg3Ro9D5WgGoZmTP1yyiMrmEHQdf9+iblbfTbRW0irlaX5t58fWB1u4QZqcamlhVcl65Fub0g+QkSyGDMD9G57z3CKOluNy6TxFZOxMynY6CEtaozDaiETm7NaNC1lkhi+SOHKRX5+q0KqJdnEC7GOX69hSDsCT905dpVnr8JgFKoUfXWSmbwTMj45190dw7RMzk= +-----END CERTIFICATE----- diff --git a/roles/sram-metadata/files/transform.xslt b/roles/sram-metadata/files/transform.xslt new file mode 100644 index 000000000..80673b688 --- /dev/null +++ b/roles/sram-metadata/files/transform.xslt @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/roles/sram-metadata/files/transform_proxy.xslt b/roles/sram-metadata/files/transform_proxy.xslt new file mode 100644 index 000000000..6e6f55d09 --- /dev/null +++ b/roles/sram-metadata/files/transform_proxy.xslt @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Security Response Team + mailto:securityincident@surf.nl + + + + + + + + + + + https://refeds.org/sirtfi2 + https://refeds.org/sirtfi + + + + + + diff --git a/roles/sram-metadata/handlers/main.yml b/roles/sram-metadata/handlers/main.yml new file mode 100644 index 000000000..2787eba12 --- /dev/null +++ b/roles/sram-metadata/handlers/main.yml @@ -0,0 +1,19 @@ +--- +# - name: "enable pyff-metadata job" +# systemd: +# name: "pyff-metadata.timer" +# enabled: true +# state: "started" +# daemon_reload: true + +# - name: "run pyff-metadata job" +# systemd: +# name: "pyff-metadata.service" +# state: "started" +# daemon_reload: true + +- name: Restart the pyFF container + community.docker.docker_container: + name: "sram-metadata-pyff" + restart: true + state: started diff --git a/roles/sram-metadata/tasks/http.yml b/roles/sram-metadata/tasks/http.yml new file mode 100644 index 000000000..befcc1d18 --- /dev/null +++ b/roles/sram-metadata/tasks/http.yml @@ -0,0 +1,48 @@ +--- +- name: "Install index page" + template: + src: "index.html.j2" + dest: "{{metadata.basedir}}/web/index.html" + mode: "0644" + +- name: "Install legacy link" + file: + src: "." + dest: "{{metadata.basedir}}/web/metadata" + state: "link" + +- name: "Install logos" + copy: + src: "{{item}}" + dest: "{{metadata.basedir}}/web" + mode: "0644" + with_items: + - "surf.svg" + - "surf.png" + - "surf_bimi.svg" + +- name: "Create the metadata-server container" + community.docker.docker_container: + name: "sram-metadata-server" + image: "{{ metadata.image_server }}" + restart_policy: "always" + state: "started" + pull: true + mounts: + - source: "{{metadata.basedir}}/web" + target: "/var/www/html" + type: "bind" + read_only: true + networks: + - name: "loadbalancer" + labels: + traefik.http.routers.metadata.rule: "Host(`{{ metadata.hostname }}`)" + traefik.http.routers.metadata.tls: "true" + traefik.enable: "true" + healthcheck: + test: [ "CMD", "curl", "-fail", "http://localhost/" ] + interval: "10s" + timeout: "5s" + retries: 3 + start_period: "5s" + diff --git a/roles/sram-metadata/tasks/main.yml b/roles/sram-metadata/tasks/main.yml new file mode 100644 index 000000000..d6ac55f29 --- /dev/null +++ b/roles/sram-metadata/tasks/main.yml @@ -0,0 +1,49 @@ +--- +- name: "Create metadata group" + group: + name: "{{ metadata.group }}" + state: "present" + register: "result" + +- name: "Save metadata group gid" + set_fact: + metadata_group_gid: "{{ result.gid }}" + +- name: "Create metadata user" + user: + name: "{{ metadata.user }}" + group: "{{ metadata.group }}" + comment: "User to run metadata service" + shell: "/bin/false" + password: "!" + home: "{{ metadata.basedir }}" + create_home: false + state: "present" + register: "result" + +- name: "Save metadata user uid" + set_fact: + metadata_user_uid: "{{ result.uid }}" + + +- name: "Create metadata directories" + file: + path: "{{ item.dir }}" + state: "directory" + mode: "{{ item.mode }}" + owner: "root" + group: "{{ metadata.group }}" + with_items: + - { dir: "{{metadata.basedir}}/web", mode: "0775" } + - { dir: "{{metadata.basedir}}/feeds", mode: "0755" } + - { dir: "{{metadata.basedir}}/src", mode: "0755" } + - { dir: "{{metadata.basedir}}/certs", mode: "0755" } + - { dir: "{{metadata.basedir}}/xslt", mode: "0755" } + notify: "Restart the pyFF container" + + +- name: "Start pyff container" + include_tasks: "pyff.yml" + +- name: "Start http container" + include_tasks: "http.yml" diff --git a/roles/sram-metadata/tasks/pyff.yml b/roles/sram-metadata/tasks/pyff.yml new file mode 100644 index 000000000..6c66a5696 --- /dev/null +++ b/roles/sram-metadata/tasks/pyff.yml @@ -0,0 +1,106 @@ +--- +- name: "create self-signed Metadata Signing SSL certs" + shell: + cmd: ' + openssl genrsa -out "{{ metadata.basedir }}/certs/signing.key" 2048; + openssl req -new -nodes -x509 -subj "/C=NL/CN=signing" + -days 3650 -key "{{ metadata.basedir }}/certs/signing.key" + -out "{{ metadata.basedir }}/certs/signing.crt" -extensions v3_ca; + chown {{metadata.user}}:{{metadata.group}} {{ metadata.basedir }}/certs/*; + ' + creates: "{{ metadata.basedir }}/certs/signing.crt" + when: "metadata.signing_cert is not defined" + notify: "Restart the pyFF container" + +- name: "Write fixed Metadata signing certificates" + copy: + dest: "{{ metadata.basedir }}/certs/{{ item.file }}" + content: "{{item.contents}}" + mode: "{{item.mode}}" + owner: "{{metadata.user}}" + group: "{{metadata.group}}" + with_items: + - { file: "signing.key", mode: "0640", contents: "{{metadata.signing_cert.priv}}" } + - { file: "signing.crt", mode: "0644", contents: "{{metadata.signing_cert.pub}}" } + when: "metadata.signing_cert is defined" + notify: "Restart the pyFF container" + +- name: "Copy source certificates" + copy: + src: "{{ item }}" + dest: "{{ metadata.basedir }}/certs" + mode: "0644" + with_items: + - "surfconext.crt" + notify: "Restart the pyFF container" + +- name: "Install IdP metadata" + copy: + content: "{{item.metadata}}" + dest: "{{ metadata.basedir }}/src/{{item.name}}.xml" + mode: "0644" + with_items: "{{ metadata.idps_files }}" + notify: "Restart the pyFF container" + +- name: "Copy pyFF xslt transformations" + copy: + src: "{{item}}" + dest: "{{metadata.basedir}}/xslt" + mode: "0644" + with_items: + - "transform_proxy.xslt" + - "transform.xslt" + notify: "Restart the pyFF container" + +- name: "Copy pyFF feeds" + copy: + src: "{{item}}" + dest: "{{metadata.basedir}}/feeds" + mode: "0644" + with_items: + - "01_idps.fd" + - "02_backend.fd" + - "03_frontend.fd" + notify: "Restart the pyFF container" + +- name: "Create the pyFF container" + community.docker.docker_container: + name: "sram-metadata-pyff" + image: "{{ metadata.image_pyff }}" + restart_policy: "always" + state: "started" + pull: true + init: true + env: + USER: "{{ metadata_user_uid | string }}" + GROUP: "{{ metadata_group_gid | string }}" + mounts: + - source: "{{ metadata.basedir }}/web" + target: "/opt/pyff/web" + type: "bind" + - source: "{{ metadata.basedir }}/feeds" + target: "/opt/pyff/feeds" + type: "bind" + read_only: true + - source: "{{ metadata.basedir }}/src" + target: "/opt/pyff/src" + type: "bind" + read_only: true + - source: "{{ metadata.basedir }}/certs" + target: "/opt/pyff/certs" + type: "bind" + read_only: true + - source: "{{ metadata.basedir }}/xslt" + target: "/opt/pyff/xslt" + type: "bind" + read_only: true + healthcheck: + test: + - "CMD" + - "bash" + - "-c" + - "[[ $(($(date +%s)-$(date -r /opt/pyff/web/idps.xml +%s))) -lt 400 ]]" + interval: "10s" + timeout: "5s" + retries: 3 + start_period: "5s" diff --git a/roles/sram-metadata/templates/index.html.j2 b/roles/sram-metadata/templates/index.html.j2 new file mode 100644 index 000000000..f0e40b22b --- /dev/null +++ b/roles/sram-metadata/templates/index.html.j2 @@ -0,0 +1,11 @@ + +SRAM + + +

SRAM metadata

+

SRAM IdP proxy metadata
+(for use by Service Providers)

+

SRAM SP proxy metadata
+(for use by Identity Providers)

+ + diff --git a/roles/sram-metadata/templates/pyff-metadata.service.j2 b/roles/sram-metadata/templates/pyff-metadata.service.j2 new file mode 100644 index 000000000..3df9cc6e8 --- /dev/null +++ b/roles/sram-metadata/templates/pyff-metadata.service.j2 @@ -0,0 +1,12 @@ +[Unit] +Description=pyFF Metadata processing +After=syslog.target network.target + +[Service] +Type=oneshot +WorkingDirectory={{metadata.basedir}} +ExecStart=echo "pyff-metadata" +SyslogIdentifier=pyff-metadata + +[Install] +WantedBy=multi-user.target diff --git a/roles/sram-metadata/templates/pyff-metadata.timer.j2 b/roles/sram-metadata/templates/pyff-metadata.timer.j2 new file mode 100644 index 000000000..b1231af1f --- /dev/null +++ b/roles/sram-metadata/templates/pyff-metadata.timer.j2 @@ -0,0 +1,8 @@ +[Unit] +Description=Create Metadata timer + +[Timer] +OnCalendar=*:00 + +[Install] +WantedBy=multi-user.target diff --git a/roles/sram-metadata/templates/vhosts.conf.j2 b/roles/sram-metadata/templates/vhosts.conf.j2 new file mode 100644 index 000000000..be3733827 --- /dev/null +++ b/roles/sram-metadata/templates/vhosts.conf.j2 @@ -0,0 +1,15 @@ + + ServerName sram-metadata-server + DocumentRoot /var/www/html + Header always set Referrer-Policy "strict-origin-when-cross-origin" + Header always set X-Content-Type-Options "nosniff" + Header always set X-XSS-Protection "1; mode=block" + + Require all granted + + + Require all granted + Options FollowSymLinks + Options -MultiViews + + diff --git a/roles/plsc/vars/main.yml b/roles/sram-metadata/vars/main.yml similarity index 100% rename from roles/plsc/vars/main.yml rename to roles/sram-metadata/vars/main.yml diff --git a/roles/sram-midproxy/defaults/main.yml b/roles/sram-midproxy/defaults/main.yml new file mode 100644 index 000000000..3522fcb47 --- /dev/null +++ b/roles/sram-midproxy/defaults/main.yml @@ -0,0 +1,8 @@ +--- +midproxy: + satosa_version: 8 + state_encryption_key: 'secret' + issuer: 'issuer' + client_id: 'client' + client_secret: 'secret' + sp_metadata: 'eb-metadata.xml' diff --git a/roles/sram-midproxy/files/internal_attributes.yaml b/roles/sram-midproxy/files/internal_attributes.yaml new file mode 100644 index 000000000..eb3dcd66e --- /dev/null +++ b/roles/sram-midproxy/files/internal_attributes.yaml @@ -0,0 +1,22 @@ +attributes: + displayname: + openid: [name] + saml: [displayName] + givenname: + openid: [given_name] + saml: [givenName] + mail: + openid: [email] + saml: [mail] + name: + openid: [name] + saml: [cn] + surname: + openid: [family_name] + saml: [sn, surname] + uid: + openid: [sub] + saml: [uid] + schachomeorganization: + openid: [schac_home_organization] + saml: [schacHomeOrganization] diff --git a/roles/sram-midproxy/files/plugins/attribute-maps/basic.py b/roles/sram-midproxy/files/plugins/attribute-maps/basic.py new file mode 100644 index 000000000..f98466df5 --- /dev/null +++ b/roles/sram-midproxy/files/plugins/attribute-maps/basic.py @@ -0,0 +1,51 @@ +DEF = "urn:mace:dir:attribute-def:" +TERENA = "urn:mace:terena.org:attribute-def:" + +MAP = { + "identifier": "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", + "fro": { + f"{TERENA}schacHomeOrganization": "schacHomeOrganization", + f"{DEF}cn": "cn", + f"{DEF}displayName": "displayName", + f"{DEF}eduPersonAffiliation": "eduPersonAffiliation", + f"{DEF}eduPersonEntitlement": "eduPersonEntitlement", + f"{DEF}eduPersonPrincipalName": "eduPersonPrincipalName", + f"{DEF}eduPersonScopedAffiliation": "eduPersonScopedAffiliation", + f"{DEF}eduPersonTargetedID": "eduPersonTargetedID", + f"{DEF}eduPersonAssurance": "eduPersonAssurance", + f"{DEF}email": "email", + f"{DEF}emailAddress": "emailAddress", + f"{DEF}givenName": "givenName", + f"{DEF}gn": "gn", + f"{DEF}isMemberOf": "isMemberOf", + f"{DEF}mail": "mail", + f"{DEF}member": "member", + f"{DEF}name": "name", + f"{DEF}sn": "sn", + f"{DEF}surname": "surname", + f"{DEF}uid": "uid", + }, + "to": { + "schacHomeOrganization": f"{TERENA}schacHomeOrganization", + "cn": f"{DEF}cn", + "displayName": f"{DEF}displayName", + "eduPersonAffiliation": f"{DEF}eduPersonAffiliation", + "eduPersonEntitlement": f"{DEF}eduPersonEntitlement", + "eduPersonPrincipalName": f"{DEF}eduPersonPrincipalName", + "eduPersonScopedAffiliation": f"{DEF}eduPersonScopedAffiliation", + "eduPersonTargetedID": f"{DEF}eduPersonTargetedID", + "eduPersonAssurance": f"{DEF}eduPersonAssurance", + "eduPersonOrcid": f"{DEF}eduPersonOrcid", + "email": f"{DEF}email", + "emailAddress": f"{DEF}emailAddress", + "givenName": f"{DEF}givenName", + "gn": f"{DEF}gn", + "isMemberOf": f"{DEF}isMemberOf", + "mail": f"{DEF}mail", + "member": f"{DEF}member", + "name": f"{DEF}name", + "sn": f"{DEF}sn", + "surname": f"{DEF}surname", + "uid": f"{DEF}uid", + }, +} diff --git a/roles/sram-midproxy/files/plugins/backends/openid_backend.yaml b/roles/sram-midproxy/files/plugins/backends/openid_backend.yaml new file mode 100644 index 000000000..cb78fcccd --- /dev/null +++ b/roles/sram-midproxy/files/plugins/backends/openid_backend.yaml @@ -0,0 +1,14 @@ +module: satosa.backends.openid_connect.OpenIDConnectBackend +name: myaccessid +config: + provider_metadata: + issuer: !ENV SATOSA_ISSUER + client: + verify_ssl: yes + auth_req_params: + response_type: code + scope: [openid, profile, email, schac_home_organization] + client_metadata: + client_id: !ENV SATOSA_CLIENT_ID + client_secret: !ENV SATOSA_CLIENT_SECRET + redirect_uris: [/] diff --git a/roles/sram-midproxy/files/plugins/backends/saml2_backend.yaml b/roles/sram-midproxy/files/plugins/backends/saml2_backend.yaml new file mode 100644 index 000000000..ed97d539c --- /dev/null +++ b/roles/sram-midproxy/files/plugins/backends/saml2_backend.yaml @@ -0,0 +1 @@ +--- diff --git a/roles/sram-midproxy/files/plugins/frontends/ping_frontend.yaml b/roles/sram-midproxy/files/plugins/frontends/ping_frontend.yaml new file mode 100644 index 000000000..c09b218b6 --- /dev/null +++ b/roles/sram-midproxy/files/plugins/frontends/ping_frontend.yaml @@ -0,0 +1,3 @@ +module: satosa.frontends.ping.PingFrontend +name: ping +config: null diff --git a/roles/sram-midproxy/files/plugins/frontends/saml2_frontend.yaml b/roles/sram-midproxy/files/plugins/frontends/saml2_frontend.yaml new file mode 100644 index 000000000..1f8029b66 --- /dev/null +++ b/roles/sram-midproxy/files/plugins/frontends/saml2_frontend.yaml @@ -0,0 +1,63 @@ +module: satosa.frontends.saml2.SAMLFrontend +name: idp +config: + #acr_mapping: + # "": "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified" + # "https://accounts.google.com": "http://eidas.europa.eu/LoA/low" + + endpoints: + single_sign_on_service: + 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST': sso/post + 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect': sso/redirect + + # If configured and not false or empty the common domain cookie _saml_idp will be set + # with or have appended the IdP used for authentication. The default is not to set the + # cookie. If the value is a dictionary with key 'domain' then the domain for the cookie + # will be set to the value for the 'domain' key. If no 'domain' is set then the domain + # from the BASE defined for the proxy will be used. + #common_domain_cookie: + # domain: .example.com + + entityid_endpoint: true + enable_metadata_reload: no + + idp_config: + organization: {display_name: SURF, name: SURF, url: 'https://www.surf.nl/'} + contact_person: + - {contact_type: technical, email_address: 'mailto:sram-beheer@surf.nl', given_name: Technical} + - {contact_type: support, email_address: 'mailto:sram-beheer@surf.nl', given_name: Support} + - {contact_type: other, email_address: 'mailto:sram-beheer@surf.nl', given_name: Security, extension_attributes: {'xmlns:remd': 'http://refeds.org/metadata', 'remd:contactType': 'http://refeds.org/metadata/contactType/security'}} + key_file: frontend.key + cert_file: frontend.crt + metadata: + # remote: + # - url: https://engine.test2.surfconext.nl/authentication/sp/metadata + # cert: null + local: [!ENV SATOSA_SP_METADATA] + entityid: //proxy.xml + accepted_time_diff: 60 + attribute_map_dir: plugins/attribute-maps + service: + idp: + endpoints: + single_sign_on_service: [] + name: Proxy IdP + ui_info: + display_name: + - lang: en + text: "MyAccessID proxy" + description: + - lang: en + text: "MyAccessID proxy" + keywords: + - lang: en + text: ["MyAccessID", "proxy"] + name_id_format: ['urn:oasis:names:tc:SAML:2.0:nameid-format:persistent', 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'] + policy: + default: + fail_on_missing_requested: false + name_form: urn:oasis:names:tc:SAML:2.0:attrname-format:basic + attribute_restrictions: null + lifetime: {minutes: 15} + encrypt_assertion: false + encrypted_advice_attributes: false diff --git a/roles/sram-midproxy/files/plugins/microservices/generate_attributes.yaml b/roles/sram-midproxy/files/plugins/microservices/generate_attributes.yaml new file mode 100644 index 000000000..86ac4e1f1 --- /dev/null +++ b/roles/sram-midproxy/files/plugins/microservices/generate_attributes.yaml @@ -0,0 +1,8 @@ +module: satosa.micro_services.attribute_generation.AddSyntheticAttributes +name: AddSyntheticAttributes +config: + synthetic_attributes: + default: + default: + schachomeorganization: >- + {{ uid.scope }} diff --git a/roles/sram-midproxy/files/plugins/microservices/regex_attributes.yaml b/roles/sram-midproxy/files/plugins/microservices/regex_attributes.yaml new file mode 100644 index 000000000..e820311e7 --- /dev/null +++ b/roles/sram-midproxy/files/plugins/microservices/regex_attributes.yaml @@ -0,0 +1,10 @@ +module: satosa.micro_services.attribute_processor.AttributeProcessor +name: RegexAttributeProcessor +config: + process: + - attribute: uid + processors: + - name: RegexSubProcessor + module: satosa.micro_services.processors.regex_sub_processor + regex_sub_match_pattern: ^(.+)@.+$ + regex_sub_replace_pattern: \1 diff --git a/roles/sram-midproxy/files/proxy_conf.yaml b/roles/sram-midproxy/files/proxy_conf.yaml new file mode 100644 index 000000000..136268e61 --- /dev/null +++ b/roles/sram-midproxy/files/proxy_conf.yaml @@ -0,0 +1,74 @@ +# BASE: https://example.com +BASE: !ENV SATOSA_BASE + +COOKIE_STATE_NAME: "SATOSA_STATE" +CONTEXT_STATE_DELETE: yes +#STATE_ENCRYPTION_KEY: "asdASD123" + +cookies_samesite_compat: + - ["SATOSA_STATE", "SATOSA_STATE_LEGACY"] + +INTERNAL_ATTRIBUTES: "internal_attributes.yaml" + +BACKEND_MODULES: + - "plugins/backends/openid_backend.yaml" + +FRONTEND_MODULES: + - "plugins/frontends/saml2_frontend.yaml" + - "plugins/frontends/ping_frontend.yaml" + +MICRO_SERVICES: + - "plugins/microservices/generate_attributes.yaml" + - "plugins/microservices/regex_attributes.yaml" + +LOGGING: + version: 1 + formatters: + simple: + format: "[%(asctime)s] [%(levelname)s] [%(name)s.%(funcName)s] %(message)s" + handlers: + stdout: + class: logging.StreamHandler + stream: "ext://sys.stdout" + level: INFO + formatter: simple + syslog: + class: logging.handlers.SysLogHandler + address: "/dev/log" + level: INFO + formatter: simple + debug_file: + class: logging.FileHandler + filename: satosa-debug.log + encoding: utf8 + level: INFO + formatter: simple + error_file: + class: logging.FileHandler + filename: satosa-error.log + encoding: utf8 + level: ERROR + formatter: simple + info_file: + class: logging.handlers.RotatingFileHandler + filename: satosa-info.log + encoding: utf8 + maxBytes: 10485760 # 10MB + backupCount: 20 + level: INFO + formatter: simple + loggers: + satosa: + level: INFO + saml2: + level: INFO + oidcendpoint: + level: INFO + pyop: + level: INFO + oic: + level: INFO + root: + level: INFO + handlers: + - stdout diff --git a/roles/sram-midproxy/tasks/main.yml b/roles/sram-midproxy/tasks/main.yml new file mode 100644 index 000000000..2962a6f08 --- /dev/null +++ b/roles/sram-midproxy/tasks/main.yml @@ -0,0 +1,59 @@ +--- +- name: Create directory to keep configfile + ansible.builtin.file: + dest: "/opt/sram/midproxy" + state: directory + owner: 1000 + group: 1000 + mode: "0770" + +- name: Copy EB SP metadata + ansible.builtin.copy: + src: "{{ inventory_dir }}/files/midproxy/{{ midproxy.sp_metadata }}" + dest: "/opt/sram/midproxy/{{ midproxy.sp_metadata }}" + owner: 1000 + group: 1000 + mode: "0740" + +- name: Copy SATOSA conf files + ansible.builtin.copy: + src: "{{ item }}" + dest: "/opt/sram/midproxy/{{ item }}" + owner: 1000 + group: 1000 + with_items: + - internal_attributes.yaml + - proxy_conf.yaml + - plugins/ + +- name: Create the SATOSA container + community.docker.docker_container: + name: midproxy + image: satosa:{{ midproxy.satosa_version }} + pull: true + restart_policy: "always" + state: started + restart: true + networks: + - name: "loadbalancer" + env: + SATOSA_BASE: 'https://midproxy.{{ openconextaccess_base_domain }}' + SATOSA_STATE_ENCRYPTION_KEY: '{{ midproxy_state_encryption_key }}' + SATOSA_SP_METADATA: '{{ midproxy.sp_metadata }}' + SATOSA_ISSUER: '{{ midproxy.issuer }}' + SATOSA_CLIENT_ID: '{{ midproxy_client_id }}' + SATOSA_CLIENT_SECRET: '{{ midproxy_client_secret }}' + volumes: + - /opt/sram/midproxy:/etc/satosa + labels: + traefik.http.routers.midproxy.rule: "Host(`midproxy.{{ openconextaccess_base_domain }}`)" + traefik.http.routers.midproxy.tls: "true" + traefik.enable: "true" + # curl is not availavble in the minimized satosa image + # so this healthcheck won't work + # healthcheck: + # test: ["CMD", "curl", "--fail" , "http://localhost" ] + # interval: 10s + # timeout: 10s + # retries: 3 + # start_period: 10s diff --git a/roles/plsc/defaults/main.yml b/roles/sram-plsc/defaults/main.yml similarity index 100% rename from roles/plsc/defaults/main.yml rename to roles/sram-plsc/defaults/main.yml diff --git a/roles/plsc/handlers/main.yml b/roles/sram-plsc/handlers/main.yml similarity index 94% rename from roles/plsc/handlers/main.yml rename to roles/sram-plsc/handlers/main.yml index 9ce03e899..70cbb3672 100644 --- a/roles/plsc/handlers/main.yml +++ b/roles/sram-plsc/handlers/main.yml @@ -13,6 +13,6 @@ - name: Restart the plsc container community.docker.docker_container: - name: "plsc" + name: sram-plsc restart: true state: started diff --git a/roles/plsc/tasks/main.yml b/roles/sram-plsc/tasks/main.yml similarity index 96% rename from roles/plsc/tasks/main.yml rename to roles/sram-plsc/tasks/main.yml index 14e7b40b7..0d3900bd9 100644 --- a/roles/plsc/tasks/main.yml +++ b/roles/sram-plsc/tasks/main.yml @@ -15,7 +15,7 @@ - name: Create the plsc container community.docker.docker_container: - name: "plsc" + name: "sram-plsc" image: "{{ plsc.image }}" restart_policy: "always" state: started diff --git a/roles/plsc/templates/plsc.yml.j2 b/roles/sram-plsc/templates/plsc.yml.j2 similarity index 100% rename from roles/plsc/templates/plsc.yml.j2 rename to roles/sram-plsc/templates/plsc.yml.j2 diff --git a/roles/redis/vars/main.yml b/roles/sram-plsc/vars/main.yml similarity index 100% rename from roles/redis/vars/main.yml rename to roles/sram-plsc/vars/main.yml diff --git a/roles/redis/defaults/main.yml b/roles/sram-redis/defaults/main.yml similarity index 67% rename from roles/redis/defaults/main.yml rename to roles/sram-redis/defaults/main.yml index d4eb4b182..31d44935e 100644 --- a/roles/redis/defaults/main.yml +++ b/roles/sram-redis/defaults/main.yml @@ -2,8 +2,8 @@ redis: "{{ redis_defaults | combine(redis_overrides, recursive=true) }}" redis_defaults: image: "docker.io/library/redis:7" - conf_dir: "{{ current_release_appdir }}/redis" - data_dir: "{{ current_release_appdir }}/redis/data" + conf_dir: "{{ current_release_appdir }}/sram/redis" + data_dir: "{{ current_release_appdir }}/sram/redis/data" user: redis group: redis redis_user: default diff --git a/roles/redis/handlers/main.yml b/roles/sram-redis/handlers/main.yml similarity index 83% rename from roles/redis/handlers/main.yml rename to roles/sram-redis/handlers/main.yml index 5ed78e133..b08f0b62b 100644 --- a/roles/redis/handlers/main.yml +++ b/roles/sram-redis/handlers/main.yml @@ -1,6 +1,6 @@ --- - name: Restart redis container community.docker.docker_container: - name: redis + name: sram-redis state: started restart: true diff --git a/roles/redis/tasks/main.yml b/roles/sram-redis/tasks/main.yml similarity index 98% rename from roles/redis/tasks/main.yml rename to roles/sram-redis/tasks/main.yml index 65e7392ef..72789b08f 100644 --- a/roles/redis/tasks/main.yml +++ b/roles/sram-redis/tasks/main.yml @@ -47,7 +47,7 @@ - name: "Create redis container" community.docker.docker_container: - name: "redis" + name: "sram-redis" image: "{{ redis.image }}" restart_policy: "always" state: "started" diff --git a/roles/redis/templates/redis.conf.j2 b/roles/sram-redis/templates/redis.conf.j2 similarity index 100% rename from roles/redis/templates/redis.conf.j2 rename to roles/sram-redis/templates/redis.conf.j2 diff --git a/roles/sbs/vars/main.yml b/roles/sram-redis/vars/main.yml similarity index 100% rename from roles/sbs/vars/main.yml rename to roles/sram-redis/vars/main.yml diff --git a/roles/sbs/defaults/main.yml b/roles/sram-sbs/defaults/main.yml similarity index 99% rename from roles/sbs/defaults/main.yml rename to roles/sram-sbs/defaults/main.yml index aedcee959..8cbe8d109 100644 --- a/roles/sbs/defaults/main.yml +++ b/roles/sram-sbs/defaults/main.yml @@ -36,7 +36,7 @@ sbs_defaults: secret_key_suffix: suffix encryption_key: encryption_key - redis_host: redis + redis_host: sram-redis redis_port: 6379 redis_ssl: false redis_user: default diff --git a/roles/sbs/files/yarn.gpg b/roles/sram-sbs/files/yarn.gpg similarity index 100% rename from roles/sbs/files/yarn.gpg rename to roles/sram-sbs/files/yarn.gpg diff --git a/roles/sbs/handlers/main.yml b/roles/sram-sbs/handlers/main.yml similarity index 76% rename from roles/sbs/handlers/main.yml rename to roles/sram-sbs/handlers/main.yml index 2d7710d43..bc8be505b 100644 --- a/roles/sbs/handlers/main.yml +++ b/roles/sram-sbs/handlers/main.yml @@ -5,5 +5,5 @@ state: started restart: true loop: - - sbs-client - - sbs-server + - sram-sbs-client + - sram-sbs-server diff --git a/roles/sbs/tasks/main.yml b/roles/sram-sbs/tasks/main.yml similarity index 96% rename from roles/sbs/tasks/main.yml rename to roles/sram-sbs/tasks/main.yml index 560191f8a..6881736ec 100644 --- a/roles/sbs/tasks/main.yml +++ b/roles/sram-sbs/tasks/main.yml @@ -60,9 +60,9 @@ # Create dummy file in certs dir to pacify container pre-init script # https://github.com/SURFscz/SBS/pull/2312 - name: "Touch file in {{ sbs.cert_dir }}" - ansible.builtin.file: - path: "{{sbs.cert_dir}}/dummy" - state: file + ansible.builtin.copy: + content: "" + dest: "{{sbs.cert_dir}}/dummy" - name: "Create SBS config files" template: @@ -104,7 +104,7 @@ - name: "Run SBS migrations" throttle: 1 community.docker.docker_container: - name: "sbs-migration" + name: "sram-sbs-migration" image: "{{ sbs.server_image }}" pull: "never" state: "started" @@ -131,14 +131,14 @@ # will not save the output in result - name: "Remove migration container" community.docker.docker_container: - name: "sbs-migration" + name: "sram-sbs-migration" state: "absent" # TODO: fix this by only running this if "sbs_image is changed" changed_when: false - name: "Start sbs client container" community.docker.docker_container: - name: "sbs-client" + name: "sram-sbs-client" image: "{{ sbs.client_image }}" pull: "never" restart_policy: "always" @@ -155,7 +155,7 @@ - name: "Start SBS server container" community.docker.docker_container: - name: "sbs-server" + name: "sram-sbs-server" image: "{{ sbs.server_image }}" restart_policy: "always" state: "started" diff --git a/roles/sbs/templates/alembic.ini.j2 b/roles/sram-sbs/templates/alembic.ini.j2 similarity index 100% rename from roles/sbs/templates/alembic.ini.j2 rename to roles/sram-sbs/templates/alembic.ini.j2 diff --git a/roles/sbs/templates/config.yml.j2 b/roles/sram-sbs/templates/config.yml.j2 similarity index 100% rename from roles/sbs/templates/config.yml.j2 rename to roles/sram-sbs/templates/config.yml.j2 diff --git a/roles/sbs/templates/disclaimer.css.j2 b/roles/sram-sbs/templates/disclaimer.css.j2 similarity index 100% rename from roles/sbs/templates/disclaimer.css.j2 rename to roles/sram-sbs/templates/disclaimer.css.j2 diff --git a/roles/sbs/templates/saml_advanced_settings.json.j2 b/roles/sram-sbs/templates/saml_advanced_settings.json.j2 similarity index 100% rename from roles/sbs/templates/saml_advanced_settings.json.j2 rename to roles/sram-sbs/templates/saml_advanced_settings.json.j2 diff --git a/roles/sbs/templates/saml_settings.json.j2 b/roles/sram-sbs/templates/saml_settings.json.j2 similarity index 100% rename from roles/sbs/templates/saml_settings.json.j2 rename to roles/sram-sbs/templates/saml_settings.json.j2 diff --git a/roles/sbs/templates/sbs-apache.conf.j2 b/roles/sram-sbs/templates/sbs-apache.conf.j2 similarity index 88% rename from roles/sbs/templates/sbs-apache.conf.j2 rename to roles/sram-sbs/templates/sbs-apache.conf.j2 index 0743c2ddb..f0140a845 100644 --- a/roles/sbs/templates/sbs-apache.conf.j2 +++ b/roles/sram-sbs/templates/sbs-apache.conf.j2 @@ -12,10 +12,10 @@ RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f RewriteRule ^/(.*)$ /index.html [L] ProxyRequests off -ProxyPassMatch ^/(api|pam-weblogin|flasgger_static|swagger|health|config|info) http://sbs-server:8080/ -ProxyPassReverse / http://sbs-server:8080/ -ProxyPass /socket.io/ ws://sbs-server:8080/socket.io/ -ProxyPassReverse /socket.io/ ws://sbs-server:8080/socket.io/ +ProxyPassMatch ^/(api|pam-weblogin|flasgger_static|swagger|health|config|info) http://sram-sbs-server:8080/ +ProxyPassReverse / http://sram-sbs-server:8080/ +ProxyPass /socket.io/ ws://sram-sbs-server:8080/socket.io/ +ProxyPassReverse /socket.io/ ws://sram-sbs-server:8080/socket.io/ Header set Cache-Control: "public, max-age=31536000, immutable" diff --git a/roles/sbs/templates/sbs.service.j2 b/roles/sram-sbs/templates/sbs.service.j2 similarity index 100% rename from roles/sbs/templates/sbs.service.j2 rename to roles/sram-sbs/templates/sbs.service.j2 diff --git a/roles/sram-sbs/vars/main.yml b/roles/sram-sbs/vars/main.yml new file mode 100644 index 000000000..761942f7b --- /dev/null +++ b/roles/sram-sbs/vars/main.yml @@ -0,0 +1 @@ +current_release_appdir: /opt/openconext From 799d722349522e586acfd7d29b4deb2ceef987e5 Mon Sep 17 00:00:00 2001 From: Martin van Es Date: Fri, 27 Mar 2026 16:45:19 +0100 Subject: [PATCH 07/15] Fix haproxy_backend.cfg.j2 WIP WIP --- environments/template/group_vars/all.yml | 2 + .../haproxy/templates/haproxy_backend.cfg.j2 | 3 +- roles/mailpit/defaults/main.yml | 11 +- roles/mailpit/tasks/main.yml | 10 +- roles/sram-ldap/defaults/main.yml | 41 ---- roles/sram-ldap/vars/main.yml | 1 - roles/sram-metadata/vars/main.yml | 1 - roles/sram-plsc/defaults/main.yml | 15 -- roles/sram-plsc/templates/plsc.yml.j2 | 25 --- roles/sram-plsc/vars/main.yml | 1 - roles/sram-redis/defaults/main.yml | 11 -- roles/sram-redis/templates/redis.conf.j2 | 3 - roles/sram-redis/vars/main.yml | 1 - roles/sram-sbs/defaults/main.yml | 168 ---------------- roles/sram-sbs/templates/disclaimer.css.j2 | 6 - roles/sram-sbs/vars/main.yml | 1 - roles/sram_ldap/defaults/main.yml | 38 ++++ .../files/eduMember.ldif | 0 .../files/eduPerson.ldif | 0 .../files/groupOfMembers.ldif | 0 roles/{sram-ldap => sram_ldap}/files/ldap-add | 0 .../files/ldapPublicKey.ldif | 0 .../files/logrotate_slapd | 0 .../files/rsyslog_slapd.conf | 0 .../files/sczGroup.ldif | 0 .../files/sramPerson.ldif | 0 .../files/voPerson.ldif | 0 .../handlers/main.yml | 0 .../{sram-ldap => sram_ldap}/tasks/admins.yml | 38 ++-- roles/{sram-ldap => sram_ldap}/tasks/main.yml | 104 +++++----- .../templates/ldap-backup.sh.j2 | 0 .../templates/ldap.conf.j2 | 0 .../templates/slapd.service.j2 | 2 +- .../defaults/main.yml | 69 ++++--- .../files/01_idps.fd | 0 .../files/02_backend.fd | 0 .../files/03_frontend.fd | 0 .../files/surf.png | Bin .../files/surf.svg | 0 .../files/surf_bimi.svg | 0 .../files/surfconext.crt | 0 .../files/transform.xslt | 0 .../files/transform_proxy.xslt | 0 .../handlers/main.yml | 0 .../tasks/http.yml | 12 +- .../tasks/main.yml | 20 +- .../tasks/pyff.yml | 46 ++--- .../templates/index.html.j2 | 0 .../templates/pyff-metadata.service.j2 | 0 .../templates/pyff-metadata.timer.j2 | 0 .../templates/vhosts.conf.j2 | 0 .../defaults/main.yml | 0 .../files/internal_attributes.yaml | 0 .../files/plugins/attribute-maps/basic.py | 0 .../plugins/backends/openid_backend.yaml | 0 .../files/plugins/backends/saml2_backend.yaml | 0 .../plugins/frontends/ping_frontend.yaml | 0 .../plugins/frontends/saml2_frontend.yaml | 0 .../microservices/generate_attributes.yaml | 0 .../microservices/regex_attributes.yaml | 0 .../files/proxy_conf.yaml | 0 .../tasks/main.yml | 0 roles/sram_plsc/defaults/main.yml | 12 ++ .../handlers/main.yml | 0 roles/{sram-plsc => sram_plsc}/tasks/main.yml | 11 +- roles/sram_plsc/templates/plsc.yml.j2 | 25 +++ roles/sram_redis/defaults/main.yml | 9 + .../handlers/main.yml | 0 .../{sram-redis => sram_redis}/tasks/main.yml | 29 +-- roles/sram_redis/templates/redis.conf.j2 | 3 + roles/sram_sbs/defaults/main.yml | 166 ++++++++++++++++ roles/{sram-sbs => sram_sbs}/files/yarn.gpg | 0 .../{sram-sbs => sram_sbs}/handlers/main.yml | 0 roles/{sram-sbs => sram_sbs}/tasks/main.yml | 65 +++---- .../templates/alembic.ini.j2 | 2 +- .../templates/config.yml.j2 | 180 +++++++++--------- roles/sram_sbs/templates/disclaimer.css.j2 | 6 + .../templates/saml_advanced_settings.json.j2 | 0 .../templates/saml_settings.json.j2 | 0 .../templates/sbs-apache.conf.j2 | 2 +- .../templates/sbs.service.j2 | 0 81 files changed, 558 insertions(+), 581 deletions(-) delete mode 100644 roles/sram-ldap/defaults/main.yml delete mode 100644 roles/sram-ldap/vars/main.yml delete mode 100644 roles/sram-metadata/vars/main.yml delete mode 100644 roles/sram-plsc/defaults/main.yml delete mode 100644 roles/sram-plsc/templates/plsc.yml.j2 delete mode 100644 roles/sram-plsc/vars/main.yml delete mode 100644 roles/sram-redis/defaults/main.yml delete mode 100644 roles/sram-redis/templates/redis.conf.j2 delete mode 100644 roles/sram-redis/vars/main.yml delete mode 100644 roles/sram-sbs/defaults/main.yml delete mode 100644 roles/sram-sbs/templates/disclaimer.css.j2 delete mode 100644 roles/sram-sbs/vars/main.yml create mode 100644 roles/sram_ldap/defaults/main.yml rename roles/{sram-ldap => sram_ldap}/files/eduMember.ldif (100%) rename roles/{sram-ldap => sram_ldap}/files/eduPerson.ldif (100%) rename roles/{sram-ldap => sram_ldap}/files/groupOfMembers.ldif (100%) rename roles/{sram-ldap => sram_ldap}/files/ldap-add (100%) rename roles/{sram-ldap => sram_ldap}/files/ldapPublicKey.ldif (100%) rename roles/{sram-ldap => sram_ldap}/files/logrotate_slapd (100%) rename roles/{sram-ldap => sram_ldap}/files/rsyslog_slapd.conf (100%) rename roles/{sram-ldap => sram_ldap}/files/sczGroup.ldif (100%) rename roles/{sram-ldap => sram_ldap}/files/sramPerson.ldif (100%) rename roles/{sram-ldap => sram_ldap}/files/voPerson.ldif (100%) rename roles/{sram-ldap => sram_ldap}/handlers/main.yml (100%) rename roles/{sram-ldap => sram_ldap}/tasks/admins.yml (67%) rename roles/{sram-ldap => sram_ldap}/tasks/main.yml (78%) rename roles/{sram-ldap => sram_ldap}/templates/ldap-backup.sh.j2 (100%) rename roles/{sram-ldap => sram_ldap}/templates/ldap.conf.j2 (100%) rename roles/{sram-ldap => sram_ldap}/templates/slapd.service.j2 (92%) rename roles/{sram-metadata => sram_metadata}/defaults/main.yml (53%) rename roles/{sram-metadata => sram_metadata}/files/01_idps.fd (100%) rename roles/{sram-metadata => sram_metadata}/files/02_backend.fd (100%) rename roles/{sram-metadata => sram_metadata}/files/03_frontend.fd (100%) rename roles/{sram-metadata => sram_metadata}/files/surf.png (100%) rename roles/{sram-metadata => sram_metadata}/files/surf.svg (100%) rename roles/{sram-metadata => sram_metadata}/files/surf_bimi.svg (100%) rename roles/{sram-metadata => sram_metadata}/files/surfconext.crt (100%) rename roles/{sram-metadata => sram_metadata}/files/transform.xslt (100%) rename roles/{sram-metadata => sram_metadata}/files/transform_proxy.xslt (100%) rename roles/{sram-metadata => sram_metadata}/handlers/main.yml (100%) rename roles/{sram-metadata => sram_metadata}/tasks/http.yml (72%) rename roles/{sram-metadata => sram_metadata}/tasks/main.yml (60%) rename roles/{sram-metadata => sram_metadata}/tasks/pyff.yml (58%) rename roles/{sram-metadata => sram_metadata}/templates/index.html.j2 (100%) rename roles/{sram-metadata => sram_metadata}/templates/pyff-metadata.service.j2 (100%) rename roles/{sram-metadata => sram_metadata}/templates/pyff-metadata.timer.j2 (100%) rename roles/{sram-metadata => sram_metadata}/templates/vhosts.conf.j2 (100%) rename roles/{sram-midproxy => sram_midproxy}/defaults/main.yml (100%) rename roles/{sram-midproxy => sram_midproxy}/files/internal_attributes.yaml (100%) rename roles/{sram-midproxy => sram_midproxy}/files/plugins/attribute-maps/basic.py (100%) rename roles/{sram-midproxy => sram_midproxy}/files/plugins/backends/openid_backend.yaml (100%) rename roles/{sram-midproxy => sram_midproxy}/files/plugins/backends/saml2_backend.yaml (100%) rename roles/{sram-midproxy => sram_midproxy}/files/plugins/frontends/ping_frontend.yaml (100%) rename roles/{sram-midproxy => sram_midproxy}/files/plugins/frontends/saml2_frontend.yaml (100%) rename roles/{sram-midproxy => sram_midproxy}/files/plugins/microservices/generate_attributes.yaml (100%) rename roles/{sram-midproxy => sram_midproxy}/files/plugins/microservices/regex_attributes.yaml (100%) rename roles/{sram-midproxy => sram_midproxy}/files/proxy_conf.yaml (100%) rename roles/{sram-midproxy => sram_midproxy}/tasks/main.yml (100%) create mode 100644 roles/sram_plsc/defaults/main.yml rename roles/{sram-plsc => sram_plsc}/handlers/main.yml (100%) rename roles/{sram-plsc => sram_plsc}/tasks/main.yml (68%) create mode 100644 roles/sram_plsc/templates/plsc.yml.j2 create mode 100644 roles/sram_redis/defaults/main.yml rename roles/{sram-redis => sram_redis}/handlers/main.yml (100%) rename roles/{sram-redis => sram_redis}/tasks/main.yml (64%) create mode 100644 roles/sram_redis/templates/redis.conf.j2 create mode 100644 roles/sram_sbs/defaults/main.yml rename roles/{sram-sbs => sram_sbs}/files/yarn.gpg (100%) rename roles/{sram-sbs => sram_sbs}/handlers/main.yml (100%) rename roles/{sram-sbs => sram_sbs}/tasks/main.yml (74%) rename roles/{sram-sbs => sram_sbs}/templates/alembic.ini.j2 (96%) rename roles/{sram-sbs => sram_sbs}/templates/config.yml.j2 (58%) create mode 100644 roles/sram_sbs/templates/disclaimer.css.j2 rename roles/{sram-sbs => sram_sbs}/templates/saml_advanced_settings.json.j2 (100%) rename roles/{sram-sbs => sram_sbs}/templates/saml_settings.json.j2 (100%) rename roles/{sram-sbs => sram_sbs}/templates/sbs-apache.conf.j2 (98%) rename roles/{sram-sbs => sram_sbs}/templates/sbs.service.j2 (100%) diff --git a/environments/template/group_vars/all.yml b/environments/template/group_vars/all.yml index 1d8bd6f84..a314ae546 100644 --- a/environments/template/group_vars/all.yml +++ b/environments/template/group_vars/all.yml @@ -30,6 +30,8 @@ admin_email: "openconext-admin@example.edu" environment_shortname: "" environment_ribbon_colour: "" +current_release_appdir: /opt/openconext + httpd_csp: lenient: "default-src 'self'; object-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; form-action 'self'; frame-ancestors 'none'; base-uri 'none'" lenient_with_static_img: "default-src 'self'; object-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https://{{ static_vhost }} http://localhost:* data:; form-action 'self'; frame-ancestors 'none'; base-uri 'none'" diff --git a/roles/haproxy/templates/haproxy_backend.cfg.j2 b/roles/haproxy/templates/haproxy_backend.cfg.j2 index ea52ac5b0..8ef005da4 100644 --- a/roles/haproxy/templates/haproxy_backend.cfg.j2 +++ b/roles/haproxy/templates/haproxy_backend.cfg.j2 @@ -81,5 +81,4 @@ backend ldap_servers {% for server in haproxy_ldap_servers -%} server {{server.label}} {{server.ip}}:{{server.port}} ssl verify none check weight 10 {% if loop.index==1 %}on-marked-up shutdown-backup-sessions{% else %}backup{% endif %} {% endfor %} -{% endfor %} -{% endig %} +{% endif %} diff --git a/roles/mailpit/defaults/main.yml b/roles/mailpit/defaults/main.yml index 7647de9dc..f400a8d2e 100644 --- a/roles/mailpit/defaults/main.yml +++ b/roles/mailpit/defaults/main.yml @@ -1,8 +1,5 @@ --- -mailpit: "{{ mailpit_defaults | combine(mailpit_overrides, recursive=true) }}" - -mailpit_defaults: - image: "axllent/mailpit" - hostname: "mailpit.{{ base_domain }}" - user: "mailpit" - group: "mailpit" +mailpit_image: "axllent/mailpit" +mailpit_hostname: "mailpit.{{ base_domain }}" +mailpit_user: "mailpit" +mailpit_group: "mailpit" diff --git a/roles/mailpit/tasks/main.yml b/roles/mailpit/tasks/main.yml index 7fb32d8a7..5d89f4a7c 100644 --- a/roles/mailpit/tasks/main.yml +++ b/roles/mailpit/tasks/main.yml @@ -1,14 +1,14 @@ --- - name: "Create mailpit group" group: - name: "{{ mailpit.group }}" + name: "{{ mailpit_group }}" state: "present" register: "result" - name: "Create mailpit user" user: - name: "{{ mailpit.user }}" - group: "{{ mailpit.group }}" + name: "{{ mailpit_user }}" + group: "{{ mailpit_group }}" comment: "User to run Mailpit service" shell: "/bin/false" password: "!" @@ -23,7 +23,7 @@ - name: "Create mailpit container" docker_container: name: "mailpit" - image: "{{ mailpit.image }}" + image: "{{ mailpit_image }}" restart_policy: "always" state: "started" user: "{{ mailpit_user_uid }}" @@ -32,6 +32,6 @@ - name: "loadbalancer" labels: traefik.enable: "true" - traefik.http.routers.mailpit.rule: "Host(`{{ mailpit.hostname }}`)" + traefik.http.routers.mailpit.rule: "Host(`{{ mailpit_hostname }}`)" traefik.http.routers.mailpit.tls: "true" traefik.http.services.mailpit.loadbalancer.server.port: 8025 diff --git a/roles/sram-ldap/defaults/main.yml b/roles/sram-ldap/defaults/main.yml deleted file mode 100644 index e20f4553d..000000000 --- a/roles/sram-ldap/defaults/main.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- -ldap: "{{ ldap_defaults | combine(ldap_overrides, recursive=true) }}" - -ldap_defaults: - image: "ghcr.io/surfscz/sram-ldap:main" - conf_dir: "{{ current_release_appdir }}/sram/ldap" - ldif_dir: "{{ ldap_defaults.conf_dir }}/schema" - certs_dir: "{{ ldap_defaults.conf_dir }}/certs" - backup_dir: "{{ ldap_defaults.conf_dir }}/ldap" - data_dir: "{{ ldap_defaults.conf_dir}}/data" - uri: "ldap://localhost/" - - user: "openldap" - group: "openldap" - - # admin_group: "ldap_admin" - admins: - - name: Admin - uid: admin - pw_hash: - sshkey: "" - - loglevel: "stats stats2 filter" - - services_password: secret - monitor_password: secret - ldap_monitor_password: secret - - uri: "ldap://localhost/" - rid_prefix: "ldap://" - - base_domain: "{{ base_domain }}" - base_dn: >- - {{ ((ldap_defaults.base_domain.split('.')|length)*['dc=']) | - zip(ldap_defaults.base_domain.split('.')) | list | map('join', '') | list | join(',') }} - services_dn: - basedn: "dc=services,{{ ldap_defaults.base_dn }}" - o: "Services" - binddn: "cn=admin,{{ ldap_defaults.base_dn }}" - - hosts: {} diff --git a/roles/sram-ldap/vars/main.yml b/roles/sram-ldap/vars/main.yml deleted file mode 100644 index 761942f7b..000000000 --- a/roles/sram-ldap/vars/main.yml +++ /dev/null @@ -1 +0,0 @@ -current_release_appdir: /opt/openconext diff --git a/roles/sram-metadata/vars/main.yml b/roles/sram-metadata/vars/main.yml deleted file mode 100644 index 761942f7b..000000000 --- a/roles/sram-metadata/vars/main.yml +++ /dev/null @@ -1 +0,0 @@ -current_release_appdir: /opt/openconext diff --git a/roles/sram-plsc/defaults/main.yml b/roles/sram-plsc/defaults/main.yml deleted file mode 100644 index 2a3711b0c..000000000 --- a/roles/sram-plsc/defaults/main.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -plsc: "{{ plsc_defaults | combine(plsc_overrides, recursive=true) }}" - -plsc_defaults: - image: "ghcr.io/surfscz/sram-plsc:main" - conf_dir: "{{current_release_appdir}}/sram/plsc" - ansible_nolog: false - ldap_uri: "ldap://ldap:389/" - ldap_basedn: "dc=services,dc=vnet" - ldap_binddn: "cn=admin,dc=vnet" - ldap_password: "secret" - sbs_host: "http://sbs-server:8080" - sbs_user: "sysread" - sbs_password: "secret" - retry: 3 diff --git a/roles/sram-plsc/templates/plsc.yml.j2 b/roles/sram-plsc/templates/plsc.yml.j2 deleted file mode 100644 index a42c00807..000000000 --- a/roles/sram-plsc/templates/plsc.yml.j2 +++ /dev/null @@ -1,25 +0,0 @@ ---- -ldap: - src: - uri: "{{ plsc.ldap_uri }}" - basedn: "{{ plsc.ldap_basedn }}" - binddn: "{{ plsc.ldap_binddn }}" - passwd: "{{ plsc.ldap_password }}" - sizelimit: 500 - dst: - uri: "{{ plsc.ldap_uri }}" - basedn: "{{ plsc.ldap_basedn }}" - binddn: "{{ plsc.ldap_binddn }}" - passwd: "{{ plsc.ldap_password }}" - sizelimit: 500 -sbs: - src: - host: "{{ plsc.sbs_host }}" - user: "{{ plsc.sbs_user }}" - passwd: "{{ plsc.sbs_password }}" - verify_ssl: {{ false if env=='vm' else true }} - timeout: 60 - retry: {{ plsc.retry }} -pwd: "{CRYPT}!" -uid: 1000 -gid: 1000 diff --git a/roles/sram-plsc/vars/main.yml b/roles/sram-plsc/vars/main.yml deleted file mode 100644 index 761942f7b..000000000 --- a/roles/sram-plsc/vars/main.yml +++ /dev/null @@ -1 +0,0 @@ -current_release_appdir: /opt/openconext diff --git a/roles/sram-redis/defaults/main.yml b/roles/sram-redis/defaults/main.yml deleted file mode 100644 index 31d44935e..000000000 --- a/roles/sram-redis/defaults/main.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -redis: "{{ redis_defaults | combine(redis_overrides, recursive=true) }}" -redis_defaults: - image: "docker.io/library/redis:7" - conf_dir: "{{ current_release_appdir }}/sram/redis" - data_dir: "{{ current_release_appdir }}/sram/redis/data" - user: redis - group: redis - redis_user: default - redis_password: changethispassword - max_memory: 100mb diff --git a/roles/sram-redis/templates/redis.conf.j2 b/roles/sram-redis/templates/redis.conf.j2 deleted file mode 100644 index ba231dc58..000000000 --- a/roles/sram-redis/templates/redis.conf.j2 +++ /dev/null @@ -1,3 +0,0 @@ -user {{redis.redis_user}} on +@all ~* &* >{{redis.redis_password}} -maxmemory {{ redis.max_memory }} -maxmemory-policy allkeys-lru diff --git a/roles/sram-redis/vars/main.yml b/roles/sram-redis/vars/main.yml deleted file mode 100644 index 761942f7b..000000000 --- a/roles/sram-redis/vars/main.yml +++ /dev/null @@ -1 +0,0 @@ -current_release_appdir: /opt/openconext diff --git a/roles/sram-sbs/defaults/main.yml b/roles/sram-sbs/defaults/main.yml deleted file mode 100644 index 8cbe8d109..000000000 --- a/roles/sram-sbs/defaults/main.yml +++ /dev/null @@ -1,168 +0,0 @@ ---- -sbs: "{{ sbs_defaults | combine(sbs_overrides, recursive=true) }}" - -sbs_defaults: - base_domain: "test2.sram.surf.nl" - ansible_nolog: true - base_url: "https://{{ sbs_defaults.base_domain }}" - server_image: "ghcr.io/surfscz/sram-sbs-server:main" - client_image: "ghcr.io/surfscz/sram-sbs-client:main" - - openidc_timeout: 86400 - sram_conf_dir: "{{ current_release_appdir }}/sram" - - work_dir: "{{ sbs_defaults.sram_conf_dir }}/sbs" - git_dir: "{{ sbs_defaults.work_dir }}/sbs" - env_dir: "{{ sbs_defaults.work_dir }}/sbs-env" - conf_dir: "{{ sbs_defaults.work_dir }}/config" - log_dir: "{{ sbs_defaults.work_dir }}/log" - cert_dir: "{{ sbs_defaults.work_dir }}/cert" - apache_conf: "{{ sbs_defaults.work_dir }}/sbs_defaults.conf" - nginx_conf: "{{ sbs_defaults.work_dir }}/nginx.conf" - - db_name: "sbs" - db_user: "sbsrw" - # dbbackup_user: "sbs_backupper" - migration_user: "sbsmigrate" - - db_connection: "\ - mysql+mysqldb://%s:%s@{{ mariadb_host }}/{{ sbs_defaults.db_name }}\ - ?ssl=true&charset=utf8mb4" - db_connection_sbs: "{{ sbs_defaults.db_connection | format(sbs_defaults.db_user, mysql_passwords.sbs) }}" - db_connection_migration: "\ - {{ sbs_defaults.db_connection | format(sbs_defaults.migration_user, mysql_passwords.sbsmigrate) }}" - - db_secret: secret - secret_key_suffix: suffix - encryption_key: encryption_key - - redis_host: sram-redis - redis_port: 6379 - redis_ssl: false - redis_user: default - - mail_host: "host.docker.internal" - mail_port: 25 - - user: "sbs" - group: "sbs" - - session_lifetime: 1440 - secret_key_suffix: "" - - oidc_crypto_password: "CHANGEME" - uid_attribute: "sub" - - disclaimer_color: "#a29c13" - disclaimer_label: wsgi - - urn_namespace: "urn:example:sbs" - eppn_scope: "sbs.example.edu" - restricted_co_default_org: "example.org" - - mail_sender_name: "SURF" - mail_sender_email: "no-reply@localhost" - exceptions_mail: "root@localhost" - - support_email: "sram-support@localhost" - admin_email: "sram-beheer@localhost" - ticket_email: "sram-support@surf.nl" - eduteams_email: "eduteams@localhost" - - suppress_mails: False - - wiki_link: "https://www.example.org/wiki" - - cron_hour_of_day: 4 - seed_allowed: True - api_keys_enabled: True - feedback_enabled: True - audit_trail_notifications_enabled: True - send_exceptions: False - send_js_exceptions: False - second_factor_authentication_required: True - totp_token_name: "SRAM-example" - notifications_enabled: True - invitation_reminders_enabled: True - invitation_expirations_enabled: True - open_requests_enabled: True - scim_sweep: False - impersonation_allowed: True - admin_platform_backdoor_totp: True - past_dates_allowed: True - mock_scim_enabled: True - log_to_stdout: True - - delete_orphaned: True - suspension_inactive_days: 365 - suspension_reminder_days: 14 - suspension_notify_admin: False - - oidc_config_url: "http://localhost/.well-known/openid-configuration" - oidc_authz_endpoint: "http://localhost/OIDC/authorization" - oidc_token_endpoint: "http://localhost/OIDC/token" - oidc_userinfo_endpoint: "http://localhost/OIDC/userinfo" - oidc_jwks_endpoint: "http://localhost/OIDC/jwks.json" - oidc_redirect_uri: "https://{{sbs_defaults.base_domain}}/api/users/resume-session" - mfa_idp_allowed: false - eduteams_continue_endpoint: "https://localhost/continue" - eb_continue_endpoint: "https://engine.(.*)surfconext.nl(.*)" - oidc_jwt_audience: "https://localhost" - continue_eduteams_redirect_uri: "https://localhost/continue" - oidc_verify_peer: False - oidc_scopes: - - openid - - manage_base_enabled: False - manage_base_url: "https://manage.{{base_domain}}" - manage_sram_rp_entity_id: "sbs.{{sbs_defaults.base_domain}}" - manage_verify_peer: False - - idp_metadata_url: "https://metadata.surfconext.nl/signed/2023/edugain-downstream-idp.xml " - # backup_dir: "{{backup_base}}/sbs" - - swagger_enabled: true - - ssid_identity_providers: [] - surf_secure_id: - environment: "unknown.example.org" - sp_entity_id: "https://sbs.{{sbs_defaults.base_domain}}" - acs_url: "https://{{sbs_defaults.base_domain}}/api/users/acs" - sa_gw_environment: "sa-gw.unknown.example.org" - sa_idp_certificate: | - -----BEGIN CERTIFICATE----- - 12345 - -----END CERTIFICATE----- - priv: | - -----BEGIN RSA PRIVATE KEY----- - abcde - -----END RSA PRIVATE KEY----- - pub: | - -----BEGIN CERTIFICATE----- - 12345 - -----END CERTIFICATE----- - - ssid_authncontext: "\ - http://{{ sbs_defaults.surf_secure_id.environment }}/assurance/sfo-level2" - ssid_entityid: "\ - https://{{ sbs_defaults.surf_secure_id.sa_gw_environment }}/second-factor-only/metadata" - ssid_sso_endpoint: "\ - https://{{ sbs_defaults.surf_secure_id.sa_gw_environment }}/second-factor-only/single-sign-on" - - mfa_sso_minutes: 10 - mfa_fallback_enabled: true - - ldap_url: "ldap://ldap.example.com/dc=example,dc=com" - ldap_bind_account: "cn=admin,dc=entity_id,dc=services,dc=sram-tst,dc=surf,dc=nl" - - csp_style_hashes: - - 'sha256-0+ANsgYUJdh56RK8gGvTF2vnriYqvFHfWqtA8xXa+bA=' - - 'sha256-3SnfHQolDHbZMbDAPmhrZf1keHiXfj/KJyh2phhFAAY=' - - 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' - - 'sha256-Ng6y+QCkPChG4Q49SIfXB5ToIDcDhITtQNFkDBPpCTw=' - - 'sha256-orBPipbqpMvkNi+Z+m6qEn0XS6ymmAQE6+FwCNs1FbQ=' - - 'sha256-vFt3L2qLqpJmRpcXGbYr2UVSmgSp9VCUzz2lnqWIATw=' - - 'sha256-SU3XCwbQ/8qgzoGOWCYdkwIr3xRrl5rsvdFcpw8NSiE=' # on /new-service-request - - 'sha256-WTC9gHKjIpzl5ub1eg/YrRy/k+jlzeyRojah9dxAApc=' # on /new-service-request - - engine_block_api_token: secret diff --git a/roles/sram-sbs/templates/disclaimer.css.j2 b/roles/sram-sbs/templates/disclaimer.css.j2 deleted file mode 100644 index 7922f5e5b..000000000 --- a/roles/sram-sbs/templates/disclaimer.css.j2 +++ /dev/null @@ -1,6 +0,0 @@ -{% if env!="prd" -%} -body::after { - background: {{ sbs.disclaimer_color }}; - content: "{{ sbs.disclaimer_label }}"; -} -{% endif %} diff --git a/roles/sram-sbs/vars/main.yml b/roles/sram-sbs/vars/main.yml deleted file mode 100644 index 761942f7b..000000000 --- a/roles/sram-sbs/vars/main.yml +++ /dev/null @@ -1 +0,0 @@ -current_release_appdir: /opt/openconext diff --git a/roles/sram_ldap/defaults/main.yml b/roles/sram_ldap/defaults/main.yml new file mode 100644 index 000000000..cb2bc981a --- /dev/null +++ b/roles/sram_ldap/defaults/main.yml @@ -0,0 +1,38 @@ +--- +ldap_image: "ghcr.io/surfscz/sram-ldap:main" +ldap_conf_dir: "{{ current_release_appdir }}/sram/ldap" +ldap_ldif_dir: "{{ ldap_conf_dir }}/schema" +ldap_certs_dir: "{{ ldap_conf_dir }}/certs" +ldap_backup_dir: "{{ ldap_conf_dir }}/ldap" +ldap_data_dir: "{{ ldap_conf_dir}}/data" +ldap_uri: "ldap://localhost/" + +ldap_user: "openldap" +ldap_group: "openldap" + +# admin_group: "ldap_admin" +ldap_admins: + - name: Admin + uid: admin + pw_hash: + sshkey: "" + +ldap_loglevel: "stats stats2 filter" + +ldap_services_password: secret +ldap_monitor_password: secret +ldap_ldap_monitor_password: secret + +ldap_uri: "ldap://localhost/" +ldap_rid_prefix: "ldap://" + +ldap_base_domain: "{{ base_domain }}" +ldap_base_dn: >- + {{ ((ldap_base_domain.split('.')|length)*['dc=']) | + zip(ldap_base_domain.split('.')) | list | map('join', '') | list | join(',') }} +ldap_services_dn: + basedn: "dc=services,{{ ldap_base_dn }}" + o: "Services" + binddn: "cn=admin,{{ ldap_base_dn }}" + +ldap_hosts: {} diff --git a/roles/sram-ldap/files/eduMember.ldif b/roles/sram_ldap/files/eduMember.ldif similarity index 100% rename from roles/sram-ldap/files/eduMember.ldif rename to roles/sram_ldap/files/eduMember.ldif diff --git a/roles/sram-ldap/files/eduPerson.ldif b/roles/sram_ldap/files/eduPerson.ldif similarity index 100% rename from roles/sram-ldap/files/eduPerson.ldif rename to roles/sram_ldap/files/eduPerson.ldif diff --git a/roles/sram-ldap/files/groupOfMembers.ldif b/roles/sram_ldap/files/groupOfMembers.ldif similarity index 100% rename from roles/sram-ldap/files/groupOfMembers.ldif rename to roles/sram_ldap/files/groupOfMembers.ldif diff --git a/roles/sram-ldap/files/ldap-add b/roles/sram_ldap/files/ldap-add similarity index 100% rename from roles/sram-ldap/files/ldap-add rename to roles/sram_ldap/files/ldap-add diff --git a/roles/sram-ldap/files/ldapPublicKey.ldif b/roles/sram_ldap/files/ldapPublicKey.ldif similarity index 100% rename from roles/sram-ldap/files/ldapPublicKey.ldif rename to roles/sram_ldap/files/ldapPublicKey.ldif diff --git a/roles/sram-ldap/files/logrotate_slapd b/roles/sram_ldap/files/logrotate_slapd similarity index 100% rename from roles/sram-ldap/files/logrotate_slapd rename to roles/sram_ldap/files/logrotate_slapd diff --git a/roles/sram-ldap/files/rsyslog_slapd.conf b/roles/sram_ldap/files/rsyslog_slapd.conf similarity index 100% rename from roles/sram-ldap/files/rsyslog_slapd.conf rename to roles/sram_ldap/files/rsyslog_slapd.conf diff --git a/roles/sram-ldap/files/sczGroup.ldif b/roles/sram_ldap/files/sczGroup.ldif similarity index 100% rename from roles/sram-ldap/files/sczGroup.ldif rename to roles/sram_ldap/files/sczGroup.ldif diff --git a/roles/sram-ldap/files/sramPerson.ldif b/roles/sram_ldap/files/sramPerson.ldif similarity index 100% rename from roles/sram-ldap/files/sramPerson.ldif rename to roles/sram_ldap/files/sramPerson.ldif diff --git a/roles/sram-ldap/files/voPerson.ldif b/roles/sram_ldap/files/voPerson.ldif similarity index 100% rename from roles/sram-ldap/files/voPerson.ldif rename to roles/sram_ldap/files/voPerson.ldif diff --git a/roles/sram-ldap/handlers/main.yml b/roles/sram_ldap/handlers/main.yml similarity index 100% rename from roles/sram-ldap/handlers/main.yml rename to roles/sram_ldap/handlers/main.yml diff --git a/roles/sram-ldap/tasks/admins.yml b/roles/sram_ldap/tasks/admins.yml similarity index 67% rename from roles/sram-ldap/tasks/admins.yml rename to roles/sram_ldap/tasks/admins.yml index e00115c04..dfba23d6e 100644 --- a/roles/sram-ldap/tasks/admins.yml +++ b/roles/sram_ldap/tasks/admins.yml @@ -1,10 +1,10 @@ --- # - name: Initialize DIT admin # community.general.ldap_entry: -# dn: "{{ services_ldap.binddn }}" +# dn: "{{ ldap_services_dn.binddn }}" # objectClass: organizationalRole # attributes: -# cn: "{{ services_ldap.binddn | regex_replace('^cn=([^,]+).*', '\\1') }}" +# cn: "{{ ldap_services_dn.binddn | regex_replace('^cn=([^,]+).*', '\\1') }}" # determine which users need to be admin # check for each role of each user if it leads to membership of group {{ldap_admin_group}} @@ -16,19 +16,19 @@ - name: determine ldap admins set_fact: - ldap_admins: "{{ ldap.admins }}" + ldap_admins: "{{ ldap_admins }}" # Find existing ldap admins - name: Initialize admins (I) community.general.ldap_search: - dn: "{{ ldap.services_dn.basedn }}" + dn: "{{ ldap_services_dn.basedn }}" scope: "onelevel" filter: "(objectClass=organizationalRole)" attrs: - "cn" - bind_dn: "{{ ldap.services_dn.binddn }}" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ldap.uri }}" + bind_dn: "{{ ldap_services_dn.binddn }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ldap_uri }}" register: "existing_ldap_admins_result" # ansible sucks like this: we need to extract the results from the result @@ -44,11 +44,11 @@ # Remove LDAP non-admins - name: Initialize admins (II) community.general.ldap_entry: - dn: "cn={{ item.cn }},{{ services_ldap.basedn }}" + dn: "cn={{ item.cn }},{{ ldap_services_dn.basedn }}" state: absent - bind_dn: "{{ ldap.services_dn.binddn }}" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ldap.uri }}" + bind_dn: "{{ ldap_services_dn.binddn }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ldap_uri }}" when: > item.cn not in ldap_admins | map(attribute='uid') and item.cn != 'admin' @@ -57,26 +57,26 @@ # Insert LDAP admins - name: Initialize admins (III) community.general.ldap_entry: - dn: "cn={{ item.uid }},{{ ldap.services_dn.basedn }}" + dn: "cn={{ item.uid }},{{ ldap_services_dn.basedn }}" objectClass: - simpleSecurityObject - organizationalRole attributes: description: An LDAP administrator userPassword: "{{ item.pw_hash }}" - bind_dn: "{{ ldap.services_dn.binddn }}" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ldap.uri }}" + bind_dn: "{{ ldap_services_dn.binddn }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ldap_uri }}" loop: "{{ ldap_admins }}" # Make sure passwords are updated for existing admins - name: Initialize admins (IV) community.general.ldap_attrs: - dn: "cn={{ item.uid }},{{ ldap.services_dn.basedn }}" + dn: "cn={{ item.uid }},{{ ldap_services_dn.basedn }}" attributes: userPassword: "{{ item.pw_hash }}" - bind_dn: "{{ ldap.services_dn.binddn }}" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ldap.uri }}" + bind_dn: "{{ ldap_services_dn.binddn }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ldap_uri }}" loop: "{{ ldap_admins }}" diff --git a/roles/sram-ldap/tasks/main.yml b/roles/sram_ldap/tasks/main.yml similarity index 78% rename from roles/sram-ldap/tasks/main.yml rename to roles/sram_ldap/tasks/main.yml index 97977e06a..a4877722d 100644 --- a/roles/sram-ldap/tasks/main.yml +++ b/roles/sram_ldap/tasks/main.yml @@ -11,19 +11,19 @@ file: path: "{{ item.path }}" state: "directory" - # owner: "{{ ldap.user }}" - # group: "{{ ldap.group }}" + # owner: "{{ ldap_user }}" + # group: "{{ ldap_group }}" mode: "{{ item.mode }}" with_items: - - { path: "{{ldap.ldif_dir}}", mode: "0755" } - - { path: "{{ldap.certs_dir}}", mode: "0755" } - - { path: "{{ldap.data_dir}}", mode: "0777" } + - { path: "{{ldap_ldif_dir}}", mode: "0755" } + - { path: "{{ldap_certs_dir}}", mode: "0755" } + - { path: "{{ldap_data_dir}}", mode: "0777" } notify: Restart the ldap container - name: Copy schemas copy: src: "{{ item }}" - dest: "{{ ldap.ldif_dir }}/{{ item }}" + dest: "{{ ldap_ldif_dir }}/{{ item }}" mode: "0644" with_items: - sczGroup.ldif @@ -38,7 +38,7 @@ - name: Copying ldap-add script copy: src: "{{ item }}" - dest: "{{ ldap.conf_dir }}/{{ item }}" + dest: "{{ ldap_conf_dir }}/{{ item }}" mode: "0755" with_items: - ldap-add @@ -49,7 +49,7 @@ # copy: # src: "/etc/ssl/certs/sram-https.pem" # was installed here by update-ca-certificates # remote_src: true -# dest: "{{ldap.certs_dir}}/frontend.crt" +# dest: "{{ldap_certs_dir}}/frontend.crt" # mode: "0644" # when: "is_dev" # notify: Restart the ldap container @@ -57,19 +57,19 @@ - name: Setup ldap hosts vars: host: - key: "%s.{{ ldap.base_domain }}" + key: "%s.{{ ldap_base_domain }}" value: "%s" etc_hosts: {} set_fact: etc_hosts: >- {{ etc_hosts | combine({ host.key | format(item.key): host.value | format(item.value) }) }} - with_dict: "{{ ldap.hosts }}" + with_dict: "{{ ldap_hosts }}" - name: Create the ldap container community.docker.docker_container: name: "sram-ldap" - image: "{{ ldap.image }}" + image: "{{ ldap_image }}" restart_policy: "always" state: started pull: true @@ -77,12 +77,12 @@ - 0.0.0.0:389:389 env: LDAP_ORGANISATION: "{{ env }}" - LDAP_DOMAIN: "{{ ldap.base_domain }}" - LDAP_ROOTPASS: "{{ ldap.services_password }}" + LDAP_DOMAIN: "{{ ldap_base_domain }}" + LDAP_ROOTPASS: "{{ ldap_services_password }}" etc_hosts: "{{ etc_hosts }}" volumes: # For now the target side /opt/ldap is hard-coded - - "{{ ldap.conf_dir }}:/opt/ldap" + - "{{ ldap_conf_dir }}:/opt/ldap" networks: - name: "loadbalancer" labels: @@ -133,8 +133,8 @@ olcDbIndex: "{{item}}" state: "present" bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ ldap.uri }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ ldap_uri }}" with_items: - "entryUUID eq" - "o eq" @@ -148,21 +148,21 @@ attributes: olcSizeLimit: "unlimited" bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ ldap.uri }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ ldap_uri }}" - name: Set config community.general.ldap_attrs: dn: "cn=config" state: "present" attributes: - olcServerID: "{{ ldap.server_id }}" + olcServerID: "{{ ldap_server_id }}" olcSizeLimit: "unlimited" - olcLogLevel: "{{ ldap.loglevel }}" + olcLogLevel: "{{ ldap_loglevel }}" olcAttributeOptions: "time-" bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ ldap.uri }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ ldap_uri }}" # # cert is used for communication between ldap for sync # # is generated in roles/certificates/tasks/main.yml @@ -173,8 +173,8 @@ # attributes: # olcTLSCACertificateFile: "/opt/ldap/certs/frontend.crt" # bind_dn: "cn=admin,cn=config" -# bind_pw: "{{ ldap.services_password }}" -# server_uri: "{{ ldap.uri }}" +# bind_pw: "{{ ldap_services_password }}" +# server_uri: "{{ ldap_uri }}" - name: Setup Modules community.general.ldap_attrs: @@ -184,8 +184,8 @@ - syncprov - dynlist.so bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ ldap.uri }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ ldap_uri }}" - name: Setup Dynlist community.general.ldap_entry: @@ -196,8 +196,8 @@ attributes: olcDlAttrSet: "voPerson labeledURI member+memberOf@groupOfMembers" bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ ldap.uri }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ ldap_uri }}" - name: Setup Syncprov community.general.ldap_entry: @@ -209,8 +209,8 @@ olcSpCheckpoint: 100 10 olcSpSessionLog: 100 bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ ldap.uri }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ ldap_uri }}" # Leave this here in case we do want to build our own # root database from scratch instead of relying on the @@ -220,8 +220,8 @@ # community.general.ldap_attrs: # dn: olcDatabase={1}mdb,cn=config # attributes: -# olcSuffix: "{{ services_ldap.basedn }}" -# olcRootDN: "{{ services_ldap.binddn }}" +# olcSuffix: "{{ ldap_services_dn.basedn }}" +# olcRootDN: "{{ ldap_services_dn.binddn }}" # olcRootPW: "{{ '%s' | format(services_ldap_password) | slapd_hash }}" # state: exact # @@ -235,11 +235,11 @@ # {% if environment_name=="vm" %} # by dn.exact=gidNumber=1000+uidNumber=1000,cn=peercred,cn=external,cn=auth manage # {% endif %} -# by dn.exact="{{ services_ldap.binddn }}" manage +# by dn.exact="{{ ldap_services_dn.binddn }}" manage # by * break # state: exact # bind_dn: "cn=admin,cn=config" -# bind_pw: "{{ services_ldap_password }}" +# bind_pw: "{{ ldap_services_password }}" # server_uri: "{{ ldap_uri }}" # # @@ -261,15 +261,15 @@ attributes: olcAccess: - >- - to dn.regex="(([^,]+),{{ ldap.services_dn.basedn }})$" - by dn.exact="{{ ldap.services_dn.binddn }}" write + to dn.regex="(([^,]+),{{ ldap_services_dn.basedn }})$" + by dn.exact="{{ ldap_services_dn.binddn }}" write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth write by dn.exact,expand="cn=admin,$1" read by * break - >- to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage - by dn.regex="cn=[^,]+,{{ ldap.services_dn.basedn }}" read + by dn.regex="cn=[^,]+,{{ ldap_services_dn.basedn }}" read {% if env=="vm" %} by dn.exact=gidNumber=1000+uidNumber=1000,cn=peercred,cn=external,cn=auth manage {% endif %} @@ -282,8 +282,8 @@ state: exact ordered: true bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ ldap.uri }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ ldap_uri }}" # ldap_rids: # 101: ldaps://ldap1.scz-vm.net/ @@ -294,14 +294,14 @@ start: 101 rid: key: "%d" - value: "{{ ldap.rid_prefix }}%s.{{ ldap.base_domain }}/" + value: "{{ ldap_rid_prefix }}%s.{{ ldap_base_domain }}/" ldap_rids: {} set_fact: ldap_rids: >- {{ ldap_rids | combine({ rid.key | format(start|int): rid.value | format(item.key) }) }} start: "{{ start|int + 1 }}" - with_dict: "{{ ldap.hosts | dict2items | sort(attribute='key') }}" + with_dict: "{{ ldap_hosts | dict2items | sort(attribute='key') }}" # Voor toekomstige Claude gebruikers: onderstaande construct levert aan het eind # een string representatie van de dict op, die niet meer gebruikt kan worden @@ -321,11 +321,11 @@ rid: >- rid={} provider="{}" - searchbase="{{ ldap.services_dn.basedn }}" + searchbase="{{ ldap_services_dn.basedn }}" type=refreshAndPersist bindmethod=simple - binddn="{{ ldap.services_dn.binddn }}" - credentials={{ ldap.services_password }} + binddn="{{ ldap_services_dn.binddn }}" + credentials={{ ldap_services_password }} retry="30 +" timeout=30 network-timeout=5 @@ -341,24 +341,24 @@ olcSyncrepl: "{{ rids }}" olcMultiProvider: "TRUE" bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ ldap.uri }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ ldap_uri }}" # We now have Syncrepl in place, so only write to primary - name: Initialize DIT community.general.ldap_entry: - dn: "{{ ldap.services_dn.basedn }}" + dn: "{{ ldap_services_dn.basedn }}" state: "present" objectClass: - "top" - "dcObject" - "organization" attributes: - dc: "{{ ldap.services_dn.basedn | regex_replace('^dc=([^,]+).*', '\\1') }}" - o: "{{ ldap.services_dn.o }}" - bind_dn: "{{ ldap.services_dn.binddn }}" - bind_pw: "{{ ldap.services_password }}" - server_uri: "{{ ldap.uri }}" + dc: "{{ ldap_services_dn.basedn | regex_replace('^dc=([^,]+).*', '\\1') }}" + o: "{{ ldap_services_dn.o }}" + bind_dn: "{{ ldap_services_dn.binddn }}" + bind_pw: "{{ ldap_services_password }}" + server_uri: "{{ ldap_uri }}" when: > inventory_hostname in groups['ldap_primary'] diff --git a/roles/sram-ldap/templates/ldap-backup.sh.j2 b/roles/sram_ldap/templates/ldap-backup.sh.j2 similarity index 100% rename from roles/sram-ldap/templates/ldap-backup.sh.j2 rename to roles/sram_ldap/templates/ldap-backup.sh.j2 diff --git a/roles/sram-ldap/templates/ldap.conf.j2 b/roles/sram_ldap/templates/ldap.conf.j2 similarity index 100% rename from roles/sram-ldap/templates/ldap.conf.j2 rename to roles/sram_ldap/templates/ldap.conf.j2 diff --git a/roles/sram-ldap/templates/slapd.service.j2 b/roles/sram_ldap/templates/slapd.service.j2 similarity index 92% rename from roles/sram-ldap/templates/slapd.service.j2 rename to roles/sram_ldap/templates/slapd.service.j2 index 7e0f79397..299b3af0c 100644 --- a/roles/sram-ldap/templates/slapd.service.j2 +++ b/roles/sram_ldap/templates/slapd.service.j2 @@ -6,7 +6,7 @@ Type = forking User = root SupplementaryGroups = ssl-cert ExecStartPre=-/bin/mkdir -p /var/run/slapd -ExecStartPre=-/bin/chown openldap. /var/run/slapd +ExecStartPre=-/bin/chown openldap: /var/run/slapd ExecStart = /usr/sbin/slapd -F /etc/ldap/slapd.d -u openldap -g openldap -h 'ldapi:/// ldap://localhost/ ldaps://{{inventory_hostname}}/' Restart = always RestartSec = 30 diff --git a/roles/sram-metadata/defaults/main.yml b/roles/sram_metadata/defaults/main.yml similarity index 53% rename from roles/sram-metadata/defaults/main.yml rename to roles/sram_metadata/defaults/main.yml index 5355989bc..7982c7f9d 100644 --- a/roles/sram-metadata/defaults/main.yml +++ b/roles/sram_metadata/defaults/main.yml @@ -1,16 +1,13 @@ --- -metadata: "{{ metadata_defaults | combine(metadata_overrides, recursive=true) }}" +sram_metadata_image_server: "ghcr.io/openconext/openconext-basecontainers/apache2:latest" +sram_metadata_image_pyff: "ghcr.io/surfscz/sram-pyff:main" +sram_metadata_hostname: "meta.{{ base_domain }}" +sram_metadata_basedir: "{{current_release_appdir}}/sram/metadata" -metadata_defaults: - image_server: "ghcr.io/openconext/openconext-basecontainers/apache2:latest" - image_pyff: "ghcr.io/surfscz/sram-pyff:main" - hostname: "meta.{{ base_domain }}" - basedir: "{{current_release_appdir}}/sram/metadata" +# server_name: "metadata-server" - # server_name: "metadata-server" - - user: "sram-metadata" - group: "sram-metadata" +sram_metadata_user: "sram-metadata" +sram_metadata_group: "sram-metadata" # idps_source: "https://metadata.surfconext.nl/idps-metadata.xml" # idps_cert: | @@ -39,33 +36,33 @@ metadata_defaults: # 9bFiAimF5LLk/LnMfplK9w0vvxWVcdQkDgVPYvEGNtttj0QC7/jM4ZeihGb6Oyzy # DZA6aeg73/ygOATQ13A= # -----END CERTIFICATE----- - idps_filters: [] +sram_metadata_idps_filters: [] - idps_files: - - name: "dummy-idp" - metadata: | - - - - - - - SRAM VM Dummy IdP - SRAM VM Dummy IdP - https://test-idp.sram.example.org/ - - - Administrator - mailto:sinterklaas@example.nl - - +sram_metadata_idps_files: +- name: "dummy-idp" + metadata: | + + + + + + + SRAM VM Dummy IdP + SRAM VM Dummy IdP + https://test-idp.sram.example.org/ + + + Administrator + mailto:sinterklaas@example.nl + + # idps_xrd: "{{metadata_defaults.basedir}}/certs/surfconext.xrd" # idps_source_dir: "/opt/metadata-src" diff --git a/roles/sram-metadata/files/01_idps.fd b/roles/sram_metadata/files/01_idps.fd similarity index 100% rename from roles/sram-metadata/files/01_idps.fd rename to roles/sram_metadata/files/01_idps.fd diff --git a/roles/sram-metadata/files/02_backend.fd b/roles/sram_metadata/files/02_backend.fd similarity index 100% rename from roles/sram-metadata/files/02_backend.fd rename to roles/sram_metadata/files/02_backend.fd diff --git a/roles/sram-metadata/files/03_frontend.fd b/roles/sram_metadata/files/03_frontend.fd similarity index 100% rename from roles/sram-metadata/files/03_frontend.fd rename to roles/sram_metadata/files/03_frontend.fd diff --git a/roles/sram-metadata/files/surf.png b/roles/sram_metadata/files/surf.png similarity index 100% rename from roles/sram-metadata/files/surf.png rename to roles/sram_metadata/files/surf.png diff --git a/roles/sram-metadata/files/surf.svg b/roles/sram_metadata/files/surf.svg similarity index 100% rename from roles/sram-metadata/files/surf.svg rename to roles/sram_metadata/files/surf.svg diff --git a/roles/sram-metadata/files/surf_bimi.svg b/roles/sram_metadata/files/surf_bimi.svg similarity index 100% rename from roles/sram-metadata/files/surf_bimi.svg rename to roles/sram_metadata/files/surf_bimi.svg diff --git a/roles/sram-metadata/files/surfconext.crt b/roles/sram_metadata/files/surfconext.crt similarity index 100% rename from roles/sram-metadata/files/surfconext.crt rename to roles/sram_metadata/files/surfconext.crt diff --git a/roles/sram-metadata/files/transform.xslt b/roles/sram_metadata/files/transform.xslt similarity index 100% rename from roles/sram-metadata/files/transform.xslt rename to roles/sram_metadata/files/transform.xslt diff --git a/roles/sram-metadata/files/transform_proxy.xslt b/roles/sram_metadata/files/transform_proxy.xslt similarity index 100% rename from roles/sram-metadata/files/transform_proxy.xslt rename to roles/sram_metadata/files/transform_proxy.xslt diff --git a/roles/sram-metadata/handlers/main.yml b/roles/sram_metadata/handlers/main.yml similarity index 100% rename from roles/sram-metadata/handlers/main.yml rename to roles/sram_metadata/handlers/main.yml diff --git a/roles/sram-metadata/tasks/http.yml b/roles/sram_metadata/tasks/http.yml similarity index 72% rename from roles/sram-metadata/tasks/http.yml rename to roles/sram_metadata/tasks/http.yml index befcc1d18..561201096 100644 --- a/roles/sram-metadata/tasks/http.yml +++ b/roles/sram_metadata/tasks/http.yml @@ -2,19 +2,19 @@ - name: "Install index page" template: src: "index.html.j2" - dest: "{{metadata.basedir}}/web/index.html" + dest: "{{sram_metadata_basedir}}/web/index.html" mode: "0644" - name: "Install legacy link" file: src: "." - dest: "{{metadata.basedir}}/web/metadata" + dest: "{{sram_metadata_basedir}}/web/metadata" state: "link" - name: "Install logos" copy: src: "{{item}}" - dest: "{{metadata.basedir}}/web" + dest: "{{sram_metadata_basedir}}/web" mode: "0644" with_items: - "surf.svg" @@ -24,19 +24,19 @@ - name: "Create the metadata-server container" community.docker.docker_container: name: "sram-metadata-server" - image: "{{ metadata.image_server }}" + image: "{{ sram_metadata_image_server }}" restart_policy: "always" state: "started" pull: true mounts: - - source: "{{metadata.basedir}}/web" + - source: "{{sram_metadata_basedir}}/web" target: "/var/www/html" type: "bind" read_only: true networks: - name: "loadbalancer" labels: - traefik.http.routers.metadata.rule: "Host(`{{ metadata.hostname }}`)" + traefik.http.routers.metadata.rule: "Host(`{{ sram_metadata_hostname }}`)" traefik.http.routers.metadata.tls: "true" traefik.enable: "true" healthcheck: diff --git a/roles/sram-metadata/tasks/main.yml b/roles/sram_metadata/tasks/main.yml similarity index 60% rename from roles/sram-metadata/tasks/main.yml rename to roles/sram_metadata/tasks/main.yml index d6ac55f29..89c0ce9e9 100644 --- a/roles/sram-metadata/tasks/main.yml +++ b/roles/sram_metadata/tasks/main.yml @@ -1,7 +1,7 @@ --- - name: "Create metadata group" group: - name: "{{ metadata.group }}" + name: "{{ sram_metadata_group }}" state: "present" register: "result" @@ -11,12 +11,12 @@ - name: "Create metadata user" user: - name: "{{ metadata.user }}" - group: "{{ metadata.group }}" + name: "{{ sram_metadata_user }}" + group: "{{ sram_metadata_group }}" comment: "User to run metadata service" shell: "/bin/false" password: "!" - home: "{{ metadata.basedir }}" + home: "{{ sram_metadata_basedir }}" create_home: false state: "present" register: "result" @@ -32,13 +32,13 @@ state: "directory" mode: "{{ item.mode }}" owner: "root" - group: "{{ metadata.group }}" + group: "{{ sram_metadata_group }}" with_items: - - { dir: "{{metadata.basedir}}/web", mode: "0775" } - - { dir: "{{metadata.basedir}}/feeds", mode: "0755" } - - { dir: "{{metadata.basedir}}/src", mode: "0755" } - - { dir: "{{metadata.basedir}}/certs", mode: "0755" } - - { dir: "{{metadata.basedir}}/xslt", mode: "0755" } + - { dir: "{{sram_metadata_basedir}}/web", mode: "0775" } + - { dir: "{{sram_metadata_basedir}}/feeds", mode: "0755" } + - { dir: "{{sram_metadata_basedir}}/src", mode: "0755" } + - { dir: "{{sram_metadata_basedir}}/certs", mode: "0755" } + - { dir: "{{sram_metadata_basedir}}/xslt", mode: "0755" } notify: "Restart the pyFF container" diff --git a/roles/sram-metadata/tasks/pyff.yml b/roles/sram_metadata/tasks/pyff.yml similarity index 58% rename from roles/sram-metadata/tasks/pyff.yml rename to roles/sram_metadata/tasks/pyff.yml index 6c66a5696..4e9b960d3 100644 --- a/roles/sram-metadata/tasks/pyff.yml +++ b/roles/sram_metadata/tasks/pyff.yml @@ -2,33 +2,33 @@ - name: "create self-signed Metadata Signing SSL certs" shell: cmd: ' - openssl genrsa -out "{{ metadata.basedir }}/certs/signing.key" 2048; + openssl genrsa -out "{{ sram_metadata_basedir }}/certs/signing.key" 2048; openssl req -new -nodes -x509 -subj "/C=NL/CN=signing" - -days 3650 -key "{{ metadata.basedir }}/certs/signing.key" - -out "{{ metadata.basedir }}/certs/signing.crt" -extensions v3_ca; - chown {{metadata.user}}:{{metadata.group}} {{ metadata.basedir }}/certs/*; + -days 3650 -key "{{ sram_metadata_basedir }}/certs/signing.key" + -out "{{ sram_metadata_basedir }}/certs/signing.crt" -extensions v3_ca; + chown {{sram_metadata_user}}:{{sram_metadata_group}} {{ sram_metadata_basedir }}/certs/*; ' - creates: "{{ metadata.basedir }}/certs/signing.crt" - when: "metadata.signing_cert is not defined" + creates: "{{ sram_metadata_basedir }}/certs/signing.crt" + when: "sram_metadata_signing_cert is not defined" notify: "Restart the pyFF container" - name: "Write fixed Metadata signing certificates" copy: - dest: "{{ metadata.basedir }}/certs/{{ item.file }}" + dest: "{{ sram_metadata_basedir }}/certs/{{ item.file }}" content: "{{item.contents}}" mode: "{{item.mode}}" - owner: "{{metadata.user}}" - group: "{{metadata.group}}" + owner: "{{sram_metadata_user}}" + group: "{{sram_metadata_group}}" with_items: - - { file: "signing.key", mode: "0640", contents: "{{metadata.signing_cert.priv}}" } - - { file: "signing.crt", mode: "0644", contents: "{{metadata.signing_cert.pub}}" } - when: "metadata.signing_cert is defined" + - { file: "signing.key", mode: "0640", contents: "{{sram_metadata_signing_cert.priv}}" } + - { file: "signing.crt", mode: "0644", contents: "{{sram_metadata_signing_cert.pub}}" } + when: "sram_metadata_signing_cert is defined" notify: "Restart the pyFF container" - name: "Copy source certificates" copy: src: "{{ item }}" - dest: "{{ metadata.basedir }}/certs" + dest: "{{ sram_metadata_basedir }}/certs" mode: "0644" with_items: - "surfconext.crt" @@ -37,15 +37,15 @@ - name: "Install IdP metadata" copy: content: "{{item.metadata}}" - dest: "{{ metadata.basedir }}/src/{{item.name}}.xml" + dest: "{{ sram_metadata_basedir }}/src/{{item.name}}.xml" mode: "0644" - with_items: "{{ metadata.idps_files }}" + with_items: "{{ sram_metadata_idps_files }}" notify: "Restart the pyFF container" - name: "Copy pyFF xslt transformations" copy: src: "{{item}}" - dest: "{{metadata.basedir}}/xslt" + dest: "{{sram_metadata_basedir}}/xslt" mode: "0644" with_items: - "transform_proxy.xslt" @@ -55,7 +55,7 @@ - name: "Copy pyFF feeds" copy: src: "{{item}}" - dest: "{{metadata.basedir}}/feeds" + dest: "{{sram_metadata_basedir}}/feeds" mode: "0644" with_items: - "01_idps.fd" @@ -66,7 +66,7 @@ - name: "Create the pyFF container" community.docker.docker_container: name: "sram-metadata-pyff" - image: "{{ metadata.image_pyff }}" + image: "{{ sram_metadata_image_pyff }}" restart_policy: "always" state: "started" pull: true @@ -75,22 +75,22 @@ USER: "{{ metadata_user_uid | string }}" GROUP: "{{ metadata_group_gid | string }}" mounts: - - source: "{{ metadata.basedir }}/web" + - source: "{{ sram_metadata_basedir }}/web" target: "/opt/pyff/web" type: "bind" - - source: "{{ metadata.basedir }}/feeds" + - source: "{{ sram_metadata_basedir }}/feeds" target: "/opt/pyff/feeds" type: "bind" read_only: true - - source: "{{ metadata.basedir }}/src" + - source: "{{ sram_metadata_basedir }}/src" target: "/opt/pyff/src" type: "bind" read_only: true - - source: "{{ metadata.basedir }}/certs" + - source: "{{ sram_metadata_basedir }}/certs" target: "/opt/pyff/certs" type: "bind" read_only: true - - source: "{{ metadata.basedir }}/xslt" + - source: "{{ sram_metadata_basedir }}/xslt" target: "/opt/pyff/xslt" type: "bind" read_only: true diff --git a/roles/sram-metadata/templates/index.html.j2 b/roles/sram_metadata/templates/index.html.j2 similarity index 100% rename from roles/sram-metadata/templates/index.html.j2 rename to roles/sram_metadata/templates/index.html.j2 diff --git a/roles/sram-metadata/templates/pyff-metadata.service.j2 b/roles/sram_metadata/templates/pyff-metadata.service.j2 similarity index 100% rename from roles/sram-metadata/templates/pyff-metadata.service.j2 rename to roles/sram_metadata/templates/pyff-metadata.service.j2 diff --git a/roles/sram-metadata/templates/pyff-metadata.timer.j2 b/roles/sram_metadata/templates/pyff-metadata.timer.j2 similarity index 100% rename from roles/sram-metadata/templates/pyff-metadata.timer.j2 rename to roles/sram_metadata/templates/pyff-metadata.timer.j2 diff --git a/roles/sram-metadata/templates/vhosts.conf.j2 b/roles/sram_metadata/templates/vhosts.conf.j2 similarity index 100% rename from roles/sram-metadata/templates/vhosts.conf.j2 rename to roles/sram_metadata/templates/vhosts.conf.j2 diff --git a/roles/sram-midproxy/defaults/main.yml b/roles/sram_midproxy/defaults/main.yml similarity index 100% rename from roles/sram-midproxy/defaults/main.yml rename to roles/sram_midproxy/defaults/main.yml diff --git a/roles/sram-midproxy/files/internal_attributes.yaml b/roles/sram_midproxy/files/internal_attributes.yaml similarity index 100% rename from roles/sram-midproxy/files/internal_attributes.yaml rename to roles/sram_midproxy/files/internal_attributes.yaml diff --git a/roles/sram-midproxy/files/plugins/attribute-maps/basic.py b/roles/sram_midproxy/files/plugins/attribute-maps/basic.py similarity index 100% rename from roles/sram-midproxy/files/plugins/attribute-maps/basic.py rename to roles/sram_midproxy/files/plugins/attribute-maps/basic.py diff --git a/roles/sram-midproxy/files/plugins/backends/openid_backend.yaml b/roles/sram_midproxy/files/plugins/backends/openid_backend.yaml similarity index 100% rename from roles/sram-midproxy/files/plugins/backends/openid_backend.yaml rename to roles/sram_midproxy/files/plugins/backends/openid_backend.yaml diff --git a/roles/sram-midproxy/files/plugins/backends/saml2_backend.yaml b/roles/sram_midproxy/files/plugins/backends/saml2_backend.yaml similarity index 100% rename from roles/sram-midproxy/files/plugins/backends/saml2_backend.yaml rename to roles/sram_midproxy/files/plugins/backends/saml2_backend.yaml diff --git a/roles/sram-midproxy/files/plugins/frontends/ping_frontend.yaml b/roles/sram_midproxy/files/plugins/frontends/ping_frontend.yaml similarity index 100% rename from roles/sram-midproxy/files/plugins/frontends/ping_frontend.yaml rename to roles/sram_midproxy/files/plugins/frontends/ping_frontend.yaml diff --git a/roles/sram-midproxy/files/plugins/frontends/saml2_frontend.yaml b/roles/sram_midproxy/files/plugins/frontends/saml2_frontend.yaml similarity index 100% rename from roles/sram-midproxy/files/plugins/frontends/saml2_frontend.yaml rename to roles/sram_midproxy/files/plugins/frontends/saml2_frontend.yaml diff --git a/roles/sram-midproxy/files/plugins/microservices/generate_attributes.yaml b/roles/sram_midproxy/files/plugins/microservices/generate_attributes.yaml similarity index 100% rename from roles/sram-midproxy/files/plugins/microservices/generate_attributes.yaml rename to roles/sram_midproxy/files/plugins/microservices/generate_attributes.yaml diff --git a/roles/sram-midproxy/files/plugins/microservices/regex_attributes.yaml b/roles/sram_midproxy/files/plugins/microservices/regex_attributes.yaml similarity index 100% rename from roles/sram-midproxy/files/plugins/microservices/regex_attributes.yaml rename to roles/sram_midproxy/files/plugins/microservices/regex_attributes.yaml diff --git a/roles/sram-midproxy/files/proxy_conf.yaml b/roles/sram_midproxy/files/proxy_conf.yaml similarity index 100% rename from roles/sram-midproxy/files/proxy_conf.yaml rename to roles/sram_midproxy/files/proxy_conf.yaml diff --git a/roles/sram-midproxy/tasks/main.yml b/roles/sram_midproxy/tasks/main.yml similarity index 100% rename from roles/sram-midproxy/tasks/main.yml rename to roles/sram_midproxy/tasks/main.yml diff --git a/roles/sram_plsc/defaults/main.yml b/roles/sram_plsc/defaults/main.yml new file mode 100644 index 000000000..f3b60a23e --- /dev/null +++ b/roles/sram_plsc/defaults/main.yml @@ -0,0 +1,12 @@ +--- +plsc_image: "ghcr.io/surfscz/sram-plsc:main" +plsc_conf_dir: "{{current_release_appdir}}/sram/plsc" +plsc_ansible_nolog: false +plsc_ldap_uri: "ldap://ldap:389/" +plsc_ldap_basedn: "dc=services,dc=vnet" +plsc_ldap_binddn: "cn=admin,dc=vnet" +plsc_ldap_password: "secret" +plsc_sbs_host: "http://sbs-server:8080" +plsc_sbs_user: "sysread" +plsc_sbs_password: "secret" +plsc_retry: 3 diff --git a/roles/sram-plsc/handlers/main.yml b/roles/sram_plsc/handlers/main.yml similarity index 100% rename from roles/sram-plsc/handlers/main.yml rename to roles/sram_plsc/handlers/main.yml diff --git a/roles/sram-plsc/tasks/main.yml b/roles/sram_plsc/tasks/main.yml similarity index 68% rename from roles/sram-plsc/tasks/main.yml rename to roles/sram_plsc/tasks/main.yml index 0d3900bd9..0523ce118 100644 --- a/roles/sram-plsc/tasks/main.yml +++ b/roles/sram_plsc/tasks/main.yml @@ -1,28 +1,29 @@ --- - name: Make sure clients sync directory exists file: - path: "{{ plsc.conf_dir }}" + path: "{{ plsc_conf_dir }}" state: directory mode: "0755" - name: "Create plsc.yml source if it doesn't exist" template: src: "plsc.yml.j2" - dest: "{{ plsc.conf_dir }}/plsc.yml" + dest: "{{ plsc_conf_dir }}/plsc.yml" mode: "0640" - no_log: "{{plsc.ansible_nolog}}" + no_log: "{{plsc_ansible_nolog}}" notify: "Restart the plsc container" - name: Create the plsc container community.docker.docker_container: name: "sram-plsc" - image: "{{ plsc.image }}" + image: "{{ plsc_image }}" restart_policy: "always" state: started pull: true mounts: - type: bind - source: "{{ plsc.conf_dir }}/plsc.yml" + source: "{{ plsc_conf_dir }}/plsc.yml" target: "/opt/plsc/plsc.yml" networks: + # TODO: Should this not be parametrized? - name: "loadbalancer" diff --git a/roles/sram_plsc/templates/plsc.yml.j2 b/roles/sram_plsc/templates/plsc.yml.j2 new file mode 100644 index 000000000..f1e7d4c6c --- /dev/null +++ b/roles/sram_plsc/templates/plsc.yml.j2 @@ -0,0 +1,25 @@ +--- +ldap: + src: + uri: "{{ plsc_ldap_uri }}" + basedn: "{{ plsc_ldap_basedn }}" + binddn: "{{ plsc_ldap_binddn }}" + passwd: "{{ plsc_ldap_password }}" + sizelimit: 500 + dst: + uri: "{{ plsc_ldap_uri }}" + basedn: "{{ plsc_ldap_basedn }}" + binddn: "{{ plsc_ldap_binddn }}" + passwd: "{{ plsc_ldap_password }}" + sizelimit: 500 +sbs: + src: + host: "{{ plsc_sbs_host }}" + user: "{{ plsc_sbs_user }}" + passwd: "{{ plsc_sbs_password }}" + verify_ssl: {{ false if env=='vm' else true }} + timeout: 60 + retry: {{ plsc_retry }} +pwd: "{CRYPT}!" +uid: 1000 +gid: 1000 diff --git a/roles/sram_redis/defaults/main.yml b/roles/sram_redis/defaults/main.yml new file mode 100644 index 000000000..857311145 --- /dev/null +++ b/roles/sram_redis/defaults/main.yml @@ -0,0 +1,9 @@ +--- +redis_image: "docker.io/library/redis:7" +redis_conf_dir: "{{ current_release_appdir }}/sram/redis" +redis_data_dir: "{{ current_release_appdir }}/sram/redis/data" +redis_user: redis +redis_group: redis +redis_redis_user: default +redis_redis_password: changethispassword +redis_max_memory: 100mb diff --git a/roles/sram-redis/handlers/main.yml b/roles/sram_redis/handlers/main.yml similarity index 100% rename from roles/sram-redis/handlers/main.yml rename to roles/sram_redis/handlers/main.yml diff --git a/roles/sram-redis/tasks/main.yml b/roles/sram_redis/tasks/main.yml similarity index 64% rename from roles/sram-redis/tasks/main.yml rename to roles/sram_redis/tasks/main.yml index 72789b08f..0212fe0b0 100644 --- a/roles/sram-redis/tasks/main.yml +++ b/roles/sram_redis/tasks/main.yml @@ -1,7 +1,7 @@ --- - name: "Create redis group" group: - name: "{{ redis.group }}" + name: "{{ redis_group }}" state: "present" register: "result" @@ -11,12 +11,12 @@ - name: "Create redis user" user: - name: "{{ redis.user }}" - group: "{{ redis.group }}" + name: "{{ redis_user }}" + group: "{{ redis_group }}" comment: "User to run SRAM Redis service" shell: "/bin/false" password: "!" - home: "{{ redis.conf_dir }}" + home: "{{ redis_conf_dir }}" create_home: false state: "present" register: "result" @@ -29,33 +29,34 @@ file: path: "{{item.path}}" state: "directory" - owner: "{{ redis.user }}" - group: "{{ redis.group }}" + owner: "{{ redis_user }}" + group: "{{ redis_group }}" mode: "{{item.mode}}" with_items: - - { path: "{{redis.conf_dir}}", mode: "0755" } - - { path: "{{redis.data_dir}}", mode: "0755" } + - { path: "{{redis_conf_dir}}", mode: "0755" } + - { path: "{{redis_data_dir}}", mode: "0755" } - name: "Create redis config" template: src: "redis.conf.j2" - dest: "{{ redis.conf_dir }}/redis.conf" - owner: "{{ redis.user }}" - group: "{{ redis.group }}" + dest: "{{ redis_conf_dir }}/redis.conf" + owner: "{{ redis_user }}" + group: "{{ redis_group }}" mode: "0644" notify: "Restart redis container" - name: "Create redis container" community.docker.docker_container: name: "sram-redis" - image: "{{ redis.image }}" + image: "{{ redis_image }}" restart_policy: "always" state: "started" user: "{{ redis_user_uid }}:{{ redis_group_gid }}" command: | redis-server /usr/local/etc/redis/redis.conf volumes: - - "{{ redis.conf_dir }}:/usr/local/etc/redis" - - "{{ redis.data_dir }}:/data" + - "{{ redis_conf_dir }}:/usr/local/etc/redis" + - "{{ redis_data_dir }}:/data" networks: + # TODO: Should this not be parametrized? - name: loadbalancer diff --git a/roles/sram_redis/templates/redis.conf.j2 b/roles/sram_redis/templates/redis.conf.j2 new file mode 100644 index 000000000..159ea9599 --- /dev/null +++ b/roles/sram_redis/templates/redis.conf.j2 @@ -0,0 +1,3 @@ +user {{redis_redis_user}} on +@all ~* &* >{{redis_redis_password}} +maxmemory {{ redis_max_memory }} +maxmemory-policy allkeys-lru diff --git a/roles/sram_sbs/defaults/main.yml b/roles/sram_sbs/defaults/main.yml new file mode 100644 index 000000000..b564e0779 --- /dev/null +++ b/roles/sram_sbs/defaults/main.yml @@ -0,0 +1,166 @@ +--- +sbs_base_domain: "test2.sram.surf.nl" +sbs_ansible_nolog: true +sbs_base_url: "https://{{ sbs_base_domain }}" +sbs_server_image: "ghcr.io/surfscz/sram-sbs-server:main" +sbs_client_image: "ghcr.io/surfscz/sram-sbs-client:main" + +sbs_openidc_timeout: 86400 +sbs_sram_conf_dir: "{{ current_release_appdir }}/sram" + +sbs_work_dir: "{{ sbs_sram_conf_dir }}/sbs" +sbs_git_dir: "{{ sbs_work_dir }}/sbs" +sbs_env_dir: "{{ sbs_work_dir }}/sbs-env" +sbs_conf_dir: "{{ sbs_work_dir }}/config" +sbs_log_dir: "{{ sbs_work_dir }}/log" +sbs_cert_dir: "{{ sbs_work_dir }}/cert" +sbs_apache_conf: "{{ sbs_work_dir }}/sbs.conf" +sbs_nginx_conf: "{{ sbs_work_dir }}/nginx.conf" + +sbs_db_name: "sbs" +sbs_db_user: "sbsrw" +# dbbackup_user: "sbs_backupper" +sbs_migration_user: "sbsmigrate" + +sbs_db_connection: "\ + mysql+mysqldb://%s:%s@{{ mariadb_host }}/{{ sbs_db_name }}\ + ?ssl=true&charset=utf8mb4" +sbs_db_connection_sbs: "{{ sbs_db_connection | format(sbs_db_user, mysql_passwords.sbs) }}" +sbs_db_connection_migration: "\ + {{ sbs_db_connection | format(sbs_migration_user, mysql_passwords.sbsmigrate) }}" + +sbs_db_secret: secret +sbs_secret_key_suffix: suffix +sbs_encryption_key: encryption_key + +sbs_redis_host: sram-redis +sbs_redis_port: 6379 +sbs_redis_ssl: false +sbs_redis_user: default + +sbs_mail_host: "host.docker.internal" +sbs_mail_port: 25 + +sbs_user: "sbs" +sbs_group: "sbs" + +sbs_session_lifetime: 1440 +sbs_secret_key_suffix: "" + +sbs_oidc_crypto_password: "CHANGEME" +sbs_uid_attribute: "sub" + +sbs_disclaimer_color: "#a29c13" +sbs_disclaimer_label: wsgi + +sbs_urn_namespace: "urn:example:sbs" +sbs_eppn_scope: "sbs.example.edu" +sbs_restricted_co_default_org: "example.org" + +sbs_mail_sender_name: "SURF" +sbs_mail_sender_email: "no-reply@localhost" +sbs_exceptions_mail: "root@localhost" + +sbs_support_email: "sram-support@localhost" +sbs_admin_email: "sram-beheer@localhost" +sbs_ticket_email: "sram-support@surf.nl" +sbs_eduteams_email: "eduteams@localhost" + +sbs_suppress_mails: False + +sbs_wiki_link: "https://www.example.org/wiki" + +sbs_cron_hour_of_day: 4 +sbs_seed_allowed: True +sbs_api_keys_enabled: True +sbs_feedback_enabled: True +sbs_audit_trail_notifications_enabled: True +sbs_send_exceptions: False +sbs_send_js_exceptions: False +sbs_second_factor_authentication_required: True +sbs_totp_token_name: "SRAM-example" +sbs_notifications_enabled: True +sbs_invitation_reminders_enabled: True +sbs_invitation_expirations_enabled: True +sbs_open_requests_enabled: True +sbs_scim_sweep: False +sbs_impersonation_allowed: True +sbs_admin_platform_backdoor_totp: True +sbs_past_dates_allowed: True +sbs_mock_scim_enabled: True +sbs_log_to_stdout: True + +sbs_delete_orphaned: True +sbs_suspension_inactive_days: 365 +sbs_suspension_reminder_days: 14 +sbs_suspension_notify_admin: False + +sbs_oidc_config_url: "http://localhost/.well-known/openid-configuration" +sbs_oidc_authz_endpoint: "http://localhost/OIDC/authorization" +sbs_oidc_token_endpoint: "http://localhost/OIDC/token" +sbs_oidc_userinfo_endpoint: "http://localhost/OIDC/userinfo" +sbs_oidc_jwks_endpoint: "http://localhost/OIDC/jwks.json" +sbs_oidc_redirect_uri: "https://{{sbs_base_domain}}/api/users/resume-session" +sbs_oidc_jwt_audience: "https://localhost" +sbs_continue_eduteams_redirect_uri: "https://localhost/continue" +sbs_oidc_verify_peer: False +sbs_oidc_scopes: + - openid + +sbs_mfa_idp_allowed: false +sbs_eduteams_continue_endpoint: "https://localhost/continue" +sbs_eb_continue_endpoint: "https://engine.(.*)surfconext.nl(.*)" + +sbs_manage_base_enabled: False +sbs_manage_base_url: "https://manage.{{base_domain}}" +sbs_manage_sram_rp_entity_id: "sbs.{{sbs_base_domain}}" +sbs_manage_verify_peer: False + +sbs_idp_metadata_url: "https://metadata.surfconext.nl/signed/2023/edugain-downstream-idp.xml " +# backup_dir: "{{backup_base}}/sbs" + +sbs_swagger_enabled: true + +sbs_ssid_identity_providers: [] +sbs_surf_secure_id: + environment: "unknown.example.org" + sp_entity_id: "https://sbs.{{sbs_base_domain}}" + acs_url: "https://{{sbs_base_domain}}/api/users/acs" + sa_gw_environment: "sa-gw.unknown.example.org" + sa_idp_certificate: | + -----BEGIN CERTIFICATE----- + 12345 + -----END CERTIFICATE----- + priv: | + -----BEGIN RSA PRIVATE KEY----- + abcde + -----END RSA PRIVATE KEY----- + pub: | + -----BEGIN CERTIFICATE----- + 12345 + -----END CERTIFICATE----- + +sbs_ssid_authncontext: "\ + http://{{ sbs_surf_secure_id.environment }}/assurance/sfo-level2" +sbs_ssid_entityid: "\ + https://{{ sbs_surf_secure_id.sa_gw_environment }}/second-factor-only/metadata" +sbs_ssid_sso_endpoint: "\ + https://{{ sbs_surf_secure_id.sa_gw_environment }}/second-factor-only/single-sign-on" + +sbs_mfa_sso_minutes: 10 +sbs_mfa_fallback_enabled: true + +sbs_ldap_url: "ldap://ldap.example.com/dc=example,dc=com" +sbs_ldap_bind_account: "cn=admin,dc=entity_id,dc=services,dc=sram-tst,dc=surf,dc=nl" + +sbs_csp_style_hashes: + - 'sha256-0+ANsgYUJdh56RK8gGvTF2vnriYqvFHfWqtA8xXa+bA=' + - 'sha256-3SnfHQolDHbZMbDAPmhrZf1keHiXfj/KJyh2phhFAAY=' + - 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' + - 'sha256-Ng6y+QCkPChG4Q49SIfXB5ToIDcDhITtQNFkDBPpCTw=' + - 'sha256-orBPipbqpMvkNi+Z+m6qEn0XS6ymmAQE6+FwCNs1FbQ=' + - 'sha256-vFt3L2qLqpJmRpcXGbYr2UVSmgSp9VCUzz2lnqWIATw=' + - 'sha256-SU3XCwbQ/8qgzoGOWCYdkwIr3xRrl5rsvdFcpw8NSiE=' # on /new-service-request + - 'sha256-WTC9gHKjIpzl5ub1eg/YrRy/k+jlzeyRojah9dxAApc=' # on /new-service-request + +sbs_engine_block_api_token: secret diff --git a/roles/sram-sbs/files/yarn.gpg b/roles/sram_sbs/files/yarn.gpg similarity index 100% rename from roles/sram-sbs/files/yarn.gpg rename to roles/sram_sbs/files/yarn.gpg diff --git a/roles/sram-sbs/handlers/main.yml b/roles/sram_sbs/handlers/main.yml similarity index 100% rename from roles/sram-sbs/handlers/main.yml rename to roles/sram_sbs/handlers/main.yml diff --git a/roles/sram-sbs/tasks/main.yml b/roles/sram_sbs/tasks/main.yml similarity index 74% rename from roles/sram-sbs/tasks/main.yml rename to roles/sram_sbs/tasks/main.yml index 6881736ec..f5f16e117 100644 --- a/roles/sram-sbs/tasks/main.yml +++ b/roles/sram_sbs/tasks/main.yml @@ -1,12 +1,7 @@ --- -# - name: "Initialize database" -# throttle: 1 -# import_tasks: "database_init.yml" -# when: "is_dev" - - name: "Create SBS group" group: - name: "{{ sbs.group }}" + name: "{{ sbs_group }}" state: "present" register: "result" @@ -16,12 +11,12 @@ - name: "Create SBS user" user: - name: "{{ sbs.user }}" - group: "{{ sbs.group }}" + name: "{{ sbs_user }}" + group: "{{ sbs_group }}" comment: "User to run SBS service" shell: "/bin/false" password: "!" - home: "{{ sbs.conf_dir }}" + home: "{{ sbs_conf_dir }}" create_home: false state: "present" register: "result" @@ -38,15 +33,15 @@ group: "{{sbs_group_gid}}" mode: "{{item.mode}}" with_items: - - { path: "{{sbs.work_dir}}", mode: "0755" } - - { path: "{{sbs.conf_dir}}", mode: "0755" } - - { path: "{{sbs.conf_dir}}/saml", mode: "0755" } - - { path: "{{sbs.log_dir}}", mode: "0775" } - - { path: "{{sbs.cert_dir}}", mode: "0755" } + - { path: "{{sbs_work_dir}}", mode: "0755" } + - { path: "{{sbs_conf_dir}}", mode: "0755" } + - { path: "{{sbs_conf_dir}}/saml", mode: "0755" } + - { path: "{{sbs_log_dir}}", mode: "0775" } + - { path: "{{sbs_cert_dir}}", mode: "0755" } - name: "Fix file permissions" file: - path: "{{sbs.log_dir}}/{{item}}" + path: "{{sbs_log_dir}}/{{item}}" owner: "{{sbs_user_uid}}" group: "{{sbs_group_gid}}" mode: "0664" @@ -59,15 +54,15 @@ # Create dummy file in certs dir to pacify container pre-init script # https://github.com/SURFscz/SBS/pull/2312 -- name: "Touch file in {{ sbs.cert_dir }}" +- name: "Touch file in {{ sbs_cert_dir }}" ansible.builtin.copy: content: "" - dest: "{{sbs.cert_dir}}/dummy" + dest: "{{sbs_cert_dir}}/dummy" - name: "Create SBS config files" template: src: "{{item.name}}.j2" - dest: "{{ sbs.conf_dir }}/{{item.name}}" + dest: "{{ sbs_conf_dir }}/{{item.name}}" owner: "{{sbs_user_uid}}" group: "{{sbs_group_gid}}" mode: "{{item.mode}}" @@ -76,15 +71,15 @@ - { name: "alembic.ini", mode: "0644" } - { name: "disclaimer.css", mode: "0644" } - { name: "sbs-apache.conf", mode: "0644" } - no_log: "{{sbs.ansible_nolog}}" + no_log: "{{sbs_ansible_nolog}}" notify: "Restart sbs containers" - name: "Pull sbs image" community.docker.docker_image_pull: name: "{{ item }}" with_items: - - "{{ sbs.client_image }}" - - "{{ sbs.server_image }}" + - "{{ sbs_client_image }}" + - "{{ sbs_server_image }}" register: "sbs_image" # We need to remove sram-static so it gets repopulated @@ -98,14 +93,13 @@ with_items: - "sbs-client" - "sbs-server" - when: "sbs_image is changed" - name: "Run SBS migrations" throttle: 1 community.docker.docker_container: name: "sram-sbs-migration" - image: "{{ sbs.server_image }}" + image: "{{ sbs_server_image }}" pull: "never" state: "started" restart_policy: "no" @@ -117,14 +111,15 @@ # don't actually run the server command: "/bin/true" volumes: - - "{{ sbs.conf_dir }}:/sbs-config" - - "{{ sbs.cert_dir }}:/sbs-config/cert:ro" - - "{{ sbs.log_dir }}:/opt/sbs/log" + - "{{ sbs_conf_dir }}:/sbs-config" + - "{{ sbs_cert_dir }}:/sbs-config/cert:ro" + - "{{ sbs_log_dir }}:/opt/sbs/log" networks: + # TODO: Should we parametrize this? - name: "loadbalancer" register: "result" failed_when: "'container' not in result or result.container.State.ExitCode != 0" - changed_when: "'[alembic.runtime.migration] Running upgrade' in result.container.Output" + changed_when: "'[alembic.runtime.migration] Running upgrade' in result.container.Output | default('')" notify: "Restart sbs containers" # Remove the migration container; we can do that with auto_remove, because if we use that, ansible @@ -139,24 +134,24 @@ - name: "Start sbs client container" community.docker.docker_container: name: "sram-sbs-client" - image: "{{ sbs.client_image }}" + image: "{{ sbs_client_image }}" pull: "never" restart_policy: "always" state: "started" volumes: - - "{{ sbs.conf_dir }}/sbs-apache.conf:/etc/apache2/sites-enabled/sbs.conf:ro" - - "{{ sbs.conf_dir }}/disclaimer.css:/opt/sbs/client/dist/disclaimer.css:ro" + - "{{ sbs_conf_dir }}/sbs-apache.conf:/etc/apache2/sites-enabled/sbs.conf:ro" + - "{{ sbs_conf_dir }}/disclaimer.css:/opt/sbs/client/dist/disclaimer.css:ro" networks: - name: "loadbalancer" labels: - traefik.http.routers.sbsclient.rule: "Host(`{{ sbs.base_domain }}`)" + traefik.http.routers.sbsclient.rule: "Host(`{{ sbs_base_domain }}`)" traefik.http.routers.sbsclient.tls: "true" traefik.enable: "true" - name: "Start SBS server container" community.docker.docker_container: name: "sram-sbs-server" - image: "{{ sbs.server_image }}" + image: "{{ sbs_server_image }}" restart_policy: "always" state: "started" env: @@ -167,9 +162,9 @@ RUN_MIGRATIONS: "0" pull: "never" volumes: - - "{{ sbs.conf_dir }}:/sbs-config" - - "{{ sbs.cert_dir }}:/sbs-config/cert:ro" - - "{{ sbs.log_dir }}:/opt/sbs/log" + - "{{ sbs_conf_dir }}:/sbs-config" + - "{{ sbs_cert_dir }}:/sbs-config/cert:ro" + - "{{ sbs_log_dir }}:/opt/sbs/log" - "/tmp/ci-runner:/tmp/ci-runner" networks: - name: "loadbalancer" diff --git a/roles/sram-sbs/templates/alembic.ini.j2 b/roles/sram_sbs/templates/alembic.ini.j2 similarity index 96% rename from roles/sram-sbs/templates/alembic.ini.j2 rename to roles/sram_sbs/templates/alembic.ini.j2 index 9ccd51979..7849e4f89 100644 --- a/roles/sram-sbs/templates/alembic.ini.j2 +++ b/roles/sram_sbs/templates/alembic.ini.j2 @@ -35,7 +35,7 @@ script_location = migrations # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = {{ sbs.db_connection_migration }} +sqlalchemy.url = {{ sbs_db_connection_migration }} # Logging configuration [loggers] diff --git a/roles/sram-sbs/templates/config.yml.j2 b/roles/sram_sbs/templates/config.yml.j2 similarity index 58% rename from roles/sram-sbs/templates/config.yml.j2 rename to roles/sram_sbs/templates/config.yml.j2 index 7d4c92bf4..9f597debe 100644 --- a/roles/sram-sbs/templates/config.yml.j2 +++ b/roles/sram_sbs/templates/config.yml.j2 @@ -1,110 +1,110 @@ --- database: - uri: {{ sbs.db_connection_sbs }} + uri: {{ sbs_db_connection_sbs }} redis: -{% if env == 'test2' %} - uri: "redis://{{ sbs.redis_user }}:{{ sbs.redis_password }}@{{sbs.redis_host}}/" +{% if environment_shortname == 'test2' %} + uri: "redis://{{ sbs_redis_user }}:{{ sbs_redis_password }}@{{sbs_redis_host}}/" {% else %} - uri: "redis{% if sbs.redis_ssl %}s{% endif %}://{{ sbs.redis_user }}:{{ sbs.redis_password }}@{{ sbs.redis_host }}:{{ sbs.redis_port }}/" + uri: "redis{% if sbs_redis_ssl %}s{% endif %}://{{ sbs_redis_user }}:{{ sbs_redis_password }}@{{ sbs_redis_host }}:{{ sbs_redis_port }}/" {% endif %} # add a per-release suffix here to invalidate sessions on new releases -secret_key: {{ sbs.db_secret }}{{sbs.secret_key_suffix}} +secret_key: {{ sbs_db_secret }}{{sbs_secret_key_suffix}} # Must be a base64 encoded key of 128, 192, or 256 bits. # Generate: base64.b64encode(os.urandom(256 // 8)).decode() -encryption_key: {{ sbs.encryption_key }} +encryption_key: {{ sbs_encryption_key }} # Lifetime of session in minutes (one day is 60 * 24) -permanent_session_lifetime: {{ sbs.session_lifetime }} +permanent_session_lifetime: {{ sbs_session_lifetime }} logging: - log_to_stdout: {{ sbs.log_to_stdout }} + log_to_stdout: {{ sbs_log_to_stdout }} # Valid scopes are "READ" and "WRITE" api_users: -{% for name, user in sbs.api_users.items() %} +{% for name, user in sbs_api_users.items() %} - name: "{{ name }}" password: "{{ user.password }}" scopes: "[ {{ user.scopes | join(', ') }} ]" {% endfor %} oidc: - client_id: "{{ sbs.oidc_client_id }}" - client_secret: "{{ sbs.oidc_client_secret }}" - audience: "{{ sbs.oidc_jwt_audience }}" - verify_peer: {{ sbs.oidc_verify_peer }} - authorization_endpoint: "{{ sbs.oidc_authz_endpoint}}" - token_endpoint: "{{ sbs.oidc_token_endpoint }}" - userinfo_endpoint: "{{ sbs.oidc_userinfo_endpoint }}" - jwks_endpoint: "{{ sbs.oidc_jwks_endpoint }}" + client_id: "{{ sbs_oidc_client_id }}" + client_secret: "{{ sbs_oidc_client_secret }}" + audience: "{{ sbs_oidc_jwt_audience }}" + verify_peer: {{ sbs_oidc_verify_peer }} + authorization_endpoint: "{{ sbs_oidc_authz_endpoint}}" + token_endpoint: "{{ sbs_oidc_token_endpoint }}" + userinfo_endpoint: "{{ sbs_oidc_userinfo_endpoint }}" + jwks_endpoint: "{{ sbs_oidc_jwks_endpoint }}" #Note that the paths for these uri's is hardcoded and only domain and port differ per environment - redirect_uri: "{{ sbs.oidc_redirect_uri }}" - continue_eduteams_redirect_uri: "{{ sbs.eduteams_continue_endpoint }}" - continue_eb_redirect_uri: "{{ sbs.eb_continue_endpoint }}" - second_factor_authentication_required: {{ sbs.second_factor_authentication_required }} - totp_token_name: "{{ sbs.totp_token_name }}" + redirect_uri: "{{ sbs_oidc_redirect_uri }}" + continue_eduteams_redirect_uri: "{{ sbs_eduteams_continue_endpoint }}" + continue_eb_redirect_uri: "{{ sbs_eb_continue_endpoint }}" + second_factor_authentication_required: {{ sbs_second_factor_authentication_required }} + totp_token_name: "{{ sbs_totp_token_name }}" # The service_id in the proxy_authz endpoint when logging into SBS. Most likely to equal the oidc.client_id - sram_service_entity_id: "{{ sbs.oidc_client_id }}" - scopes: {{ sbs.oidc_scopes }} + sram_service_entity_id: "{{ sbs_oidc_client_id }}" + scopes: {{ sbs_oidc_scopes }} base_scope: "{{ base_domain }}" -entitlement_group_namespace: "{{ sbs.urn_namespace }}" -eppn_scope: " {{ sbs.eppn_scope }}" +entitlement_group_namespace: "{{ sbs_urn_namespace }}" +eppn_scope: " {{ sbs_eppn_scope }}" scim_schema_sram: "urn:mace:surf.nl:sram:scim:extension" collaboration_creation_allowed_entitlement: "urn:mace:surf.nl:sram:allow-create-co" {% if env == "prd" %} environment_disclaimer: "" {% else %} -environment_disclaimer: "{{ sbs.disclaimer_label }}" +environment_disclaimer: "{{ sbs_disclaimer_label }}" {% endif %} # All services in the white list can be requested in the create-restricted-co API # The default organisation is a fallback for when the administrator has no schac_home_org restricted_co: services_white_list: [ "https://cloud" ] - default_organisation: "{{ sbs.restricted_co_default_org }}" + default_organisation: "{{ sbs_restricted_co_default_org }}" mail: - host: {{ sbs.mail_host }} - port: {{ sbs.mail_port }} - sender_name: {{ sbs.mail_sender_name }} - sender_email: {{ sbs.mail_sender_email }} - suppress_sending_mails: {{ sbs.suppress_mails }} - info_email: {{ sbs.support_email }} - beheer_email: {{ sbs.admin_email }} - ticket_email: {{ sbs.ticket_email }} - eduteams_email: {{ sbs.eduteams_email }} + host: {{ sbs_mail_host }} + port: {{ sbs_mail_port }} + sender_name: {{ sbs_mail_sender_name }} + sender_email: {{ sbs_mail_sender_email }} + suppress_sending_mails: {{ sbs_suppress_mails }} + info_email: {{ sbs_support_email }} + beheer_email: {{ sbs_admin_email }} + ticket_email: {{ sbs_ticket_email }} + eduteams_email: {{ sbs_eduteams_email }} # Do we mail a summary of new Organizations and Services to the beheer_email? - audit_trail_notifications_enabled: {{ sbs.audit_trail_notifications_enabled }} + audit_trail_notifications_enabled: {{ sbs_audit_trail_notifications_enabled }} account_deletion_notifications_enabled: True - send_exceptions: {{ sbs.send_exceptions }} - send_js_exceptions: {{ sbs.send_js_exceptions }} - send_exceptions_recipients: [ "{{ sbs.exceptions_mail }}" ] + send_exceptions: {{ sbs_send_exceptions }} + send_js_exceptions: {{ sbs_send_js_exceptions }} + send_exceptions_recipients: [ "{{ sbs_exceptions_mail }}" ] environment: "{{ base_domain }}" manage: - enabled: {{ sbs.manage_base_enabled }} + enabled: {{ sbs_manage_base_enabled }} # The entity_id of the SRAM RP in Manage for API retrieval, e.g "sbs.test2.sram.surf.nl" - sram_rp_entity_id: "{{ sbs.manage_sram_rp_entity_id }}" - base_url: "{{ sbs.manage_base_url }}" - user: "{{ sbs.manage_user }}" - password: "{{ sbs.manage_password }}" - verify_peer: {{ sbs.manage_verify_peer }} + sram_rp_entity_id: "{{ sbs_manage_sram_rp_entity_id }}" + base_url: "{{ sbs_manage_base_url }}" + user: "{{ sbs_manage_user }}" + password: "{{ sbs_manage_password }}" + verify_peer: {{ sbs_manage_verify_peer }} aup: version: 1 url_aup_en: "https://edu.nl/6wb63" url_aup_nl: "https://edu.nl/6wb63" -base_url: {{ sbs.base_url }} -socket_url: {{ sbs.base_url }} -base_server_url: {{ sbs.base_url }} -wiki_link: {{ sbs.wiki_link }} +base_url: {{ sbs_base_url }} +socket_url: {{ sbs_base_url }} +base_server_url: {{ sbs_base_url }} +wiki_link: {{ sbs_wiki_link }} admin_users: -{% for admin_user in sbs.admin_users %} +{% for admin_user in sbs_admin_users %} - uid: "{{ admin_user.uid }}" {% endfor %} @@ -117,17 +117,17 @@ organisation_categories: - "SURF" feature: - seed_allowed: {{ sbs.seed_allowed }} - api_keys_enabled: {{ sbs.api_keys_enabled }} - feedback_enabled: {{ sbs.feedback_enabled }} - impersonation_allowed: {{ sbs.impersonation_allowed }} - sbs_swagger_enabled: {{ sbs.swagger_enabled }} - admin_platform_backdoor_totp: {{ sbs.admin_platform_backdoor_totp }} - past_dates_allowed: {{ sbs.past_dates_allowed }} - mock_scim_enabled: {{ sbs.mock_scim_enabled }} + seed_allowed: {{ sbs_seed_allowed }} + api_keys_enabled: {{ sbs_api_keys_enabled }} + feedback_enabled: {{ sbs_feedback_enabled }} + impersonation_allowed: {{ sbs_impersonation_allowed }} + sbs_swagger_enabled: {{ sbs_swagger_enabled }} + admin_platform_backdoor_totp: {{ sbs_admin_platform_backdoor_totp }} + past_dates_allowed: {{ sbs_past_dates_allowed }} + mock_scim_enabled: {{ sbs_mock_scim_enabled }} metadata: - idp_url: "{{sbs.idp_metadata_url}}" + idp_url: "{{sbs_idp_metadata_url}}" parse_at_startup: True # No need for environment specific values scope_override: @@ -136,7 +136,7 @@ metadata: platform_admin_notifications: # Do we daily check for CO join_requests and CO requests and send a summary mail to beheer_email? enabled: False - cron_hour_of_day: {{ sbs.cron_hour_of_day }} + cron_hour_of_day: {{ sbs_cron_hour_of_day }} # How long before we include open join_requests in the summary outstanding_join_request_days_threshold: 7 # How long before we include open CO requests in the summary @@ -144,8 +144,8 @@ platform_admin_notifications: user_requests_retention: # Do we daily check for CO join_requests and CO requests and delete approved and denied? - enabled: {{ sbs.notifications_enabled }} - cron_hour_of_day: {{ sbs.cron_hour_of_day }} + enabled: {{ sbs_notifications_enabled }} + cron_hour_of_day: {{ sbs_cron_hour_of_day }} # How long before we delete approved / denied join_requests outstanding_join_request_days_threshold: 90 # How long before we delete approved / denied CO requests @@ -153,24 +153,24 @@ user_requests_retention: # The retention config determines how long users may be inactive, how long the reminder email is valid and when do we resent the magic link retention: - cron_hour_of_day: {{ sbs.cron_hour_of_day }} + cron_hour_of_day: {{ sbs_cron_hour_of_day }} # how many days of inactivity before a user is suspended # 0 allows for any last_login_date in the past to trigger suspension notification - allowed_inactive_period_days: {{ sbs.suspension_inactive_days }} + allowed_inactive_period_days: {{ sbs_suspension_inactive_days }} # how many days before suspension do we send a warning # -1 will suspend notified users on second suspension cron - reminder_suspend_period_days: {{ sbs.suspension_reminder_days }} + reminder_suspend_period_days: {{ sbs_suspension_reminder_days }} # how many days after suspension do we delete the account remove_suspended_users_period_days: 90 # how many days before deletion do we send a reminder reminder_expiry_period_days: 7 # whether to send a notification of the result of the retention process to the beheer_email - admin_notification_mail: {{ sbs.suspension_notify_admin }} + admin_notification_mail: {{ sbs_suspension_notify_admin }} collaboration_expiration: # Do we daily check for CO's that will be deleted because they have been expired? - enabled: {{ sbs.notifications_enabled }} - cron_hour_of_day: {{ sbs.cron_hour_of_day }} + enabled: {{ sbs_notifications_enabled }} + cron_hour_of_day: {{ sbs_cron_hour_of_day }} # How long after expiration do we actually delete expired collaborations expired_collaborations_days_threshold: 90 # How many days before actual expiration do we mail the organisation members @@ -178,8 +178,8 @@ collaboration_expiration: collaboration_suspension: # Do we daily check for CO's that will be suspended because of inactivity? - enabled: {{ sbs.notifications_enabled }} - cron_hour_of_day: {{ sbs.cron_hour_of_day }} + enabled: {{ sbs_notifications_enabled }} + cron_hour_of_day: {{ sbs_cron_hour_of_day }} # After how many days of inactivity do we suspend collaborations collaboration_inactivity_days_threshold: 365 # How many days before actual suspension do we mail the organisation members @@ -189,8 +189,8 @@ collaboration_suspension: membership_expiration: # Do we daily check for memberships that will be deleted because they have been expired? - enabled: {{ sbs.notifications_enabled }} - cron_hour_of_day: {{ sbs.cron_hour_of_day }} + enabled: {{ sbs_notifications_enabled }} + cron_hour_of_day: {{ sbs_cron_hour_of_day }} # How long after expiration do we actually delete expired memberships expired_memberships_days_threshold: 90 # How many days before actual expiration do we mail the co admin and member @@ -198,15 +198,15 @@ membership_expiration: invitation_reminders: # Do we daily check for invitations that need a reminder? - enabled: {{ sbs.invitation_reminders_enabled }} - cron_hour_of_day: {{ sbs.cron_hour_of_day }} + enabled: {{ sbs_invitation_reminders_enabled }} + cron_hour_of_day: {{ sbs_cron_hour_of_day }} # How many days before expiration of an invitation do we remind the user? invitation_reminders_threshold: 5 invitation_expirations: # Do we daily check for invitations that are expired / accepted and are eligible for deletion ? - enabled: {{ sbs.invitation_expirations_enabled }} - cron_hour_of_day: {{ sbs.cron_hour_of_day }} + enabled: {{ sbs_invitation_expirations_enabled }} + cron_hour_of_day: {{ sbs_cron_hour_of_day }} # How long after expiration of an invitation do we delete the invitation? nbr_days_remove_expired_invitations: 10 # How long after expiration of an API created invitation do we delete the invitation? @@ -214,38 +214,38 @@ invitation_expirations: orphan_users: # Do we daily check for users that are orphans soo they can be deleted? - enabled: {{ sbs.delete_orphaned }} - cron_hour_of_day: {{ sbs.cron_hour_of_day }} + enabled: {{ sbs_delete_orphaned }} + cron_hour_of_day: {{ sbs_cron_hour_of_day }} # How long after created do we delete orphan users delete_days_threshold: 14 open_requests: # Do we weekly check for all open requests? - enabled: {{ sbs.open_requests_enabled }} + enabled: {{ sbs_open_requests_enabled }} cron_day_of_week: 1 scim_sweep: # Do we enable scim sweeps? - enabled: {{ sbs.scim_sweep }} + enabled: {{ sbs_scim_sweep }} # How often do we check if scim sweeps are needed per service cron_minutes_expression: "*/15" ldap: - url: "{{ sbs.ldap_url }}" - bind_account: "{{ sbs.ldap_bind_account }}" + url: "{{ sbs_ldap_url }}" + bind_account: "{{ sbs_ldap_bind_account }}" # A MFA login in a different flow is valid for X minutes -mfa_sso_time_in_minutes: {{sbs.mfa_sso_minutes}} +mfa_sso_time_in_minutes: {{sbs_mfa_sso_minutes}} # whether to fall back to TOTP MFA -mfa_fallback_enabled: {{sbs.mfa_fallback_enabled}} +mfa_fallback_enabled: {{sbs_mfa_fallback_enabled}} # Lower case entity ID's and schac_home allowed skipping MFA. # Note that for a login directly into SRAM only schac_home can be used as the entity_idp of the IdP is unknown -mfa_idp_allowed: {{sbs.mfa_idp_allowed}} +mfa_idp_allowed: {{sbs_mfa_idp_allowed}} # Lower case schachome organisations / entity ID's where SURFSecure ID is used for step-up -ssid_identity_providers: {{sbs.ssid_identity_providers}} +ssid_identity_providers: {{sbs_ssid_identity_providers}} ssid_config_folder: saml @@ -256,9 +256,9 @@ rate_limit_totp_guesses_per_30_seconds: 10 # The uid's of user that will never be suspended or deleted excluded_user_accounts: -{% for excluded_user in sbs.excluded_users %} +{% for excluded_user in sbs_excluded_users %} - uid: "{{ excluded_user.uid }}" {% endfor %} engine_block: - api_token: {{ sbs.engine_block_api_token }} + api_token: {{ sbs_engine_block_api_token }} diff --git a/roles/sram_sbs/templates/disclaimer.css.j2 b/roles/sram_sbs/templates/disclaimer.css.j2 new file mode 100644 index 000000000..0211d17d8 --- /dev/null +++ b/roles/sram_sbs/templates/disclaimer.css.j2 @@ -0,0 +1,6 @@ +{% if env!="prd" -%} +body::after { + background: {{ sbs_disclaimer_color }}; + content: "{{ sbs_disclaimer_label }}"; +} +{% endif %} diff --git a/roles/sram-sbs/templates/saml_advanced_settings.json.j2 b/roles/sram_sbs/templates/saml_advanced_settings.json.j2 similarity index 100% rename from roles/sram-sbs/templates/saml_advanced_settings.json.j2 rename to roles/sram_sbs/templates/saml_advanced_settings.json.j2 diff --git a/roles/sram-sbs/templates/saml_settings.json.j2 b/roles/sram_sbs/templates/saml_settings.json.j2 similarity index 100% rename from roles/sram-sbs/templates/saml_settings.json.j2 rename to roles/sram_sbs/templates/saml_settings.json.j2 diff --git a/roles/sram-sbs/templates/sbs-apache.conf.j2 b/roles/sram_sbs/templates/sbs-apache.conf.j2 similarity index 98% rename from roles/sram-sbs/templates/sbs-apache.conf.j2 rename to roles/sram_sbs/templates/sbs-apache.conf.j2 index f0140a845..af8c32ce7 100644 --- a/roles/sram-sbs/templates/sbs-apache.conf.j2 +++ b/roles/sram_sbs/templates/sbs-apache.conf.j2 @@ -1,4 +1,4 @@ -ServerName {{ sbs.base_domain }} +ServerName {{ sbs_base_domain }} #ErrorLog /proc/self/fd/2 #CustomLog /proc/self/fd/1 common DocumentRoot /opt/sbs/client/dist diff --git a/roles/sram-sbs/templates/sbs.service.j2 b/roles/sram_sbs/templates/sbs.service.j2 similarity index 100% rename from roles/sram-sbs/templates/sbs.service.j2 rename to roles/sram_sbs/templates/sbs.service.j2 From 4bdc3c49a2b0f13a67e0ddcdd7a03d532be30e6c Mon Sep 17 00:00:00 2001 From: Martin van Es Date: Thu, 16 Apr 2026 13:38:58 +0200 Subject: [PATCH 08/15] Remove SBS cert dir --- roles/sram_sbs/defaults/main.yml | 1 - roles/sram_sbs/tasks/main.yml | 89 +++++++++++++------------------- 2 files changed, 35 insertions(+), 55 deletions(-) diff --git a/roles/sram_sbs/defaults/main.yml b/roles/sram_sbs/defaults/main.yml index b564e0779..fe8696869 100644 --- a/roles/sram_sbs/defaults/main.yml +++ b/roles/sram_sbs/defaults/main.yml @@ -13,7 +13,6 @@ sbs_git_dir: "{{ sbs_work_dir }}/sbs" sbs_env_dir: "{{ sbs_work_dir }}/sbs-env" sbs_conf_dir: "{{ sbs_work_dir }}/config" sbs_log_dir: "{{ sbs_work_dir }}/log" -sbs_cert_dir: "{{ sbs_work_dir }}/cert" sbs_apache_conf: "{{ sbs_work_dir }}/sbs.conf" sbs_nginx_conf: "{{ sbs_work_dir }}/nginx.conf" diff --git a/roles/sram_sbs/tasks/main.yml b/roles/sram_sbs/tasks/main.yml index f5f16e117..c2cae754f 100644 --- a/roles/sram_sbs/tasks/main.yml +++ b/roles/sram_sbs/tasks/main.yml @@ -37,7 +37,6 @@ - { path: "{{sbs_conf_dir}}", mode: "0755" } - { path: "{{sbs_conf_dir}}/saml", mode: "0755" } - { path: "{{sbs_log_dir}}", mode: "0775" } - - { path: "{{sbs_cert_dir}}", mode: "0755" } - name: "Fix file permissions" file: @@ -52,13 +51,6 @@ - "sbs.log" - "sbs.debug.log" -# Create dummy file in certs dir to pacify container pre-init script -# https://github.com/SURFscz/SBS/pull/2312 -- name: "Touch file in {{ sbs_cert_dir }}" - ansible.builtin.copy: - content: "" - dest: "{{sbs_cert_dir}}/dummy" - - name: "Create SBS config files" template: src: "{{item.name}}.j2" @@ -82,54 +74,44 @@ - "{{ sbs_server_image }}" register: "sbs_image" -# We need to remove sram-static so it gets repopulated -# with new SBS image static content -- name: "Clean up old containers" +- name: "Migration" + # For some reason --check breaks this block + when: "sbs_image is changed and not ansible_check_mode" block: - - name: "Stop and remove sbs and sbs-server containers" + - name: "Run SBS migrations" + throttle: 1 community.docker.docker_container: - name: "{{ item }}" - state: "absent" - with_items: - - "sbs-client" - - "sbs-server" - when: "sbs_image is changed" - -- name: "Run SBS migrations" - throttle: 1 - community.docker.docker_container: - name: "sram-sbs-migration" - image: "{{ sbs_server_image }}" - pull: "never" - state: "started" - restart_policy: "no" - detach: false - env: - RUNAS_UID: "{{ sbs_user_uid | string }}" - RUNAS_GID: "{{ sbs_group_gid | string }}" - MIGRATIONS_ONLY: "1" - # don't actually run the server - command: "/bin/true" - volumes: - - "{{ sbs_conf_dir }}:/sbs-config" - - "{{ sbs_cert_dir }}:/sbs-config/cert:ro" - - "{{ sbs_log_dir }}:/opt/sbs/log" - networks: - # TODO: Should we parametrize this? - - name: "loadbalancer" - register: "result" - failed_when: "'container' not in result or result.container.State.ExitCode != 0" - changed_when: "'[alembic.runtime.migration] Running upgrade' in result.container.Output | default('')" - notify: "Restart sbs containers" + name: "sram-sbs-migration" + image: "{{ sbs_server_image }}" + pull: "never" + state: "started" + restart_policy: "no" + detach: false + env: + RUNAS_UID: "{{ sbs_user_uid | string }}" + RUNAS_GID: "{{ sbs_group_gid | string }}" + MIGRATIONS_ONLY: "1" + # don't actually run the server + command: "/bin/true" + volumes: + - "{{ sbs_conf_dir }}:/sbs-config" + - "{{ sbs_log_dir }}:/opt/sbs/log" + networks: + # TODO: Should we parametrize this? + - name: "loadbalancer" + register: "result" + failed_when: "'container' not in result or result.container.State.ExitCode != 0" + changed_when: "'[alembic.runtime.migration] Running upgrade' in result.container.Output | default('')" + notify: "Restart sbs containers" -# Remove the migration container; we can do that with auto_remove, because if we use that, ansible -# will not save the output in result -- name: "Remove migration container" - community.docker.docker_container: - name: "sram-sbs-migration" - state: "absent" - # TODO: fix this by only running this if "sbs_image is changed" - changed_when: false + # Remove the migration container; we can do that with auto_remove, because if we use that, ansible + # will not save the output in result + - name: "Remove migration container" + community.docker.docker_container: + name: "sram-sbs-migration" + state: "absent" + # TODO: fix this by only running this if "sbs_image is changed" + changed_when: false - name: "Start sbs client container" community.docker.docker_container: @@ -163,7 +145,6 @@ pull: "never" volumes: - "{{ sbs_conf_dir }}:/sbs-config" - - "{{ sbs_cert_dir }}:/sbs-config/cert:ro" - "{{ sbs_log_dir }}:/opt/sbs/log" - "/tmp/ci-runner:/tmp/ci-runner" networks: From 891a1dd74667d3df3e76d7928c438a3c3b4fa621 Mon Sep 17 00:00:00 2001 From: Martin van Es Date: Tue, 21 Apr 2026 14:51:59 +0200 Subject: [PATCH 09/15] Add Engine-SBS integration --- roles/engine/defaults/main.yml | 1 - roles/engine/tasks/main.yml | 2 +- roles/engine/templates/parameters.yml.j2 | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/roles/engine/defaults/main.yml b/roles/engine/defaults/main.yml index 3721e2f54..ca58ea135 100644 --- a/roles/engine/defaults/main.yml +++ b/roles/engine/defaults/main.yml @@ -78,7 +78,6 @@ engine_stepup_gateway_sfo_entity_id: "https://{{ engine_stepup_gateway_domain }} engine_stepup_gateway_sfo_sso_location: "https://{{ engine_stepup_gateway_domain }}/second-factor-only/single-sign-on" # SBS interrupt settings -engine_sbs_base_url: "sbs.{{ base_domain }}" engine_sbs_attributes_allowed: - 'urn:mace:dir:attribute-def:eduPersonEntitlement' - 'urn:mace:dir:attribute-def:uid' diff --git a/roles/engine/tasks/main.yml b/roles/engine/tasks/main.yml index c75ece132..2776c4b4c 100644 --- a/roles/engine/tasks/main.yml +++ b/roles/engine/tasks/main.yml @@ -208,7 +208,7 @@ PHP_MEMORY_LIMIT: "{{ engine_php_memory }}" APP_ENV: "prod" APP_SECRET: "{{ engine_parameters_secret }}" - APP_DEBUG: "{{ engine_debug | bool | int }}" + APP_DEBUG: "{{ engine_debug | bool | int | string }}" etc_hosts: host.docker.internal: host-gateway mounts: diff --git a/roles/engine/templates/parameters.yml.j2 b/roles/engine/templates/parameters.yml.j2 index 526104405..0c0d077f5 100644 --- a/roles/engine/templates/parameters.yml.j2 +++ b/roles/engine/templates/parameters.yml.j2 @@ -316,8 +316,8 @@ parameters: ########################################################################################## ## SBS external authorization/attribute enrichtment ########################################################################################## - sram.api_token: "{{ engine_sbs_api_token | default('') }}" - sram.base_url: "https://{{ engine_sbs_base_url }}/api/users/" + sram.api_token: "{{ sbs_engine_block_api_token | default('') }}" + sram.base_url: "https://{{ sbs_base_domain | default('sbs.example.org') }}/api/users/" sram.authz_location: "authz_eb" sram.attributes_location: "attributes_eb" sram.interrupt_location: "interrupt" From d69f6bf6eb08d618c726047beebeeaeb9d9ed07a Mon Sep 17 00:00:00 2001 From: Martin van Es Date: Thu, 30 Apr 2026 15:12:22 +0200 Subject: [PATCH 10/15] Remove rsyslog dust WIP --- roles/sram_ldap/defaults/main.yml | 50 ++-- roles/sram_ldap/files/logrotate_slapd | 13 - roles/sram_ldap/files/rsyslog_slapd.conf | 2 - roles/sram_ldap/handlers/main.yml | 20 +- roles/sram_ldap/tasks/admins.yml | 56 ++-- roles/sram_ldap/tasks/main.yml | 174 ++++-------- roles/sram_ldap/templates/slapd.service.j2 | 20 -- roles/sram_metadata/defaults/main.yml | 78 ------ roles/sram_metadata/files/01_idps.fd | 23 -- roles/sram_metadata/files/02_backend.fd | 14 - roles/sram_metadata/files/03_frontend.fd | 14 - roles/sram_metadata/files/surf.png | Bin 16016 -> 0 bytes roles/sram_metadata/files/surf.svg | 24 -- roles/sram_metadata/files/surf_bimi.svg | 15 - roles/sram_metadata/files/surfconext.crt | 3 - roles/sram_metadata/files/transform.xslt | 47 ---- .../sram_metadata/files/transform_proxy.xslt | 50 ---- roles/sram_metadata/handlers/main.yml | 19 -- roles/sram_metadata/tasks/http.yml | 48 ---- roles/sram_metadata/tasks/main.yml | 49 ---- roles/sram_metadata/tasks/pyff.yml | 106 -------- roles/sram_metadata/templates/index.html.j2 | 11 - .../templates/pyff-metadata.service.j2 | 12 - .../templates/pyff-metadata.timer.j2 | 8 - roles/sram_metadata/templates/vhosts.conf.j2 | 15 - roles/sram_midproxy/defaults/main.yml | 13 +- roles/sram_midproxy/tasks/main.yml | 18 +- roles/sram_plsc/defaults/main.yml | 22 +- roles/sram_plsc/handlers/main.yml | 12 - roles/sram_plsc/tasks/main.yml | 10 +- roles/sram_plsc/templates/plsc.yml.j2 | 24 +- roles/sram_redis/defaults/main.yml | 16 +- roles/sram_redis/tasks/main.yml | 28 +- roles/sram_redis/templates/redis.conf.j2 | 4 +- roles/sram_sbs/defaults/main.yml | 257 +++++++++--------- roles/sram_sbs/tasks/main.yml | 56 ++-- roles/sram_sbs/templates/alembic.ini.j2 | 2 +- roles/sram_sbs/templates/config.yml.j2 | 178 ++++++------ roles/sram_sbs/templates/disclaimer.css.j2 | 4 +- .../templates/saml_advanced_settings.json.j2 | 2 +- .../sram_sbs/templates/saml_settings.json.j2 | 14 +- roles/sram_sbs/templates/sbs-apache.conf.j2 | 2 +- roles/sram_sbs/templates/sbs.service.j2 | 32 --- 43 files changed, 420 insertions(+), 1145 deletions(-) delete mode 100644 roles/sram_ldap/files/logrotate_slapd delete mode 100644 roles/sram_ldap/files/rsyslog_slapd.conf delete mode 100644 roles/sram_ldap/templates/slapd.service.j2 delete mode 100644 roles/sram_metadata/defaults/main.yml delete mode 100644 roles/sram_metadata/files/01_idps.fd delete mode 100644 roles/sram_metadata/files/02_backend.fd delete mode 100644 roles/sram_metadata/files/03_frontend.fd delete mode 100644 roles/sram_metadata/files/surf.png delete mode 100644 roles/sram_metadata/files/surf.svg delete mode 100644 roles/sram_metadata/files/surf_bimi.svg delete mode 100644 roles/sram_metadata/files/surfconext.crt delete mode 100644 roles/sram_metadata/files/transform.xslt delete mode 100644 roles/sram_metadata/files/transform_proxy.xslt delete mode 100644 roles/sram_metadata/handlers/main.yml delete mode 100644 roles/sram_metadata/tasks/http.yml delete mode 100644 roles/sram_metadata/tasks/main.yml delete mode 100644 roles/sram_metadata/tasks/pyff.yml delete mode 100644 roles/sram_metadata/templates/index.html.j2 delete mode 100644 roles/sram_metadata/templates/pyff-metadata.service.j2 delete mode 100644 roles/sram_metadata/templates/pyff-metadata.timer.j2 delete mode 100644 roles/sram_metadata/templates/vhosts.conf.j2 delete mode 100644 roles/sram_sbs/templates/sbs.service.j2 diff --git a/roles/sram_ldap/defaults/main.yml b/roles/sram_ldap/defaults/main.yml index cb2bc981a..35d5029f3 100644 --- a/roles/sram_ldap/defaults/main.yml +++ b/roles/sram_ldap/defaults/main.yml @@ -1,38 +1,38 @@ --- -ldap_image: "ghcr.io/surfscz/sram-ldap:main" -ldap_conf_dir: "{{ current_release_appdir }}/sram/ldap" -ldap_ldif_dir: "{{ ldap_conf_dir }}/schema" -ldap_certs_dir: "{{ ldap_conf_dir }}/certs" -ldap_backup_dir: "{{ ldap_conf_dir }}/ldap" -ldap_data_dir: "{{ ldap_conf_dir}}/data" -ldap_uri: "ldap://localhost/" +sram_ldap_image: "ghcr.io/surfscz/sram-ldap:main" +sram_ldap_conf_dir: "{{ current_release_appdir }}/sram/ldap" +sram_ldap_ldif_dir: "{{ sram_ldap_conf_dir }}/schema" +sram_ldap_certs_dir: "{{ sram_ldap_conf_dir }}/certs" +sram_ldap_backup_dir: "{{ sram_ldap_conf_dir }}/ldap" +sram_ldap_data_dir: "{{ sram_ldap_conf_dir}}/data" +sram_ldap_uri: "ldap://localhost/" -ldap_user: "openldap" -ldap_group: "openldap" +sram_ldap_user: "openldap" +sram_ldap_group: "openldap" # admin_group: "ldap_admin" -ldap_admins: +sram_ldap_admins: - name: Admin uid: admin - pw_hash: + pw_hash: "!" sshkey: "" -ldap_loglevel: "stats stats2 filter" +sram_ldap_loglevel: "stats stats2 filter" -ldap_services_password: secret -ldap_monitor_password: secret -ldap_ldap_monitor_password: secret +sram_ldap_services_password: secret +sram_ldap_monitor_password: secret +sram_ldap_ldap_monitor_password: secret -ldap_uri: "ldap://localhost/" -ldap_rid_prefix: "ldap://" +sram_ldap_uri: "ldap://localhost/" +sram_ldap_rid_prefix: "ldap://" -ldap_base_domain: "{{ base_domain }}" -ldap_base_dn: >- - {{ ((ldap_base_domain.split('.')|length)*['dc=']) | - zip(ldap_base_domain.split('.')) | list | map('join', '') | list | join(',') }} -ldap_services_dn: - basedn: "dc=services,{{ ldap_base_dn }}" +sram_ldap_base_domain: "{{ base_domain }}" +sram_ldap_base_dn: >- + {{ ((sram_ldap_base_domain.split('.')|length)*['dc=']) | + zip(sram_ldap_base_domain.split('.')) | list | map('join', '') | list | join(',') }} +sram_ldap_services_dn: + basedn: "dc=services,{{ sram_ldap_base_dn }}" o: "Services" - binddn: "cn=admin,{{ ldap_base_dn }}" + binddn: "cn=admin,{{ sram_ldap_base_dn }}" -ldap_hosts: {} +sram_ldap_hosts: {} diff --git a/roles/sram_ldap/files/logrotate_slapd b/roles/sram_ldap/files/logrotate_slapd deleted file mode 100644 index f225a935f..000000000 --- a/roles/sram_ldap/files/logrotate_slapd +++ /dev/null @@ -1,13 +0,0 @@ -/var/log/slapd.log -{ - rotate 7 - daily - missingok - notifempty - delaycompress - compress - postrotate - invoke-rc.d rsyslog rotate > /dev/null - endscript -} - diff --git a/roles/sram_ldap/files/rsyslog_slapd.conf b/roles/sram_ldap/files/rsyslog_slapd.conf deleted file mode 100644 index a3435617f..000000000 --- a/roles/sram_ldap/files/rsyslog_slapd.conf +++ /dev/null @@ -1,2 +0,0 @@ -if $programname == 'slapd' then /var/log/slapd.log -if $programname == 'slapd' then ~ diff --git a/roles/sram_ldap/handlers/main.yml b/roles/sram_ldap/handlers/main.yml index 0510176a6..f6136cfeb 100644 --- a/roles/sram_ldap/handlers/main.yml +++ b/roles/sram_ldap/handlers/main.yml @@ -1,24 +1,6 @@ --- -- name: restart rsyslog - service: - name: rsyslog - state: restarted - listen: "restart rsyslog" - -- name: systemd daemon-reload - systemd: - name: slapd - daemon_reload: yes - -- name: restart LDAP - systemd: - name: slapd - state: restarted - enabled: true - daemon-reload: true - - name: Restart the ldap container community.docker.docker_container: - name: "{{ containers.ldap }}" + name: "sram-ldap" restart: true state: started diff --git a/roles/sram_ldap/tasks/admins.yml b/roles/sram_ldap/tasks/admins.yml index dfba23d6e..f20648460 100644 --- a/roles/sram_ldap/tasks/admins.yml +++ b/roles/sram_ldap/tasks/admins.yml @@ -1,54 +1,34 @@ --- -# - name: Initialize DIT admin -# community.general.ldap_entry: -# dn: "{{ ldap_services_dn.binddn }}" -# objectClass: organizationalRole -# attributes: -# cn: "{{ ldap_services_dn.binddn | regex_replace('^cn=([^,]+).*', '\\1') }}" - -# determine which users need to be admin -# check for each role of each user if it leads to membership of group {{ldap_admin_group}} -# - name: determine ldap admins -# set_fact: -# ldap_admins: "{{ ldap_admins | default([]) + [item.0] }}" -# when: ldap_admin_group in role_to_groups[item.1] or ldap_admin_group in item.0.groups -# loop: "{{ users | subelements('roles') }}" - - name: determine ldap admins set_fact: - ldap_admins: "{{ ldap_admins }}" + ldap_admins: "{{ sram_ldap_admins }}" # Find existing ldap admins - name: Initialize admins (I) community.general.ldap_search: - dn: "{{ ldap_services_dn.basedn }}" + dn: "{{ sram_ldap_services_dn.basedn }}" scope: "onelevel" filter: "(objectClass=organizationalRole)" attrs: - "cn" - bind_dn: "{{ ldap_services_dn.binddn }}" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ldap_uri }}" + bind_dn: "{{ sram_ldap_services_dn.binddn }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{sram_ldap_uri }}" register: "existing_ldap_admins_result" -# ansible sucks like this: we need to extract the results from the result +# ansible trips over stuff like this: we need to extract the results from the result - name: Initialize admins (Ia) set_fact: existing_ldap_admins: "{{ existing_ldap_admins_result.results }}" -- debug: - var: "existing_ldap_admins" -- debug: - var: "ldap_admins" - # Remove LDAP non-admins - name: Initialize admins (II) community.general.ldap_entry: - dn: "cn={{ item.cn }},{{ ldap_services_dn.basedn }}" + dn: "cn={{ item.cn }},{{ sram_ldap_services_dn.basedn }}" state: absent - bind_dn: "{{ ldap_services_dn.binddn }}" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ldap_uri }}" + bind_dn: "{{ sram_ldap_services_dn.binddn }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" when: > item.cn not in ldap_admins | map(attribute='uid') and item.cn != 'admin' @@ -57,26 +37,26 @@ # Insert LDAP admins - name: Initialize admins (III) community.general.ldap_entry: - dn: "cn={{ item.uid }},{{ ldap_services_dn.basedn }}" + dn: "cn={{ item.uid }},{{ sram_ldap_services_dn.basedn }}" objectClass: - simpleSecurityObject - organizationalRole attributes: description: An LDAP administrator userPassword: "{{ item.pw_hash }}" - bind_dn: "{{ ldap_services_dn.binddn }}" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ldap_uri }}" + bind_dn: "{{ sram_ldap_services_dn.binddn }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" loop: "{{ ldap_admins }}" # Make sure passwords are updated for existing admins - name: Initialize admins (IV) community.general.ldap_attrs: - dn: "cn={{ item.uid }},{{ ldap_services_dn.basedn }}" + dn: "cn={{ item.uid }},{{ sram_ldap_services_dn.basedn }}" attributes: userPassword: "{{ item.pw_hash }}" - bind_dn: "{{ ldap_services_dn.binddn }}" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ldap_uri }}" + bind_dn: "{{ sram_ldap_services_dn.binddn }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" loop: "{{ ldap_admins }}" diff --git a/roles/sram_ldap/tasks/main.yml b/roles/sram_ldap/tasks/main.yml index a4877722d..318e3b340 100644 --- a/roles/sram_ldap/tasks/main.yml +++ b/roles/sram_ldap/tasks/main.yml @@ -11,19 +11,17 @@ file: path: "{{ item.path }}" state: "directory" - # owner: "{{ ldap_user }}" - # group: "{{ ldap_group }}" mode: "{{ item.mode }}" with_items: - - { path: "{{ldap_ldif_dir}}", mode: "0755" } - - { path: "{{ldap_certs_dir}}", mode: "0755" } - - { path: "{{ldap_data_dir}}", mode: "0777" } + - { path: "{{sram_ldap_ldif_dir}}", mode: "0755" } + - { path: "{{sram_ldap_certs_dir}}", mode: "0755" } + - { path: "{{sram_ldap_data_dir}}", mode: "0777" } notify: Restart the ldap container - name: Copy schemas copy: src: "{{ item }}" - dest: "{{ ldap_ldif_dir }}/{{ item }}" + dest: "{{ sram_ldap_ldif_dir }}/{{ item }}" mode: "0644" with_items: - sczGroup.ldif @@ -38,51 +36,39 @@ - name: Copying ldap-add script copy: src: "{{ item }}" - dest: "{{ ldap_conf_dir }}/{{ item }}" + dest: "{{ sram_ldap_conf_dir }}/{{ item }}" mode: "0755" with_items: - ldap-add -# # cert is used for communication between ldap for sync -# # is generated in roles/certificates/tasks/main.yml -# - name: Copy wildcard frontend cert -# copy: -# src: "/etc/ssl/certs/sram-https.pem" # was installed here by update-ca-certificates -# remote_src: true -# dest: "{{ldap_certs_dir}}/frontend.crt" -# mode: "0644" -# when: "is_dev" -# notify: Restart the ldap container - - name: Setup ldap hosts vars: host: - key: "%s.{{ ldap_base_domain }}" + key: "%s.{{ sram_ldap_base_domain }}" value: "%s" etc_hosts: {} set_fact: etc_hosts: >- {{ etc_hosts | combine({ host.key | format(item.key): host.value | format(item.value) }) }} - with_dict: "{{ ldap_hosts }}" + with_dict: "{{ sram_ldap_hosts }}" - name: Create the ldap container community.docker.docker_container: name: "sram-ldap" - image: "{{ ldap_image }}" + image: "{{ sram_ldap_image }}" restart_policy: "always" state: started - pull: true + # pull: true ports: - 0.0.0.0:389:389 env: LDAP_ORGANISATION: "{{ env }}" - LDAP_DOMAIN: "{{ ldap_base_domain }}" - LDAP_ROOTPASS: "{{ ldap_services_password }}" + LDAP_DOMAIN: "{{ sram_ldap_base_domain }}" + LDAP_ROOTPASS: "{{ sram_ldap_services_password }}" etc_hosts: "{{ etc_hosts }}" volumes: - # For now the target side /opt/ldap is hard-coded - - "{{ ldap_conf_dir }}:/opt/ldap" + - "{{ sram_ldap_conf_dir }}:/opt/ldap" networks: - name: "loadbalancer" labels: @@ -92,11 +78,11 @@ traefik.tcp.routers.ldap.tls: "true" traefik.tcp.services.ldap.loadbalancer.server.port: "389" healthcheck: - test: - - "CMD" - - "bash" - - "-c" - - "[[ -S /var/run/slapd/ldapi ]]" + test: "-S /var/run/slapd/ldapi" + # - "CMD" + # - "bash" + # - "-c" + # - "[[ -S /var/run/slapd/ldapi ]]" register: "ldap_container" - name: Wait for LDAP initialization @@ -111,7 +97,6 @@ - name: Ensure the schemas are added to LDAP ansible.builtin.shell: - # For now the target side /opt/ldap is hard-coded cmd: "docker exec sram-ldap /opt/ldap/ldap-add /opt/ldap/schema/{{ item }}" register: "result" failed_when: "result.rc not in [0,80]" @@ -133,8 +118,8 @@ olcDbIndex: "{{item}}" state: "present" bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ ldap_uri }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" with_items: - "entryUUID eq" - "o eq" @@ -148,33 +133,21 @@ attributes: olcSizeLimit: "unlimited" bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ ldap_uri }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" - name: Set config community.general.ldap_attrs: dn: "cn=config" state: "present" attributes: - olcServerID: "{{ ldap_server_id }}" + olcServerID: "{{ sram_ldap_server_id }}" olcSizeLimit: "unlimited" - olcLogLevel: "{{ ldap_loglevel }}" + olcLogLevel: "{{ sram_ldap_loglevel }}" olcAttributeOptions: "time-" bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ ldap_uri }}" - -# # cert is used for communication between ldap for sync -# # is generated in roles/certificates/tasks/main.yml -# - name: Set TLS config -# community.general.ldap_attrs: -# dn: "cn=config" -# state: "exact" -# attributes: -# olcTLSCACertificateFile: "/opt/ldap/certs/frontend.crt" -# bind_dn: "cn=admin,cn=config" -# bind_pw: "{{ ldap_services_password }}" -# server_uri: "{{ ldap_uri }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" - name: Setup Modules community.general.ldap_attrs: @@ -184,8 +157,8 @@ - syncprov - dynlist.so bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ ldap_uri }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" - name: Setup Dynlist community.general.ldap_entry: @@ -196,8 +169,8 @@ attributes: olcDlAttrSet: "voPerson labeledURI member+memberOf@groupOfMembers" bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ ldap_uri }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" - name: Setup Syncprov community.general.ldap_entry: @@ -209,51 +182,8 @@ olcSpCheckpoint: 100 10 olcSpSessionLog: 100 bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ ldap_uri }}" - -# Leave this here in case we do want to build our own -# root database from scratch instead of relying on the -# domain based Debian slapd package setup -# -# - name: Setup main database -# community.general.ldap_attrs: -# dn: olcDatabase={1}mdb,cn=config -# attributes: -# olcSuffix: "{{ ldap_services_dn.basedn }}" -# olcRootDN: "{{ ldap_services_dn.binddn }}" -# olcRootPW: "{{ '%s' | format(services_ldap_password) | slapd_hash }}" -# state: exact -# -# - name: Set root credentials -# community.general.ldap_attrs: -# dn: olcDatabase={0}config,cn=config -# attributes: -# olcAccess: >- -# {0}to * -# by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage -# {% if environment_name=="vm" %} -# by dn.exact=gidNumber=1000+uidNumber=1000,cn=peercred,cn=external,cn=auth manage -# {% endif %} -# by dn.exact="{{ ldap_services_dn.binddn }}" manage -# by * break -# state: exact -# bind_dn: "cn=admin,cn=config" -# bind_pw: "{{ ldap_services_password }}" -# server_uri: "{{ ldap_uri }}" -# -# -# - name: Get uid of openldap user -# ansible.builtin.getent: -# database: "passwd" -# key: "openldap" -# -# # store it in a nice var (so line below doesn't get too long) -# - name: Save gid of openldap user -# ansible.builtin.set_fact: -# openldap_auth: "gidNumber={{ansible_facts.getent_passwd['openldap'][2]}}+\ -# uidNumber={{ansible_facts.getent_passwd['openldap'][1]}}" -# + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" - name: Set ACLs community.general.ldap_attrs: @@ -261,15 +191,15 @@ attributes: olcAccess: - >- - to dn.regex="(([^,]+),{{ ldap_services_dn.basedn }})$" - by dn.exact="{{ ldap_services_dn.binddn }}" write + to dn.regex="(([^,]+),{{ sram_ldap_services_dn.basedn }})$" + by dn.exact="{{ sram_ldap_services_dn.binddn }}" write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth write by dn.exact,expand="cn=admin,$1" read by * break - >- to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage - by dn.regex="cn=[^,]+,{{ ldap_services_dn.basedn }}" read + by dn.regex="cn=[^,]+,{{ sram_ldap_services_dn.basedn }}" read {% if env=="vm" %} by dn.exact=gidNumber=1000+uidNumber=1000,cn=peercred,cn=external,cn=auth manage {% endif %} @@ -282,10 +212,10 @@ state: exact ordered: true bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ ldap_uri }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" -# ldap_rids: +# sram_ldap_rids: # 101: ldaps://ldap1.scz-vm.net/ # 102: ldaps://ldap2.scz-vm.net/ @@ -294,14 +224,14 @@ start: 101 rid: key: "%d" - value: "{{ ldap_rid_prefix }}%s.{{ ldap_base_domain }}/" + value: "{{ sram_ldap_rid_prefix }}%s.{{ sram_ldap_base_domain }}/" ldap_rids: {} set_fact: ldap_rids: >- {{ ldap_rids | combine({ rid.key | format(start|int): rid.value | format(item.key) }) }} start: "{{ start|int + 1 }}" - with_dict: "{{ ldap_hosts | dict2items | sort(attribute='key') }}" + with_dict: "{{ sram_ldap_hosts | dict2items | sort(attribute='key') }}" # Voor toekomstige Claude gebruikers: onderstaande construct levert aan het eind # een string representatie van de dict op, die niet meer gebruikt kan worden @@ -321,11 +251,11 @@ rid: >- rid={} provider="{}" - searchbase="{{ ldap_services_dn.basedn }}" + searchbase="{{ sram_ldap_services_dn.basedn }}" type=refreshAndPersist bindmethod=simple - binddn="{{ ldap_services_dn.binddn }}" - credentials={{ ldap_services_password }} + binddn="{{ sram_ldap_services_dn.binddn }}" + credentials={{ sram_ldap_services_password }} retry="30 +" timeout=30 network-timeout=5 @@ -341,29 +271,29 @@ olcSyncrepl: "{{ rids }}" olcMultiProvider: "TRUE" bind_dn: "cn=admin,cn=config" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ ldap_uri }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" # We now have Syncrepl in place, so only write to primary - name: Initialize DIT community.general.ldap_entry: - dn: "{{ ldap_services_dn.basedn }}" + dn: "{{ sram_ldap_services_dn.basedn }}" state: "present" objectClass: - "top" - "dcObject" - "organization" attributes: - dc: "{{ ldap_services_dn.basedn | regex_replace('^dc=([^,]+).*', '\\1') }}" - o: "{{ ldap_services_dn.o }}" - bind_dn: "{{ ldap_services_dn.binddn }}" - bind_pw: "{{ ldap_services_password }}" - server_uri: "{{ ldap_uri }}" + dc: "{{ sram_ldap_services_dn.basedn | regex_replace('^dc=([^,]+).*', '\\1') }}" + o: "{{ sram_ldap_services_dn.o }}" + bind_dn: "{{ sram_ldap_services_dn.binddn }}" + bind_pw: "{{ sram_ldap_services_password }}" + server_uri: "{{ sram_ldap_uri }}" when: > - inventory_hostname in groups['ldap_primary'] + inventory_hostname in groups['sram_ldap_primary'] # We now have Syncrepl in place, so only write to primary - name: Add ldap admins include_tasks: "admins.yml" when: > - inventory_hostname in groups['ldap_primary'] + inventory_hostname in groups['sram_ldap_primary'] diff --git a/roles/sram_ldap/templates/slapd.service.j2 b/roles/sram_ldap/templates/slapd.service.j2 deleted file mode 100644 index 299b3af0c..000000000 --- a/roles/sram_ldap/templates/slapd.service.j2 +++ /dev/null @@ -1,20 +0,0 @@ -[Unit] -Description = LDAP server - -[Service] -Type = forking -User = root -SupplementaryGroups = ssl-cert -ExecStartPre=-/bin/mkdir -p /var/run/slapd -ExecStartPre=-/bin/chown openldap: /var/run/slapd -ExecStart = /usr/sbin/slapd -F /etc/ldap/slapd.d -u openldap -g openldap -h 'ldapi:/// ldap://localhost/ ldaps://{{inventory_hostname}}/' -Restart = always -RestartSec = 30 -PIDFile = /run/slapd/slapd.pid -# defaults are 1024:524288 which is too small for slapd -# see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=378261 and -# https://www.openldap.org/lists/openldap-software/200802/msg00186.html -LimitNOFILE=4096:524288 - -[Install] -WantedBy = multi-user.target diff --git a/roles/sram_metadata/defaults/main.yml b/roles/sram_metadata/defaults/main.yml deleted file mode 100644 index 7982c7f9d..000000000 --- a/roles/sram_metadata/defaults/main.yml +++ /dev/null @@ -1,78 +0,0 @@ ---- -sram_metadata_image_server: "ghcr.io/openconext/openconext-basecontainers/apache2:latest" -sram_metadata_image_pyff: "ghcr.io/surfscz/sram-pyff:main" -sram_metadata_hostname: "meta.{{ base_domain }}" -sram_metadata_basedir: "{{current_release_appdir}}/sram/metadata" - -# server_name: "metadata-server" - -sram_metadata_user: "sram-metadata" -sram_metadata_group: "sram-metadata" - -# idps_source: "https://metadata.surfconext.nl/idps-metadata.xml" -# idps_cert: | -# -----BEGIN CERTIFICATE----- -# MIIEKjCCAhICEG12w6QqayYAWntxDN59dU0wDQYJKoZIhvcNAQELBQAwPDELMAkG -# A1UEBhMCTkwxEDAOBgNVBAoMB1NVUkZuZXQxGzAZBgNVBAMMElNVUkZjb25leHQg -# Um9vdCBDQTAeFw0xOTAxMTQxNjM5MDVaFw0yNDAxMTgxNjM5MDVaMGsxCzAJBgNV -# BAYTAk5MMRAwDgYDVQQIDAdVdHJlY2h0MRAwDgYDVQQKDAdTVVJGbmV0MRMwEQYD -# VQQLDApTVVJGY29uZXh0MSMwIQYDVQQDDBpTVVJGY29uZXh0IG1ldGFkYXRhIHNp -# Z25lcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMckFyqXzW7dbMt4 -# wDdSLaAjFAbNziUgQaivu4dl9Uf/cZ4f36a9DfQBUSraNoIR76ruwK3TPfFalemp -# xmWTsoVSQpb3AOsWbU+i0YKS1cmcqMUC1fef2j1IbuK4B4nEu9S5saGNVGNvUJ+Y -# jDUpC5vyyp7boW9E1md2jIBI6Mw+ZhlmkPucqaphxurWnm0KbxTZrYLOBZ1IXj6r -# yrRoFwwtjEH+CW8cRn8OATK0q4yb0BVr2gY2tp/lTpASHZ3WVWBK0prwK0KkusY6 -# ck+/vvlk46IdEr803NB0Dm3ECh3i65mfCaWzVTtd/md874paK+65f1JeVyd5I5al -# M2KEpvkCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAjvJXXkxOqh3K0k2NdDG5EOTy -# bA+koRbAqhdY/qJoSnqTzwBxJc6aPs+L4q2PIoLo0gNJj1Nm1taLusaaK+CBx3ar -# 1kxEika5FM0dqFjD3i7Y5U0FMeDB5cReo8TNdo31VGoY7CbRjtqHLRTuKzNmIfEm -# ahLnHIBtarE82b7Mpg0aLxjrRR+t8wSCriy+e9AEPzC5bWxtPJA+OhU8U9hMuOs5 -# SzKmHwYue4WY3q1rRaDpK3fqgXRDRfznNn9/RDDbBos7CRMSAPEmAO28qLKBW/1z -# a2TKQLddZ3uoCurFNbToSTueKYVEnveQNO2P5X6uy4rcYkjeSiwbmHo7jYuHAxx4 -# uGzHMpoqoGNx+2iYjtUo3dJUXzcZai3X+RuuMKXXvqGzrxJsoKayNVAE1dWoUHJl -# RouPhDLTdZq/pblORhFS8r10rKhSScgrNuN9LTTV7EPFeVr8trocNwl8IruH+eNL -# 6/7b5Y7fb7rvpxeHjWrTz8a9BXAIAv+bgyrg4OHGRcNIQb0XF438HD9r8Zb92B6Z -# VCR3aVS5496+1td+8aN/Blzo59LhKPiHyGZCPHFV/oBqG7nxp603kcWmJOcG+AgB -# 9bFiAimF5LLk/LnMfplK9w0vvxWVcdQkDgVPYvEGNtttj0QC7/jM4ZeihGb6Oyzy -# DZA6aeg73/ygOATQ13A= -# -----END CERTIFICATE----- -sram_metadata_idps_filters: [] - -sram_metadata_idps_files: -- name: "dummy-idp" - metadata: | - - - - - - - SRAM VM Dummy IdP - SRAM VM Dummy IdP - https://test-idp.sram.example.org/ - - - Administrator - mailto:sinterklaas@example.nl - - - -# idps_xrd: "{{metadata_defaults.basedir}}/certs/surfconext.xrd" -# idps_source_dir: "/opt/metadata-src" -# idps_feed: "{{ metadata_defaults.basedir }}/idps_feed.fd" -# idps_file: "idps.xml" -## -# proxy_frontend_source: "https://satosa.local/frontend.xml" -# proxy_frontend_feed: "{{ metadata_defaults.basedir }}/frontend_feed.fd" -# proxy_frontend_file: "proxy_idp.xml" -# -# proxy_backend_source: "https://satosa.local/metadata/backend.xml" -# proxy_backend_feed: "{{ metadata_defaults.basedir }}/backend_feed.fd" -# proxy_backend_file: "proxy_sp.xml" diff --git a/roles/sram_metadata/files/01_idps.fd b/roles/sram_metadata/files/01_idps.fd deleted file mode 100644 index 46d58b663..000000000 --- a/roles/sram_metadata/files/01_idps.fd +++ /dev/null @@ -1,23 +0,0 @@ -- load fail_on_error True: - #- "https://metadata.test.surfconext.nl/idps-metadata.xml verify certs/surfconext.crt" - - "https://metadata.test.surfconext.nl/idps-metadata.xml" - - "src/" -- select: - - "https://idp.diy.surfconext.nl/saml2/idp/metadata.php" - - "http://mock-idp" - - "https://login.test.eduid.nl" - - "https://idp-acc.surfnet.nl" - - "https://login.uaccess-a.leidenuniv.nl/nidp/saml2/metadata" - - "test-idp.lab.surf.nl" - - "https://test-idp.sram.surf.nl/saml/saml2/idp/metadata.php" - - "https://idp.ci-runner.sram.surf.nl/saml/saml2/idp/metadata.php" -- xslt: - stylesheet: "xslt/transform.xslt" -- finalize: - cacheDuration: P7D - validUntil: P14D -- sign: - key: "certs/signing.key" - cert: "certs/signing.crt" -- publish: "out/idps.xml.new" -- stats diff --git a/roles/sram_metadata/files/02_backend.fd b/roles/sram_metadata/files/02_backend.fd deleted file mode 100644 index 698d615a4..000000000 --- a/roles/sram_metadata/files/02_backend.fd +++ /dev/null @@ -1,14 +0,0 @@ -- load fail_on_error True: - - "https://proxy.acc.sram.eduteams.org/metadata/backend.xml" -- select -- xslt: - stylesheet: "xslt/transform_proxy.xslt" -- finalize: - cacheDuration: P7D - validUntil: P14D - name: "SURF Research Access Management" -- sign: - key: "certs/signing.key" - cert: "certs/signing.crt" -- publish: "out/proxy_sp.xml.new" -- stats diff --git a/roles/sram_metadata/files/03_frontend.fd b/roles/sram_metadata/files/03_frontend.fd deleted file mode 100644 index 252206d42..000000000 --- a/roles/sram_metadata/files/03_frontend.fd +++ /dev/null @@ -1,14 +0,0 @@ -- load fail_on_error True: - - "https://proxy.acc.sram.eduteams.org/metadata/frontend.xml" -- select -- xslt: - stylesheet: "xslt/transform_proxy.xslt" -- finalize: - cacheDuration: P7D - validUntil: P14D - name: "SURF Research Access Management" -- sign: - key: "certs/signing.key" - cert: "certs/signing.crt" -- publish: "out/proxy_idp.xml.new" -- stats diff --git a/roles/sram_metadata/files/surf.png b/roles/sram_metadata/files/surf.png deleted file mode 100644 index e2bc4a3c2b61cabcd3babac9f9b67ef8c860dfbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16016 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 - - - - - - - - - - - - - diff --git a/roles/sram_metadata/files/surf_bimi.svg b/roles/sram_metadata/files/surf_bimi.svg deleted file mode 100644 index f49b7a035..000000000 --- a/roles/sram_metadata/files/surf_bimi.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - surf - - - - - - - - - - - - diff --git a/roles/sram_metadata/files/surfconext.crt b/roles/sram_metadata/files/surfconext.crt deleted file mode 100644 index 0e8f074e8..000000000 --- a/roles/sram_metadata/files/surfconext.crt +++ /dev/null @@ -1,3 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFbjCCA1agAwIBAgIQagXJvtKqIRRO8zD41OktRjANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJOTDEQMA4GA1UEBwwHVXRyZWNodDEQMA4GA1UECAwHVXRyZWNodDESMBAGA1UECgwJU1VSRiBCLlYuMRMwEQYDVQQLDApTVVJGY29uZXh0MSAwHgYDVQQDDBdTVVJGY29uZXh0IENBIDIwMjMgVEVTVDAeFw0yMzA2MDcxMTQxNDRaFw0yNTA2MDYxMTQxNDRaMIGVMQswCQYDVQQGEwJOTDEQMA4GA1UEBwwHVXRyZWNodDEQMA4GA1UECAwHVXRyZWNodDESMBAGA1UECgwJU1VSRiBCLlYuMRMwEQYDVQQLDApTVVJGY29uZXh0MTkwNwYDVQQDDDBTVVJGY29uZXh0IHRlc3QgZW52aXJvbm1lbnQgbWV0YWRhdGEgc2lnbmVyIDIwMjMwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC1Wj1MYwzY646Wa9td4zUZb5W27+cbARhNbIZsteUIPV6unxoO6tHCLJhRxC4pBTQsdrhfhh3+s5rwm8mjhJs2rciQkCdPiTl860jqihhWi5bFXyGX5o1U5mZgomUT+o7+nUj0et1l/kbFJ0GqIKtf0uPj7R/zqTpqeT0c6VFxchU6LA8GOI9w5XIISEGi/IWlDKyM69I3DmbCip/rm8u6kIQ0qqXh58lNNOsZw8WYokCXP0IPFQWpPkKC1VGYtivwKLzzvNxSGcuvp39ui+37hrdjqiTxK68Z48vJ6l+KsJP+jpDXYBYE/NsSVYez3vbVTB/l664yvBfKyGIHHDdTq5akLCQDgYQzjeNOU1oSZbcsub0k+osp7MFGkslYRhLb0V9tX0Xu+7jXzGthPUWicN0XdlHS0JOlSgOBftPn8kcqYNMF0IZVe6V/AVgfj4/4iDk3OKl9FRctFp3kSa8GzLIbjqmYXpGGIEse6U2gfqHS9WHu4odfKH7rhD3hZssCAwEAAaNSMFAwHQYDVR0OBBYEFNclSgPTrGp4QJQZGjFu6VEBTX4PMB8GA1UdIwQYMBaAFI5kmzwW92s2rRY2B5NNjSYI2oj1MA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAORNL7FGBkeq6u/rmcNf+jZZz27vw86COPOiN6ygTyxaBq5fmJ4JZlDnlfO4C/4iek2QjKdgPlpvATGUUMXJdO6a7A3/vXNuoIGu3Ug9GW4vpTVPulaYZedPHC8zBsxwRKwxpSTda7ubWDxH3vUxHz/zDOD2O71O6KFj6Ph8JXwa3TLH0xRN5CXa0UMKX0S+ck8MahCYnMtd99EBL/uOr0+D4q2HwxDRDpL4I9yRwyWxCafoR+6OfzO/vc/SGcjEk/9s0DrMKDkDTJlE9eZbaaWFFCkAkg3LHHLMYjykcTvjDEV75OohYcEC5/6uKHcB/ZQjHwkPBqv9pUF897yZ7sxS66GEJmqqVIC+ayWRvC8N+UmvMGWAdohrY7r7CPeTE+iVHaeB7xGTSI9BhTEv3yMNHhqzqIOvgr8h5iCv7B5hQL+V7MRqD7e7X9uRR7wbyGmwT4p4VFbz5VqthCOFobsMxam9Axt+saebRyH6Mg3Ro9D5WgGoZmTP1yyiMrmEHQdf9+iblbfTbRW0irlaX5t58fWB1u4QZqcamlhVcl65Fub0g+QkSyGDMD9G57z3CKOluNy6TxFZOxMynY6CEtaozDaiETm7NaNC1lkhi+SOHKRX5+q0KqJdnEC7GOX69hSDsCT905dpVnr8JgFKoUfXWSmbwTMj45190dw7RMzk= ------END CERTIFICATE----- diff --git a/roles/sram_metadata/files/transform.xslt b/roles/sram_metadata/files/transform.xslt deleted file mode 100644 index 80673b688..000000000 --- a/roles/sram_metadata/files/transform.xslt +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roles/sram_metadata/files/transform_proxy.xslt b/roles/sram_metadata/files/transform_proxy.xslt deleted file mode 100644 index 6e6f55d09..000000000 --- a/roles/sram_metadata/files/transform_proxy.xslt +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - Security Response Team - mailto:securityincident@surf.nl - - - - - - - - - - - https://refeds.org/sirtfi2 - https://refeds.org/sirtfi - - - - - - diff --git a/roles/sram_metadata/handlers/main.yml b/roles/sram_metadata/handlers/main.yml deleted file mode 100644 index 2787eba12..000000000 --- a/roles/sram_metadata/handlers/main.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -# - name: "enable pyff-metadata job" -# systemd: -# name: "pyff-metadata.timer" -# enabled: true -# state: "started" -# daemon_reload: true - -# - name: "run pyff-metadata job" -# systemd: -# name: "pyff-metadata.service" -# state: "started" -# daemon_reload: true - -- name: Restart the pyFF container - community.docker.docker_container: - name: "sram-metadata-pyff" - restart: true - state: started diff --git a/roles/sram_metadata/tasks/http.yml b/roles/sram_metadata/tasks/http.yml deleted file mode 100644 index 561201096..000000000 --- a/roles/sram_metadata/tasks/http.yml +++ /dev/null @@ -1,48 +0,0 @@ ---- -- name: "Install index page" - template: - src: "index.html.j2" - dest: "{{sram_metadata_basedir}}/web/index.html" - mode: "0644" - -- name: "Install legacy link" - file: - src: "." - dest: "{{sram_metadata_basedir}}/web/metadata" - state: "link" - -- name: "Install logos" - copy: - src: "{{item}}" - dest: "{{sram_metadata_basedir}}/web" - mode: "0644" - with_items: - - "surf.svg" - - "surf.png" - - "surf_bimi.svg" - -- name: "Create the metadata-server container" - community.docker.docker_container: - name: "sram-metadata-server" - image: "{{ sram_metadata_image_server }}" - restart_policy: "always" - state: "started" - pull: true - mounts: - - source: "{{sram_metadata_basedir}}/web" - target: "/var/www/html" - type: "bind" - read_only: true - networks: - - name: "loadbalancer" - labels: - traefik.http.routers.metadata.rule: "Host(`{{ sram_metadata_hostname }}`)" - traefik.http.routers.metadata.tls: "true" - traefik.enable: "true" - healthcheck: - test: [ "CMD", "curl", "-fail", "http://localhost/" ] - interval: "10s" - timeout: "5s" - retries: 3 - start_period: "5s" - diff --git a/roles/sram_metadata/tasks/main.yml b/roles/sram_metadata/tasks/main.yml deleted file mode 100644 index 89c0ce9e9..000000000 --- a/roles/sram_metadata/tasks/main.yml +++ /dev/null @@ -1,49 +0,0 @@ ---- -- name: "Create metadata group" - group: - name: "{{ sram_metadata_group }}" - state: "present" - register: "result" - -- name: "Save metadata group gid" - set_fact: - metadata_group_gid: "{{ result.gid }}" - -- name: "Create metadata user" - user: - name: "{{ sram_metadata_user }}" - group: "{{ sram_metadata_group }}" - comment: "User to run metadata service" - shell: "/bin/false" - password: "!" - home: "{{ sram_metadata_basedir }}" - create_home: false - state: "present" - register: "result" - -- name: "Save metadata user uid" - set_fact: - metadata_user_uid: "{{ result.uid }}" - - -- name: "Create metadata directories" - file: - path: "{{ item.dir }}" - state: "directory" - mode: "{{ item.mode }}" - owner: "root" - group: "{{ sram_metadata_group }}" - with_items: - - { dir: "{{sram_metadata_basedir}}/web", mode: "0775" } - - { dir: "{{sram_metadata_basedir}}/feeds", mode: "0755" } - - { dir: "{{sram_metadata_basedir}}/src", mode: "0755" } - - { dir: "{{sram_metadata_basedir}}/certs", mode: "0755" } - - { dir: "{{sram_metadata_basedir}}/xslt", mode: "0755" } - notify: "Restart the pyFF container" - - -- name: "Start pyff container" - include_tasks: "pyff.yml" - -- name: "Start http container" - include_tasks: "http.yml" diff --git a/roles/sram_metadata/tasks/pyff.yml b/roles/sram_metadata/tasks/pyff.yml deleted file mode 100644 index 4e9b960d3..000000000 --- a/roles/sram_metadata/tasks/pyff.yml +++ /dev/null @@ -1,106 +0,0 @@ ---- -- name: "create self-signed Metadata Signing SSL certs" - shell: - cmd: ' - openssl genrsa -out "{{ sram_metadata_basedir }}/certs/signing.key" 2048; - openssl req -new -nodes -x509 -subj "/C=NL/CN=signing" - -days 3650 -key "{{ sram_metadata_basedir }}/certs/signing.key" - -out "{{ sram_metadata_basedir }}/certs/signing.crt" -extensions v3_ca; - chown {{sram_metadata_user}}:{{sram_metadata_group}} {{ sram_metadata_basedir }}/certs/*; - ' - creates: "{{ sram_metadata_basedir }}/certs/signing.crt" - when: "sram_metadata_signing_cert is not defined" - notify: "Restart the pyFF container" - -- name: "Write fixed Metadata signing certificates" - copy: - dest: "{{ sram_metadata_basedir }}/certs/{{ item.file }}" - content: "{{item.contents}}" - mode: "{{item.mode}}" - owner: "{{sram_metadata_user}}" - group: "{{sram_metadata_group}}" - with_items: - - { file: "signing.key", mode: "0640", contents: "{{sram_metadata_signing_cert.priv}}" } - - { file: "signing.crt", mode: "0644", contents: "{{sram_metadata_signing_cert.pub}}" } - when: "sram_metadata_signing_cert is defined" - notify: "Restart the pyFF container" - -- name: "Copy source certificates" - copy: - src: "{{ item }}" - dest: "{{ sram_metadata_basedir }}/certs" - mode: "0644" - with_items: - - "surfconext.crt" - notify: "Restart the pyFF container" - -- name: "Install IdP metadata" - copy: - content: "{{item.metadata}}" - dest: "{{ sram_metadata_basedir }}/src/{{item.name}}.xml" - mode: "0644" - with_items: "{{ sram_metadata_idps_files }}" - notify: "Restart the pyFF container" - -- name: "Copy pyFF xslt transformations" - copy: - src: "{{item}}" - dest: "{{sram_metadata_basedir}}/xslt" - mode: "0644" - with_items: - - "transform_proxy.xslt" - - "transform.xslt" - notify: "Restart the pyFF container" - -- name: "Copy pyFF feeds" - copy: - src: "{{item}}" - dest: "{{sram_metadata_basedir}}/feeds" - mode: "0644" - with_items: - - "01_idps.fd" - - "02_backend.fd" - - "03_frontend.fd" - notify: "Restart the pyFF container" - -- name: "Create the pyFF container" - community.docker.docker_container: - name: "sram-metadata-pyff" - image: "{{ sram_metadata_image_pyff }}" - restart_policy: "always" - state: "started" - pull: true - init: true - env: - USER: "{{ metadata_user_uid | string }}" - GROUP: "{{ metadata_group_gid | string }}" - mounts: - - source: "{{ sram_metadata_basedir }}/web" - target: "/opt/pyff/web" - type: "bind" - - source: "{{ sram_metadata_basedir }}/feeds" - target: "/opt/pyff/feeds" - type: "bind" - read_only: true - - source: "{{ sram_metadata_basedir }}/src" - target: "/opt/pyff/src" - type: "bind" - read_only: true - - source: "{{ sram_metadata_basedir }}/certs" - target: "/opt/pyff/certs" - type: "bind" - read_only: true - - source: "{{ sram_metadata_basedir }}/xslt" - target: "/opt/pyff/xslt" - type: "bind" - read_only: true - healthcheck: - test: - - "CMD" - - "bash" - - "-c" - - "[[ $(($(date +%s)-$(date -r /opt/pyff/web/idps.xml +%s))) -lt 400 ]]" - interval: "10s" - timeout: "5s" - retries: 3 - start_period: "5s" diff --git a/roles/sram_metadata/templates/index.html.j2 b/roles/sram_metadata/templates/index.html.j2 deleted file mode 100644 index f0e40b22b..000000000 --- a/roles/sram_metadata/templates/index.html.j2 +++ /dev/null @@ -1,11 +0,0 @@ - -SRAM - - -

SRAM metadata

-

SRAM IdP proxy metadata
-(for use by Service Providers)

-

SRAM SP proxy metadata
-(for use by Identity Providers)

- - diff --git a/roles/sram_metadata/templates/pyff-metadata.service.j2 b/roles/sram_metadata/templates/pyff-metadata.service.j2 deleted file mode 100644 index 3df9cc6e8..000000000 --- a/roles/sram_metadata/templates/pyff-metadata.service.j2 +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=pyFF Metadata processing -After=syslog.target network.target - -[Service] -Type=oneshot -WorkingDirectory={{metadata.basedir}} -ExecStart=echo "pyff-metadata" -SyslogIdentifier=pyff-metadata - -[Install] -WantedBy=multi-user.target diff --git a/roles/sram_metadata/templates/pyff-metadata.timer.j2 b/roles/sram_metadata/templates/pyff-metadata.timer.j2 deleted file mode 100644 index b1231af1f..000000000 --- a/roles/sram_metadata/templates/pyff-metadata.timer.j2 +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=Create Metadata timer - -[Timer] -OnCalendar=*:00 - -[Install] -WantedBy=multi-user.target diff --git a/roles/sram_metadata/templates/vhosts.conf.j2 b/roles/sram_metadata/templates/vhosts.conf.j2 deleted file mode 100644 index be3733827..000000000 --- a/roles/sram_metadata/templates/vhosts.conf.j2 +++ /dev/null @@ -1,15 +0,0 @@ - - ServerName sram-metadata-server - DocumentRoot /var/www/html - Header always set Referrer-Policy "strict-origin-when-cross-origin" - Header always set X-Content-Type-Options "nosniff" - Header always set X-XSS-Protection "1; mode=block" - - Require all granted - - - Require all granted - Options FollowSymLinks - Options -MultiViews - - diff --git a/roles/sram_midproxy/defaults/main.yml b/roles/sram_midproxy/defaults/main.yml index 3522fcb47..aeac841ea 100644 --- a/roles/sram_midproxy/defaults/main.yml +++ b/roles/sram_midproxy/defaults/main.yml @@ -1,8 +1,7 @@ --- -midproxy: - satosa_version: 8 - state_encryption_key: 'secret' - issuer: 'issuer' - client_id: 'client' - client_secret: 'secret' - sp_metadata: 'eb-metadata.xml' +sram_midproxy_satosa_version: 8 +sram_midproxy_state_encryption_key: 'secret' +sram_midproxy_issuer: 'issuer' +sram_midproxy_client_id: 'client' +sram_midproxy_client_secret: 'secret' +sram_midproxy_sp_metadata: 'eb-metadata.xml' diff --git a/roles/sram_midproxy/tasks/main.yml b/roles/sram_midproxy/tasks/main.yml index 2962a6f08..5270aab51 100644 --- a/roles/sram_midproxy/tasks/main.yml +++ b/roles/sram_midproxy/tasks/main.yml @@ -9,8 +9,8 @@ - name: Copy EB SP metadata ansible.builtin.copy: - src: "{{ inventory_dir }}/files/midproxy/{{ midproxy.sp_metadata }}" - dest: "/opt/sram/midproxy/{{ midproxy.sp_metadata }}" + src: "{{ inventory_dir }}/files/midproxy/{{ sram_midproxy_sp_metadata }}" + dest: "/opt/sram/midproxy/{{ sram_midproxy_sp_metadata }}" owner: 1000 group: 1000 mode: "0740" @@ -28,8 +28,8 @@ - name: Create the SATOSA container community.docker.docker_container: - name: midproxy - image: satosa:{{ midproxy.satosa_version }} + name: sram-midproxy + image: docker.io/satosa:{{ sram_midproxy_satosa_version }} pull: true restart_policy: "always" state: started @@ -38,11 +38,11 @@ - name: "loadbalancer" env: SATOSA_BASE: 'https://midproxy.{{ openconextaccess_base_domain }}' - SATOSA_STATE_ENCRYPTION_KEY: '{{ midproxy_state_encryption_key }}' - SATOSA_SP_METADATA: '{{ midproxy.sp_metadata }}' - SATOSA_ISSUER: '{{ midproxy.issuer }}' - SATOSA_CLIENT_ID: '{{ midproxy_client_id }}' - SATOSA_CLIENT_SECRET: '{{ midproxy_client_secret }}' + SATOSA_STATE_ENCRYPTION_KEY: '{{ sram_midproxy_state_encryption_key }}' + SATOSA_SP_METADATA: '{{ sram_midproxy_sp_metadata }}' + SATOSA_ISSUER: '{{ sram_midproxy_issuer }}' + SATOSA_CLIENT_ID: '{{ sram_midproxy_client_id }}' + SATOSA_CLIENT_SECRET: '{{ sram_midproxy_client_secret }}' volumes: - /opt/sram/midproxy:/etc/satosa labels: diff --git a/roles/sram_plsc/defaults/main.yml b/roles/sram_plsc/defaults/main.yml index f3b60a23e..6dd2780a1 100644 --- a/roles/sram_plsc/defaults/main.yml +++ b/roles/sram_plsc/defaults/main.yml @@ -1,12 +1,12 @@ --- -plsc_image: "ghcr.io/surfscz/sram-plsc:main" -plsc_conf_dir: "{{current_release_appdir}}/sram/plsc" -plsc_ansible_nolog: false -plsc_ldap_uri: "ldap://ldap:389/" -plsc_ldap_basedn: "dc=services,dc=vnet" -plsc_ldap_binddn: "cn=admin,dc=vnet" -plsc_ldap_password: "secret" -plsc_sbs_host: "http://sbs-server:8080" -plsc_sbs_user: "sysread" -plsc_sbs_password: "secret" -plsc_retry: 3 +sram_plsc_image: "ghcr.io/surfscz/sram-plsc:main" +sram_plsc_conf_dir: "{{current_release_appdir}}/sram/plsc" +sram_plsc_ansible_nolog: false +sram_plsc_ldap_uri: "ldap://ldap:389/" +sram_plsc_ldap_basedn: "dc=services,dc=vnet" +sram_plsc_ldap_binddn: "cn=admin,dc=vnet" +sram_plsc_ldap_password: "secret" +sram_plsc_sbs_host: "http://sbs-server:8080" +sram_plsc_sbs_user: "sysread" +sram_plsc_sbs_password: "secret" +sram_plsc_retry: 3 diff --git a/roles/sram_plsc/handlers/main.yml b/roles/sram_plsc/handlers/main.yml index 70cbb3672..a0dee373a 100644 --- a/roles/sram_plsc/handlers/main.yml +++ b/roles/sram_plsc/handlers/main.yml @@ -1,16 +1,4 @@ --- -# - name: enable plsc job -# systemd: -# name: "plsc.timer" -# enabled: true -# state: "restarted" -# daemon_reload: true - -# - name: "restart zabbix-agent" -# systemd: -# name: "zabbix-agent2.service" -# state: "restarted" - - name: Restart the plsc container community.docker.docker_container: name: sram-plsc diff --git a/roles/sram_plsc/tasks/main.yml b/roles/sram_plsc/tasks/main.yml index 0523ce118..2111b8646 100644 --- a/roles/sram_plsc/tasks/main.yml +++ b/roles/sram_plsc/tasks/main.yml @@ -1,28 +1,28 @@ --- - name: Make sure clients sync directory exists file: - path: "{{ plsc_conf_dir }}" + path: "{{ sram_plsc_conf_dir }}" state: directory mode: "0755" - name: "Create plsc.yml source if it doesn't exist" template: src: "plsc.yml.j2" - dest: "{{ plsc_conf_dir }}/plsc.yml" + dest: "{{ sram_plsc_conf_dir }}/plsc.yml" mode: "0640" - no_log: "{{plsc_ansible_nolog}}" + no_log: "{{ sram_plsc_ansible_nolog }}" notify: "Restart the plsc container" - name: Create the plsc container community.docker.docker_container: name: "sram-plsc" - image: "{{ plsc_image }}" + image: "{{ sram_plsc_image }}" restart_policy: "always" state: started pull: true mounts: - type: bind - source: "{{ plsc_conf_dir }}/plsc.yml" + source: "{{ sram_plsc_conf_dir }}/plsc.yml" target: "/opt/plsc/plsc.yml" networks: # TODO: Should this not be parametrized? diff --git a/roles/sram_plsc/templates/plsc.yml.j2 b/roles/sram_plsc/templates/plsc.yml.j2 index f1e7d4c6c..069f14d8b 100644 --- a/roles/sram_plsc/templates/plsc.yml.j2 +++ b/roles/sram_plsc/templates/plsc.yml.j2 @@ -1,25 +1,25 @@ --- ldap: src: - uri: "{{ plsc_ldap_uri }}" - basedn: "{{ plsc_ldap_basedn }}" - binddn: "{{ plsc_ldap_binddn }}" - passwd: "{{ plsc_ldap_password }}" + uri: "{{ sram_plsc_ldap_uri }}" + basedn: "{{ sram_plsc_ldap_basedn }}" + binddn: "{{ sram_plsc_ldap_binddn }}" + passwd: "{{ sram_plsc_ldap_password }}" sizelimit: 500 dst: - uri: "{{ plsc_ldap_uri }}" - basedn: "{{ plsc_ldap_basedn }}" - binddn: "{{ plsc_ldap_binddn }}" - passwd: "{{ plsc_ldap_password }}" + uri: "{{ sram_plsc_ldap_uri }}" + basedn: "{{ sram_plsc_ldap_basedn }}" + binddn: "{{ sram_plsc_ldap_binddn }}" + passwd: "{{ sram_plsc_ldap_password }}" sizelimit: 500 sbs: src: - host: "{{ plsc_sbs_host }}" - user: "{{ plsc_sbs_user }}" - passwd: "{{ plsc_sbs_password }}" + host: "{{ sram_plsc_sbs_host }}" + user: "{{ sram_plsc_sbs_user }}" + passwd: "{{ sram_plsc_sbs_password }}" verify_ssl: {{ false if env=='vm' else true }} timeout: 60 - retry: {{ plsc_retry }} + retry: {{ sram_plsc_retry }} pwd: "{CRYPT}!" uid: 1000 gid: 1000 diff --git a/roles/sram_redis/defaults/main.yml b/roles/sram_redis/defaults/main.yml index 857311145..28022a1af 100644 --- a/roles/sram_redis/defaults/main.yml +++ b/roles/sram_redis/defaults/main.yml @@ -1,9 +1,9 @@ --- -redis_image: "docker.io/library/redis:7" -redis_conf_dir: "{{ current_release_appdir }}/sram/redis" -redis_data_dir: "{{ current_release_appdir }}/sram/redis/data" -redis_user: redis -redis_group: redis -redis_redis_user: default -redis_redis_password: changethispassword -redis_max_memory: 100mb +sram_redis_image: "docker.io/library/redis:7" +sram_redis_conf_dir: "{{ current_release_appdir }}/sram/redis" +sram_redis_data_dir: "{{ current_release_appdir }}/sram/redis/data" +sram_redis_user: redis +sram_redis_group: redis +sram_redis_redis_user: default +sram_redis_redis_password: changethispassword +sram_redis_max_memory: 100mb diff --git a/roles/sram_redis/tasks/main.yml b/roles/sram_redis/tasks/main.yml index 0212fe0b0..b6fb8a694 100644 --- a/roles/sram_redis/tasks/main.yml +++ b/roles/sram_redis/tasks/main.yml @@ -1,7 +1,7 @@ --- - name: "Create redis group" group: - name: "{{ redis_group }}" + name: "{{ sram_redis_group }}" state: "present" register: "result" @@ -11,12 +11,12 @@ - name: "Create redis user" user: - name: "{{ redis_user }}" - group: "{{ redis_group }}" + name: "{{ sram_redis_user }}" + group: "{{ sram_redis_group }}" comment: "User to run SRAM Redis service" shell: "/bin/false" password: "!" - home: "{{ redis_conf_dir }}" + home: "{{ sram_redis_conf_dir }}" create_home: false state: "present" register: "result" @@ -29,34 +29,34 @@ file: path: "{{item.path}}" state: "directory" - owner: "{{ redis_user }}" - group: "{{ redis_group }}" + owner: "{{ sram_redis_user }}" + group: "{{ sram_redis_group }}" mode: "{{item.mode}}" with_items: - - { path: "{{redis_conf_dir}}", mode: "0755" } - - { path: "{{redis_data_dir}}", mode: "0755" } + - { path: "{{sram_redis_conf_dir}}", mode: "0755" } + - { path: "{{sram_redis_data_dir}}", mode: "0755" } - name: "Create redis config" template: src: "redis.conf.j2" - dest: "{{ redis_conf_dir }}/redis.conf" - owner: "{{ redis_user }}" - group: "{{ redis_group }}" + dest: "{{ sram_redis_conf_dir }}/redis.conf" + owner: "{{ sram_redis_user }}" + group: "{{ sram_redis_group }}" mode: "0644" notify: "Restart redis container" - name: "Create redis container" community.docker.docker_container: name: "sram-redis" - image: "{{ redis_image }}" + image: "{{ sram_redis_image }}" restart_policy: "always" state: "started" user: "{{ redis_user_uid }}:{{ redis_group_gid }}" command: | redis-server /usr/local/etc/redis/redis.conf volumes: - - "{{ redis_conf_dir }}:/usr/local/etc/redis" - - "{{ redis_data_dir }}:/data" + - "{{ sram_redis_conf_dir }}:/usr/local/etc/redis" + - "{{ sram_redis_data_dir }}:/data" networks: # TODO: Should this not be parametrized? - name: loadbalancer diff --git a/roles/sram_redis/templates/redis.conf.j2 b/roles/sram_redis/templates/redis.conf.j2 index 159ea9599..14d3ef177 100644 --- a/roles/sram_redis/templates/redis.conf.j2 +++ b/roles/sram_redis/templates/redis.conf.j2 @@ -1,3 +1,3 @@ -user {{redis_redis_user}} on +@all ~* &* >{{redis_redis_password}} -maxmemory {{ redis_max_memory }} +user {{ sram_redis_redis_user }} on +@all ~* &* >{{ sram_redis_redis_password }} +maxmemory {{ sram_redis_max_memory }} maxmemory-policy allkeys-lru diff --git a/roles/sram_sbs/defaults/main.yml b/roles/sram_sbs/defaults/main.yml index fe8696869..64bc443e4 100644 --- a/roles/sram_sbs/defaults/main.yml +++ b/roles/sram_sbs/defaults/main.yml @@ -1,130 +1,129 @@ --- -sbs_base_domain: "test2.sram.surf.nl" -sbs_ansible_nolog: true -sbs_base_url: "https://{{ sbs_base_domain }}" -sbs_server_image: "ghcr.io/surfscz/sram-sbs-server:main" -sbs_client_image: "ghcr.io/surfscz/sram-sbs-client:main" - -sbs_openidc_timeout: 86400 -sbs_sram_conf_dir: "{{ current_release_appdir }}/sram" - -sbs_work_dir: "{{ sbs_sram_conf_dir }}/sbs" -sbs_git_dir: "{{ sbs_work_dir }}/sbs" -sbs_env_dir: "{{ sbs_work_dir }}/sbs-env" -sbs_conf_dir: "{{ sbs_work_dir }}/config" -sbs_log_dir: "{{ sbs_work_dir }}/log" -sbs_apache_conf: "{{ sbs_work_dir }}/sbs.conf" -sbs_nginx_conf: "{{ sbs_work_dir }}/nginx.conf" - -sbs_db_name: "sbs" -sbs_db_user: "sbsrw" -# dbbackup_user: "sbs_backupper" -sbs_migration_user: "sbsmigrate" - -sbs_db_connection: "\ - mysql+mysqldb://%s:%s@{{ mariadb_host }}/{{ sbs_db_name }}\ +sram_sbs_base_domain: "test2.sram.surf.nl" +sram_sbs_ansible_nolog: true +sram_sbs_base_url: "https://{{ sram_sbs_base_domain }}" +sram_sbs_server_image: "ghcr.io/surfscz/sram-sbs-server:main" +sram_sbs_client_image: "ghcr.io/surfscz/sram-sbs-client:main" + +sram_sbs_openidc_timeout: 86400 +sram_sbs_sram_conf_dir: "{{ current_release_appdir }}/sram" + +sram_sbs_work_dir: "{{ sram_sbs_sram_conf_dir }}/sbs" +sram_sbs_git_dir: "{{ sram_sbs_work_dir }}/sbs" +sram_sbs_env_dir: "{{ sram_sbs_work_dir }}/sbs-env" +sram_sbs_conf_dir: "{{ sram_sbs_work_dir }}/config" +sram_sbs_log_dir: "{{ sram_sbs_work_dir }}/log" +sram_sbs_apache_conf: "{{ sram_sbs_work_dir }}/sbs.conf" +sram_sbs_nginx_conf: "{{ sram_sbs_work_dir }}/nginx.conf" + +sram_sbs_db_name: "sbs" +sram_sbs_db_user: "sbsrw" +sram_sbs_migration_user: "sbsmigrate" + +sram_sbs_db_connection: "\ + mysql+mysqldb://%s:%s@{{ mariadb_host }}/{{ sram_sbs_db_name }}\ ?ssl=true&charset=utf8mb4" -sbs_db_connection_sbs: "{{ sbs_db_connection | format(sbs_db_user, mysql_passwords.sbs) }}" -sbs_db_connection_migration: "\ - {{ sbs_db_connection | format(sbs_migration_user, mysql_passwords.sbsmigrate) }}" - -sbs_db_secret: secret -sbs_secret_key_suffix: suffix -sbs_encryption_key: encryption_key - -sbs_redis_host: sram-redis -sbs_redis_port: 6379 -sbs_redis_ssl: false -sbs_redis_user: default - -sbs_mail_host: "host.docker.internal" -sbs_mail_port: 25 - -sbs_user: "sbs" -sbs_group: "sbs" - -sbs_session_lifetime: 1440 -sbs_secret_key_suffix: "" - -sbs_oidc_crypto_password: "CHANGEME" -sbs_uid_attribute: "sub" - -sbs_disclaimer_color: "#a29c13" -sbs_disclaimer_label: wsgi - -sbs_urn_namespace: "urn:example:sbs" -sbs_eppn_scope: "sbs.example.edu" -sbs_restricted_co_default_org: "example.org" - -sbs_mail_sender_name: "SURF" -sbs_mail_sender_email: "no-reply@localhost" -sbs_exceptions_mail: "root@localhost" - -sbs_support_email: "sram-support@localhost" -sbs_admin_email: "sram-beheer@localhost" -sbs_ticket_email: "sram-support@surf.nl" -sbs_eduteams_email: "eduteams@localhost" - -sbs_suppress_mails: False - -sbs_wiki_link: "https://www.example.org/wiki" - -sbs_cron_hour_of_day: 4 -sbs_seed_allowed: True -sbs_api_keys_enabled: True -sbs_feedback_enabled: True -sbs_audit_trail_notifications_enabled: True -sbs_send_exceptions: False -sbs_send_js_exceptions: False -sbs_second_factor_authentication_required: True -sbs_totp_token_name: "SRAM-example" -sbs_notifications_enabled: True -sbs_invitation_reminders_enabled: True -sbs_invitation_expirations_enabled: True -sbs_open_requests_enabled: True -sbs_scim_sweep: False -sbs_impersonation_allowed: True -sbs_admin_platform_backdoor_totp: True -sbs_past_dates_allowed: True -sbs_mock_scim_enabled: True -sbs_log_to_stdout: True - -sbs_delete_orphaned: True -sbs_suspension_inactive_days: 365 -sbs_suspension_reminder_days: 14 -sbs_suspension_notify_admin: False - -sbs_oidc_config_url: "http://localhost/.well-known/openid-configuration" -sbs_oidc_authz_endpoint: "http://localhost/OIDC/authorization" -sbs_oidc_token_endpoint: "http://localhost/OIDC/token" -sbs_oidc_userinfo_endpoint: "http://localhost/OIDC/userinfo" -sbs_oidc_jwks_endpoint: "http://localhost/OIDC/jwks.json" -sbs_oidc_redirect_uri: "https://{{sbs_base_domain}}/api/users/resume-session" -sbs_oidc_jwt_audience: "https://localhost" -sbs_continue_eduteams_redirect_uri: "https://localhost/continue" -sbs_oidc_verify_peer: False -sbs_oidc_scopes: +sram_sbs_db_connection_sbs: "{{ sram_sbs_db_connection | format(sram_sbs_db_user, mysql_passwords.sbs) }}" +sram_sbs_db_connection_migration: "\ + {{ sram_sbs_db_connection | format(sram_sbs_migration_user, mysql_passwords.sbsmigrate) }}" + +sram_sbs_db_secret: secret +sram_sbs_secret_key_suffix: suffix +sram_sbs_encryption_key: encryption_key + +sram_sbs_redis_host: sram-redis +sram_sbs_redis_port: 6379 +sram_sbs_redis_ssl: false +sram_sbs_redis_user: default + +sram_sbs_mail_host: "host.docker.internal" +sram_sbs_mail_port: 25 + +sram_sbs_user: "sbs" +sram_sbs_group: "sbs" + +sram_sbs_session_lifetime: 1440 +sram_sbs_secret_key_suffix: "" + +sram_sbs_oidc_crypto_password: "CHANGEME" +sram_sbs_uid_attribute: "sub" + +sram_sbs_disclaimer_color: "#a29c13" +sram_sbs_disclaimer_label: wsgi + +sram_sbs_urn_namespace: "urn:example:sbs" +sram_sbs_eppn_scope: "sbs.example.edu" +sram_sbs_restricted_co_default_org: "example.org" + +sram_sbs_mail_sender_name: "SURF" +sram_sbs_mail_sender_email: "no-reply@localhost" +sram_sbs_exceptions_mail: "root@localhost" + +sram_sbs_support_email: "sram-support@localhost" +sram_sbs_admin_email: "sram-beheer@localhost" +sram_sbs_ticket_email: "sram-support@surf.nl" +sram_sbs_eduteams_email: "eduteams@localhost" + +sram_sbs_suppress_mails: False + +sram_sbs_wiki_link: "https://www.example.org/wiki" + +sram_sbs_cron_hour_of_day: 4 +sram_sbs_seed_allowed: True +sram_sbs_api_keys_enabled: True +sram_sbs_feedback_enabled: True +sram_sbs_audit_trail_notifications_enabled: True +sram_sbs_send_exceptions: False +sram_sbs_send_js_exceptions: False +sram_sbs_second_factor_authentication_required: True +sram_sbs_totp_token_name: "SRAM-example" +sram_sbs_notifications_enabled: True +sram_sbs_invitation_reminders_enabled: True +sram_sbs_invitation_expirations_enabled: True +sram_sbs_open_requests_enabled: True +sram_sbs_scim_sweep: False +sram_sbs_impersonation_allowed: True +sram_sbs_admin_platform_backdoor_totp: True +sram_sbs_past_dates_allowed: True +sram_sbs_mock_scim_enabled: True +sram_sbs_log_to_stdout: True + +sram_sbs_delete_orphaned: True +sram_sbs_suspension_inactive_days: 365 +sram_sbs_suspension_reminder_days: 14 +sram_sbs_suspension_notify_admin: False + +sram_sbs_oidc_config_url: "http://localhost/.well-known/openid-configuration" +sram_sbs_oidc_authz_endpoint: "http://localhost/OIDC/authorization" +sram_sbs_oidc_token_endpoint: "http://localhost/OIDC/token" +sram_sbs_oidc_userinfo_endpoint: "http://localhost/OIDC/userinfo" +sram_sbs_oidc_jwks_endpoint: "http://localhost/OIDC/jwks.json" +sram_sbs_oidc_redirect_uri: "https://{{sram_sbs_base_domain}}/api/users/resume-session" +sram_sbs_oidc_jwt_audience: "https://localhost" +sram_sbs_continue_eduteams_redirect_uri: "https://localhost/continue" +sram_sbs_oidc_verify_peer: False +sram_sbs_oidc_scopes: - openid -sbs_mfa_idp_allowed: false -sbs_eduteams_continue_endpoint: "https://localhost/continue" -sbs_eb_continue_endpoint: "https://engine.(.*)surfconext.nl(.*)" +sram_sbs_mfa_idp_allowed: false +sram_sbs_eduteams_continue_endpoint: "https://localhost/continue" +sram_sbs_eb_continue_endpoint: "https://engine.(.*)surfconext.nl(.*)" -sbs_manage_base_enabled: False -sbs_manage_base_url: "https://manage.{{base_domain}}" -sbs_manage_sram_rp_entity_id: "sbs.{{sbs_base_domain}}" -sbs_manage_verify_peer: False +sram_sbs_manage_base_enabled: False +sram_sbs_manage_base_url: "https://manage.{{base_domain}}" +sram_sbs_manage_sram_rp_entity_id: "sbs.{{sram_sbs_base_domain}}" +sram_sbs_manage_verify_peer: False -sbs_idp_metadata_url: "https://metadata.surfconext.nl/signed/2023/edugain-downstream-idp.xml " +sram_sbs_idp_metadata_url: "https://metadata.surfconext.nl/signed/2023/edugain-downstream-idp.xml " # backup_dir: "{{backup_base}}/sbs" -sbs_swagger_enabled: true +sram_sbs_swagger_enabled: true -sbs_ssid_identity_providers: [] -sbs_surf_secure_id: +sram_sbs_ssid_identity_providers: [] +sram_sbs_surf_secure_id: environment: "unknown.example.org" - sp_entity_id: "https://sbs.{{sbs_base_domain}}" - acs_url: "https://{{sbs_base_domain}}/api/users/acs" + sp_entity_id: "https://sbs.{{sram_sbs_base_domain}}" + acs_url: "https://{{sram_sbs_base_domain}}/api/users/acs" sa_gw_environment: "sa-gw.unknown.example.org" sa_idp_certificate: | -----BEGIN CERTIFICATE----- @@ -139,20 +138,20 @@ sbs_surf_secure_id: 12345 -----END CERTIFICATE----- -sbs_ssid_authncontext: "\ - http://{{ sbs_surf_secure_id.environment }}/assurance/sfo-level2" -sbs_ssid_entityid: "\ - https://{{ sbs_surf_secure_id.sa_gw_environment }}/second-factor-only/metadata" -sbs_ssid_sso_endpoint: "\ - https://{{ sbs_surf_secure_id.sa_gw_environment }}/second-factor-only/single-sign-on" +sram_sbs_ssid_authncontext: "\ + http://{{ sram_sbs_surf_secure_id.environment }}/assurance/sfo-level2" +sram_sbs_ssid_entityid: "\ + https://{{ sram_sbs_surf_secure_id.sa_gw_environment }}/second-factor-only/metadata" +sram_sbs_ssid_sso_endpoint: "\ + https://{{ sram_sbs_surf_secure_id.sa_gw_environment }}/second-factor-only/single-sign-on" -sbs_mfa_sso_minutes: 10 -sbs_mfa_fallback_enabled: true +sram_sbs_mfa_sso_minutes: 10 +sram_sbs_mfa_fallback_enabled: true -sbs_ldap_url: "ldap://ldap.example.com/dc=example,dc=com" -sbs_ldap_bind_account: "cn=admin,dc=entity_id,dc=services,dc=sram-tst,dc=surf,dc=nl" +sram_sbs_ldap_url: "ldap://ldap.example.com/dc=example,dc=com" +sram_sbs_ldap_bind_account: "cn=admin,dc=entity_id,dc=services,dc=sram-tst,dc=surf,dc=nl" -sbs_csp_style_hashes: +sram_sbs_csp_style_hashes: - 'sha256-0+ANsgYUJdh56RK8gGvTF2vnriYqvFHfWqtA8xXa+bA=' - 'sha256-3SnfHQolDHbZMbDAPmhrZf1keHiXfj/KJyh2phhFAAY=' - 'sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=' @@ -162,4 +161,4 @@ sbs_csp_style_hashes: - 'sha256-SU3XCwbQ/8qgzoGOWCYdkwIr3xRrl5rsvdFcpw8NSiE=' # on /new-service-request - 'sha256-WTC9gHKjIpzl5ub1eg/YrRy/k+jlzeyRojah9dxAApc=' # on /new-service-request -sbs_engine_block_api_token: secret +sram_sbs_engine_block_api_token: secret diff --git a/roles/sram_sbs/tasks/main.yml b/roles/sram_sbs/tasks/main.yml index c2cae754f..a9378fa36 100644 --- a/roles/sram_sbs/tasks/main.yml +++ b/roles/sram_sbs/tasks/main.yml @@ -1,7 +1,7 @@ --- - name: "Create SBS group" group: - name: "{{ sbs_group }}" + name: "{{ sram_sbs_group }}" state: "present" register: "result" @@ -11,12 +11,12 @@ - name: "Create SBS user" user: - name: "{{ sbs_user }}" - group: "{{ sbs_group }}" + name: "{{ sram_sbs_user }}" + group: "{{ sram_sbs_group }}" comment: "User to run SBS service" shell: "/bin/false" password: "!" - home: "{{ sbs_conf_dir }}" + home: "{{ sram_sbs_conf_dir }}" create_home: false state: "present" register: "result" @@ -33,14 +33,14 @@ group: "{{sbs_group_gid}}" mode: "{{item.mode}}" with_items: - - { path: "{{sbs_work_dir}}", mode: "0755" } - - { path: "{{sbs_conf_dir}}", mode: "0755" } - - { path: "{{sbs_conf_dir}}/saml", mode: "0755" } - - { path: "{{sbs_log_dir}}", mode: "0775" } + - { path: "{{sram_sbs_work_dir}}", mode: "0755" } + - { path: "{{sram_sbs_conf_dir}}", mode: "0755" } + - { path: "{{sram_sbs_conf_dir}}/saml", mode: "0755" } + - { path: "{{sram_sbs_log_dir}}", mode: "0775" } - name: "Fix file permissions" file: - path: "{{sbs_log_dir}}/{{item}}" + path: "{{sram_sbs_log_dir}}/{{item}}" owner: "{{sbs_user_uid}}" group: "{{sbs_group_gid}}" mode: "0664" @@ -54,7 +54,7 @@ - name: "Create SBS config files" template: src: "{{item.name}}.j2" - dest: "{{ sbs_conf_dir }}/{{item.name}}" + dest: "{{ sram_sbs_conf_dir }}/{{item.name}}" owner: "{{sbs_user_uid}}" group: "{{sbs_group_gid}}" mode: "{{item.mode}}" @@ -63,26 +63,26 @@ - { name: "alembic.ini", mode: "0644" } - { name: "disclaimer.css", mode: "0644" } - { name: "sbs-apache.conf", mode: "0644" } - no_log: "{{sbs_ansible_nolog}}" + no_log: "{{ sram_sbs_ansible_nolog }}" notify: "Restart sbs containers" - name: "Pull sbs image" community.docker.docker_image_pull: name: "{{ item }}" with_items: - - "{{ sbs_client_image }}" - - "{{ sbs_server_image }}" - register: "sbs_image" + - "{{ sram_sbs_client_image }}" + - "{{ sram_sbs_server_image }}" + register: "sram_sbs_image" - name: "Migration" # For some reason --check breaks this block - when: "sbs_image is changed and not ansible_check_mode" + when: "sram_sbs_image is changed and not ansible_check_mode" block: - name: "Run SBS migrations" throttle: 1 community.docker.docker_container: name: "sram-sbs-migration" - image: "{{ sbs_server_image }}" + image: "{{ sram_sbs_server_image }}" pull: "never" state: "started" restart_policy: "no" @@ -94,8 +94,8 @@ # don't actually run the server command: "/bin/true" volumes: - - "{{ sbs_conf_dir }}:/sbs-config" - - "{{ sbs_log_dir }}:/opt/sbs/log" + - "{{ sram_sbs_conf_dir }}:/sbs-config" + - "{{ sram_sbs_log_dir }}:/opt/sbs/log" networks: # TODO: Should we parametrize this? - name: "loadbalancer" @@ -104,36 +104,36 @@ changed_when: "'[alembic.runtime.migration] Running upgrade' in result.container.Output | default('')" notify: "Restart sbs containers" - # Remove the migration container; we can do that with auto_remove, because if we use that, ansible - # will not save the output in result + # Remove the migration container; we can't do that with auto_remove, + # because if we use that, ansible will not save the output in result - name: "Remove migration container" community.docker.docker_container: name: "sram-sbs-migration" state: "absent" - # TODO: fix this by only running this if "sbs_image is changed" + # TODO: fix this by only running this if "sram_sbs_image is changed" changed_when: false - name: "Start sbs client container" community.docker.docker_container: name: "sram-sbs-client" - image: "{{ sbs_client_image }}" + image: "{{ sram_sbs_client_image }}" pull: "never" restart_policy: "always" state: "started" volumes: - - "{{ sbs_conf_dir }}/sbs-apache.conf:/etc/apache2/sites-enabled/sbs.conf:ro" - - "{{ sbs_conf_dir }}/disclaimer.css:/opt/sbs/client/dist/disclaimer.css:ro" + - "{{ sram_sbs_conf_dir }}/sbs-apache.conf:/etc/apache2/sites-enabled/sbs.conf:ro" + - "{{ sram_sbs_conf_dir }}/disclaimer.css:/opt/sbs/client/dist/disclaimer.css:ro" networks: - name: "loadbalancer" labels: - traefik.http.routers.sbsclient.rule: "Host(`{{ sbs_base_domain }}`)" + traefik.http.routers.sbsclient.rule: "Host(`{{ sram_sbs_base_domain }}`)" traefik.http.routers.sbsclient.tls: "true" traefik.enable: "true" - name: "Start SBS server container" community.docker.docker_container: name: "sram-sbs-server" - image: "{{ sbs_server_image }}" + image: "{{ sram_sbs_server_image }}" restart_policy: "always" state: "started" env: @@ -144,8 +144,8 @@ RUN_MIGRATIONS: "0" pull: "never" volumes: - - "{{ sbs_conf_dir }}:/sbs-config" - - "{{ sbs_log_dir }}:/opt/sbs/log" + - "{{ sram_sbs_conf_dir }}:/sbs-config" + - "{{ sram_sbs_log_dir }}:/opt/sbs/log" - "/tmp/ci-runner:/tmp/ci-runner" networks: - name: "loadbalancer" diff --git a/roles/sram_sbs/templates/alembic.ini.j2 b/roles/sram_sbs/templates/alembic.ini.j2 index 7849e4f89..e6049eebb 100644 --- a/roles/sram_sbs/templates/alembic.ini.j2 +++ b/roles/sram_sbs/templates/alembic.ini.j2 @@ -35,7 +35,7 @@ script_location = migrations # are written from script.py.mako # output_encoding = utf-8 -sqlalchemy.url = {{ sbs_db_connection_migration }} +sqlalchemy.url = {{ sram_sbs_db_connection_migration }} # Logging configuration [loggers] diff --git a/roles/sram_sbs/templates/config.yml.j2 b/roles/sram_sbs/templates/config.yml.j2 index 9f597debe..0325a043f 100644 --- a/roles/sram_sbs/templates/config.yml.j2 +++ b/roles/sram_sbs/templates/config.yml.j2 @@ -1,110 +1,110 @@ --- database: - uri: {{ sbs_db_connection_sbs }} + uri: {{ sram_sbs_db_connection_sbs }} redis: {% if environment_shortname == 'test2' %} - uri: "redis://{{ sbs_redis_user }}:{{ sbs_redis_password }}@{{sbs_redis_host}}/" + uri: "redis://{{ sram_sbs_redis_user }}:{{ sram_sbs_redis_password }}@{{ sram_sbs_redis_host }}/" {% else %} - uri: "redis{% if sbs_redis_ssl %}s{% endif %}://{{ sbs_redis_user }}:{{ sbs_redis_password }}@{{ sbs_redis_host }}:{{ sbs_redis_port }}/" + uri: "redis{% if sram_sbs_redis_ssl %}s{% endif %}://{{ sram_sbs_redis_user }}:{{ sram_sbs_redis_password }}@{{ sram_sbs_redis_host }}:{{ sram_sbs_redis_port }}/" {% endif %} # add a per-release suffix here to invalidate sessions on new releases -secret_key: {{ sbs_db_secret }}{{sbs_secret_key_suffix}} +secret_key: {{ sram_sbs_db_secret }}{{ sram_sbs_secret_key_suffix }} # Must be a base64 encoded key of 128, 192, or 256 bits. # Generate: base64.b64encode(os.urandom(256 // 8)).decode() -encryption_key: {{ sbs_encryption_key }} +encryption_key: {{ sram_sbs_encryption_key }} # Lifetime of session in minutes (one day is 60 * 24) -permanent_session_lifetime: {{ sbs_session_lifetime }} +permanent_session_lifetime: {{ sram_sbs_session_lifetime }} logging: - log_to_stdout: {{ sbs_log_to_stdout }} + log_to_stdout: {{ sram_sbs_log_to_stdout }} # Valid scopes are "READ" and "WRITE" api_users: -{% for name, user in sbs_api_users.items() %} +{% for name, user in sram_sbs_api_users.items() %} - name: "{{ name }}" password: "{{ user.password }}" scopes: "[ {{ user.scopes | join(', ') }} ]" {% endfor %} oidc: - client_id: "{{ sbs_oidc_client_id }}" - client_secret: "{{ sbs_oidc_client_secret }}" - audience: "{{ sbs_oidc_jwt_audience }}" - verify_peer: {{ sbs_oidc_verify_peer }} - authorization_endpoint: "{{ sbs_oidc_authz_endpoint}}" - token_endpoint: "{{ sbs_oidc_token_endpoint }}" - userinfo_endpoint: "{{ sbs_oidc_userinfo_endpoint }}" - jwks_endpoint: "{{ sbs_oidc_jwks_endpoint }}" + client_id: "{{ sram_sbs_oidc_client_id }}" + client_secret: "{{ sram_sbs_oidc_client_secret }}" + audience: "{{ sram_sbs_oidc_jwt_audience }}" + verify_peer: {{ sram_sbs_oidc_verify_peer }} + authorization_endpoint: "{{ sram_sbs_oidc_authz_endpoint}}" + token_endpoint: "{{ sram_sbs_oidc_token_endpoint }}" + userinfo_endpoint: "{{ sram_sbs_oidc_userinfo_endpoint }}" + jwks_endpoint: "{{ sram_sbs_oidc_jwks_endpoint }}" #Note that the paths for these uri's is hardcoded and only domain and port differ per environment - redirect_uri: "{{ sbs_oidc_redirect_uri }}" - continue_eduteams_redirect_uri: "{{ sbs_eduteams_continue_endpoint }}" - continue_eb_redirect_uri: "{{ sbs_eb_continue_endpoint }}" - second_factor_authentication_required: {{ sbs_second_factor_authentication_required }} - totp_token_name: "{{ sbs_totp_token_name }}" + redirect_uri: "{{ sram_sbs_oidc_redirect_uri }}" + continue_eduteams_redirect_uri: "{{ sram_sbs_eduteams_continue_endpoint }}" + continue_eb_redirect_uri: "{{ sram_sbs_eb_continue_endpoint }}" + second_factor_authentication_required: {{ sram_sbs_second_factor_authentication_required }} + totp_token_name: "{{ sram_sbs_totp_token_name }}" # The service_id in the proxy_authz endpoint when logging into SBS. Most likely to equal the oidc.client_id - sram_service_entity_id: "{{ sbs_oidc_client_id }}" - scopes: {{ sbs_oidc_scopes }} + sram_service_entity_id: "{{ sram_sbs_oidc_client_id }}" + scopes: {{ sram_sbs_oidc_scopes }} base_scope: "{{ base_domain }}" -entitlement_group_namespace: "{{ sbs_urn_namespace }}" -eppn_scope: " {{ sbs_eppn_scope }}" +entitlement_group_namespace: "{{ sram_sbs_urn_namespace }}" +eppn_scope: " {{ sram_sbs_eppn_scope }}" scim_schema_sram: "urn:mace:surf.nl:sram:scim:extension" collaboration_creation_allowed_entitlement: "urn:mace:surf.nl:sram:allow-create-co" {% if env == "prd" %} environment_disclaimer: "" {% else %} -environment_disclaimer: "{{ sbs_disclaimer_label }}" +environment_disclaimer: "{{ sram_sbs_disclaimer_label }}" {% endif %} # All services in the white list can be requested in the create-restricted-co API # The default organisation is a fallback for when the administrator has no schac_home_org restricted_co: services_white_list: [ "https://cloud" ] - default_organisation: "{{ sbs_restricted_co_default_org }}" + default_organisation: "{{ sram_sbs_restricted_co_default_org }}" mail: - host: {{ sbs_mail_host }} - port: {{ sbs_mail_port }} - sender_name: {{ sbs_mail_sender_name }} - sender_email: {{ sbs_mail_sender_email }} - suppress_sending_mails: {{ sbs_suppress_mails }} - info_email: {{ sbs_support_email }} - beheer_email: {{ sbs_admin_email }} - ticket_email: {{ sbs_ticket_email }} - eduteams_email: {{ sbs_eduteams_email }} + host: {{ sram_sbs_mail_host }} + port: {{ sram_sbs_mail_port }} + sender_name: {{ sram_sbs_mail_sender_name }} + sender_email: {{ sram_sbs_mail_sender_email }} + suppress_sending_mails: {{ sram_sbs_suppress_mails }} + info_email: {{ sram_sbs_support_email }} + beheer_email: {{ sram_sbs_admin_email }} + ticket_email: {{ sram_sbs_ticket_email }} + eduteams_email: {{ sram_sbs_eduteams_email }} # Do we mail a summary of new Organizations and Services to the beheer_email? - audit_trail_notifications_enabled: {{ sbs_audit_trail_notifications_enabled }} + audit_trail_notifications_enabled: {{ sram_sbs_audit_trail_notifications_enabled }} account_deletion_notifications_enabled: True - send_exceptions: {{ sbs_send_exceptions }} - send_js_exceptions: {{ sbs_send_js_exceptions }} - send_exceptions_recipients: [ "{{ sbs_exceptions_mail }}" ] + send_exceptions: {{ sram_sbs_send_exceptions }} + send_js_exceptions: {{ sram_sbs_send_js_exceptions }} + send_exceptions_recipients: [ "{{ sram_sbs_exceptions_mail }}" ] environment: "{{ base_domain }}" manage: - enabled: {{ sbs_manage_base_enabled }} + enabled: {{ sram_sbs_manage_base_enabled }} # The entity_id of the SRAM RP in Manage for API retrieval, e.g "sbs.test2.sram.surf.nl" - sram_rp_entity_id: "{{ sbs_manage_sram_rp_entity_id }}" - base_url: "{{ sbs_manage_base_url }}" - user: "{{ sbs_manage_user }}" - password: "{{ sbs_manage_password }}" - verify_peer: {{ sbs_manage_verify_peer }} + sram_rp_entity_id: "{{ sram_sbs_manage_sram_rp_entity_id }}" + base_url: "{{ sram_sbs_manage_base_url }}" + user: "{{ sram_sbs_manage_user }}" + password: "{{ sram_sbs_manage_password }}" + verify_peer: {{ sram_sbs_manage_verify_peer }} aup: version: 1 url_aup_en: "https://edu.nl/6wb63" url_aup_nl: "https://edu.nl/6wb63" -base_url: {{ sbs_base_url }} -socket_url: {{ sbs_base_url }} -base_server_url: {{ sbs_base_url }} -wiki_link: {{ sbs_wiki_link }} +base_url: {{ sram_sbs_base_url }} +socket_url: {{ sram_sbs_base_url }} +base_server_url: {{ sram_sbs_base_url }} +wiki_link: {{ sram_sbs_wiki_link }} admin_users: -{% for admin_user in sbs_admin_users %} +{% for admin_user in sram_sbs_admin_users %} - uid: "{{ admin_user.uid }}" {% endfor %} @@ -117,17 +117,17 @@ organisation_categories: - "SURF" feature: - seed_allowed: {{ sbs_seed_allowed }} - api_keys_enabled: {{ sbs_api_keys_enabled }} - feedback_enabled: {{ sbs_feedback_enabled }} - impersonation_allowed: {{ sbs_impersonation_allowed }} - sbs_swagger_enabled: {{ sbs_swagger_enabled }} - admin_platform_backdoor_totp: {{ sbs_admin_platform_backdoor_totp }} - past_dates_allowed: {{ sbs_past_dates_allowed }} - mock_scim_enabled: {{ sbs_mock_scim_enabled }} + seed_allowed: {{ sram_sbs_seed_allowed }} + api_keys_enabled: {{ sram_sbs_api_keys_enabled }} + feedback_enabled: {{ sram_sbs_feedback_enabled }} + impersonation_allowed: {{ sram_sbs_impersonation_allowed }} + sbs_swagger_enabled: {{ sram_sbs_swagger_enabled }} + admin_platform_backdoor_totp: {{ sram_sbs_admin_platform_backdoor_totp }} + past_dates_allowed: {{ sram_sbs_past_dates_allowed }} + mock_scim_enabled: {{ sram_sbs_mock_scim_enabled }} metadata: - idp_url: "{{sbs_idp_metadata_url}}" + idp_url: "{{ sram_sbs_idp_metadata_url }}" parse_at_startup: True # No need for environment specific values scope_override: @@ -136,7 +136,7 @@ metadata: platform_admin_notifications: # Do we daily check for CO join_requests and CO requests and send a summary mail to beheer_email? enabled: False - cron_hour_of_day: {{ sbs_cron_hour_of_day }} + cron_hour_of_day: {{ sram_sbs_cron_hour_of_day }} # How long before we include open join_requests in the summary outstanding_join_request_days_threshold: 7 # How long before we include open CO requests in the summary @@ -144,8 +144,8 @@ platform_admin_notifications: user_requests_retention: # Do we daily check for CO join_requests and CO requests and delete approved and denied? - enabled: {{ sbs_notifications_enabled }} - cron_hour_of_day: {{ sbs_cron_hour_of_day }} + enabled: {{ sram_sbs_notifications_enabled }} + cron_hour_of_day: {{ sram_sbs_cron_hour_of_day }} # How long before we delete approved / denied join_requests outstanding_join_request_days_threshold: 90 # How long before we delete approved / denied CO requests @@ -153,24 +153,24 @@ user_requests_retention: # The retention config determines how long users may be inactive, how long the reminder email is valid and when do we resent the magic link retention: - cron_hour_of_day: {{ sbs_cron_hour_of_day }} + cron_hour_of_day: {{ sram_sbs_cron_hour_of_day }} # how many days of inactivity before a user is suspended # 0 allows for any last_login_date in the past to trigger suspension notification - allowed_inactive_period_days: {{ sbs_suspension_inactive_days }} + allowed_inactive_period_days: {{ sram_sbs_suspension_inactive_days }} # how many days before suspension do we send a warning # -1 will suspend notified users on second suspension cron - reminder_suspend_period_days: {{ sbs_suspension_reminder_days }} + reminder_suspend_period_days: {{ sram_sbs_suspension_reminder_days }} # how many days after suspension do we delete the account remove_suspended_users_period_days: 90 # how many days before deletion do we send a reminder reminder_expiry_period_days: 7 # whether to send a notification of the result of the retention process to the beheer_email - admin_notification_mail: {{ sbs_suspension_notify_admin }} + admin_notification_mail: {{ sram_sbs_suspension_notify_admin }} collaboration_expiration: # Do we daily check for CO's that will be deleted because they have been expired? - enabled: {{ sbs_notifications_enabled }} - cron_hour_of_day: {{ sbs_cron_hour_of_day }} + enabled: {{ sram_sbs_notifications_enabled }} + cron_hour_of_day: {{ sram_sbs_cron_hour_of_day }} # How long after expiration do we actually delete expired collaborations expired_collaborations_days_threshold: 90 # How many days before actual expiration do we mail the organisation members @@ -178,8 +178,8 @@ collaboration_expiration: collaboration_suspension: # Do we daily check for CO's that will be suspended because of inactivity? - enabled: {{ sbs_notifications_enabled }} - cron_hour_of_day: {{ sbs_cron_hour_of_day }} + enabled: {{ sram_sbs_notifications_enabled }} + cron_hour_of_day: {{ sram_sbs_cron_hour_of_day }} # After how many days of inactivity do we suspend collaborations collaboration_inactivity_days_threshold: 365 # How many days before actual suspension do we mail the organisation members @@ -189,8 +189,8 @@ collaboration_suspension: membership_expiration: # Do we daily check for memberships that will be deleted because they have been expired? - enabled: {{ sbs_notifications_enabled }} - cron_hour_of_day: {{ sbs_cron_hour_of_day }} + enabled: {{ sram_sbs_notifications_enabled }} + cron_hour_of_day: {{ sram_sbs_cron_hour_of_day }} # How long after expiration do we actually delete expired memberships expired_memberships_days_threshold: 90 # How many days before actual expiration do we mail the co admin and member @@ -198,15 +198,15 @@ membership_expiration: invitation_reminders: # Do we daily check for invitations that need a reminder? - enabled: {{ sbs_invitation_reminders_enabled }} - cron_hour_of_day: {{ sbs_cron_hour_of_day }} + enabled: {{ sram_sbs_invitation_reminders_enabled }} + cron_hour_of_day: {{ sram_sbs_cron_hour_of_day }} # How many days before expiration of an invitation do we remind the user? invitation_reminders_threshold: 5 invitation_expirations: # Do we daily check for invitations that are expired / accepted and are eligible for deletion ? - enabled: {{ sbs_invitation_expirations_enabled }} - cron_hour_of_day: {{ sbs_cron_hour_of_day }} + enabled: {{ sram_sbs_invitation_expirations_enabled }} + cron_hour_of_day: {{ sram_sbs_cron_hour_of_day }} # How long after expiration of an invitation do we delete the invitation? nbr_days_remove_expired_invitations: 10 # How long after expiration of an API created invitation do we delete the invitation? @@ -214,38 +214,38 @@ invitation_expirations: orphan_users: # Do we daily check for users that are orphans soo they can be deleted? - enabled: {{ sbs_delete_orphaned }} - cron_hour_of_day: {{ sbs_cron_hour_of_day }} + enabled: {{ sram_sbs_delete_orphaned }} + cron_hour_of_day: {{ sram_sbs_cron_hour_of_day }} # How long after created do we delete orphan users delete_days_threshold: 14 open_requests: # Do we weekly check for all open requests? - enabled: {{ sbs_open_requests_enabled }} + enabled: {{ sram_sbs_open_requests_enabled }} cron_day_of_week: 1 scim_sweep: # Do we enable scim sweeps? - enabled: {{ sbs_scim_sweep }} + enabled: {{ sram_sbs_scim_sweep }} # How often do we check if scim sweeps are needed per service cron_minutes_expression: "*/15" ldap: - url: "{{ sbs_ldap_url }}" - bind_account: "{{ sbs_ldap_bind_account }}" + url: "{{ sram_sbs_ldap_url }}" + bind_account: "{{ sram_sbs_ldap_bind_account }}" # A MFA login in a different flow is valid for X minutes -mfa_sso_time_in_minutes: {{sbs_mfa_sso_minutes}} +mfa_sso_time_in_minutes: {{ sram_sbs_mfa_sso_minutes }} # whether to fall back to TOTP MFA -mfa_fallback_enabled: {{sbs_mfa_fallback_enabled}} +mfa_fallback_enabled: {{ sram_sbs_mfa_fallback_enabled }} # Lower case entity ID's and schac_home allowed skipping MFA. # Note that for a login directly into SRAM only schac_home can be used as the entity_idp of the IdP is unknown -mfa_idp_allowed: {{sbs_mfa_idp_allowed}} +mfa_idp_allowed: {{ sram_sbs_mfa_idp_allowed }} # Lower case schachome organisations / entity ID's where SURFSecure ID is used for step-up -ssid_identity_providers: {{sbs_ssid_identity_providers}} +ssid_identity_providers: {{ sram_sbs_ssid_identity_providers }} ssid_config_folder: saml @@ -256,9 +256,9 @@ rate_limit_totp_guesses_per_30_seconds: 10 # The uid's of user that will never be suspended or deleted excluded_user_accounts: -{% for excluded_user in sbs_excluded_users %} +{% for excluded_user in sram_sbs_excluded_users %} - uid: "{{ excluded_user.uid }}" {% endfor %} engine_block: - api_token: {{ sbs_engine_block_api_token }} + api_token: {{ sram_sbs_engine_block_api_token }} diff --git a/roles/sram_sbs/templates/disclaimer.css.j2 b/roles/sram_sbs/templates/disclaimer.css.j2 index 0211d17d8..455cb97cb 100644 --- a/roles/sram_sbs/templates/disclaimer.css.j2 +++ b/roles/sram_sbs/templates/disclaimer.css.j2 @@ -1,6 +1,6 @@ {% if env!="prd" -%} body::after { - background: {{ sbs_disclaimer_color }}; - content: "{{ sbs_disclaimer_label }}"; + background: {{ sram_sbs_disclaimer_color }}; + content: "{{ sram_sbs_disclaimer_label }}"; } {% endif %} diff --git a/roles/sram_sbs/templates/saml_advanced_settings.json.j2 b/roles/sram_sbs/templates/saml_advanced_settings.json.j2 index bdde32050..0d03c63d7 100644 --- a/roles/sram_sbs/templates/saml_advanced_settings.json.j2 +++ b/roles/sram_sbs/templates/saml_advanced_settings.json.j2 @@ -11,7 +11,7 @@ "wantNameIdEncrypted": false, "wantAttributeStatement": false, "wantAssertionsEncrypted": false, - "requestedAuthnContext": ["{{sbs_ssid_authncontext}}"], + "requestedAuthnContext": ["{{ sram_sbs_ssid_authncontext }}"], "requestedAuthnContextComparison": "minimum", "failOnAuthnContextMismatch": false, "allowSingleLabelDomains": false, diff --git a/roles/sram_sbs/templates/saml_settings.json.j2 b/roles/sram_sbs/templates/saml_settings.json.j2 index bb5788e97..073651110 100644 --- a/roles/sram_sbs/templates/saml_settings.json.j2 +++ b/roles/sram_sbs/templates/saml_settings.json.j2 @@ -2,21 +2,21 @@ "strict": true, "debug": true, "sp": { - "entityId": "{{ sbs_surf_secure_id.sp_entity_id }}", + "entityId": "{{ sram_sbs_surf_secure_id.sp_entity_id }}", "assertionConsumerService": { - "url": "{{ sbs_surf_secure_id.acs_url }}", + "url": "{{ sram_sbs_surf_secure_id.acs_url }}", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", - "x509cert": "{{ sbs_surf_secure_id.pub | barepem }}", - "privateKey": "{{ sbs_surf_secure_id.priv | barepem }}" + "x509cert": "{{ sram_sbs_surf_secure_id.pub | barepem }}", + "privateKey": "{{ sram_sbs_surf_secure_id.priv | barepem }}" }, "idp": { - "entityId": "{{ sbs_ssid_entityid }}", + "entityId": "{{ sram_sbs_ssid_entityid }}", "singleSignOnService": { - "url": "{{ sbs_ssid_sso_endpoint }}", + "url": "{{ sram_sbs_ssid_sso_endpoint }}", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, - "x509cert": "{{ sbs_surf_secure_id.sa_idp_certificate | barepem }}" + "x509cert": "{{ sram_sbs_surf_secure_id.sa_idp_certificate | barepem }}" } } diff --git a/roles/sram_sbs/templates/sbs-apache.conf.j2 b/roles/sram_sbs/templates/sbs-apache.conf.j2 index af8c32ce7..ebf19db99 100644 --- a/roles/sram_sbs/templates/sbs-apache.conf.j2 +++ b/roles/sram_sbs/templates/sbs-apache.conf.j2 @@ -1,4 +1,4 @@ -ServerName {{ sbs_base_domain }} +ServerName {{ sram_sbs_base_domain }} #ErrorLog /proc/self/fd/2 #CustomLog /proc/self/fd/1 common DocumentRoot /opt/sbs/client/dist diff --git a/roles/sram_sbs/templates/sbs.service.j2 b/roles/sram_sbs/templates/sbs.service.j2 deleted file mode 100644 index 2920ddc8d..000000000 --- a/roles/sram_sbs/templates/sbs.service.j2 +++ /dev/null @@ -1,32 +0,0 @@ -[Unit] -Description=SBS -After=network.target - -[Service] -DynamicUser=true -User=_sram_sbs -Group=_sram_sbs -SupplementaryGroups={{sbs_group}} - -WorkingDirectory={{sbs_git_dir}} -ReadWritePaths={{sbs_log_dir}} -NoNewPrivileges=true -PrivateTmp=true - -Environment="CONFIG=config/config.yml" -Environment="PROFILE=log_to_stdout" -# the python requests module uses the CAs provided by the certifi package by default -# we'll just take the OS-provided CAs, thankyouverymuch -Environment="REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt" - -Type=notify -ExecStart={{sbs_env_dir}}/bin/gunicorn --worker-class eventlet --workers {{sbs_num_workers}} --bind 127.0.0.1:8080 server.__main__:app - -Restart=on-failure -RestartSec=10 - -KillMode=mixed -TimeoutStopSec=5 - -[Install] -WantedBy=multi-user.target From b1f09c7cc4f637150e2c6f12e2f80c82e0810681 Mon Sep 17 00:00:00 2001 From: Martin van Es Date: Tue, 5 May 2026 09:28:22 +0200 Subject: [PATCH 11/15] Fix ldap test --- roles/sram_ldap/tasks/main.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/roles/sram_ldap/tasks/main.yml b/roles/sram_ldap/tasks/main.yml index 318e3b340..c1322739e 100644 --- a/roles/sram_ldap/tasks/main.yml +++ b/roles/sram_ldap/tasks/main.yml @@ -78,11 +78,11 @@ traefik.tcp.routers.ldap.tls: "true" traefik.tcp.services.ldap.loadbalancer.server.port: "389" healthcheck: - test: "-S /var/run/slapd/ldapi" - # - "CMD" - # - "bash" - # - "-c" - # - "[[ -S /var/run/slapd/ldapi ]]" + test: + - "CMD" + - "bash" + - "-c" + - "[[ -S /var/run/slapd/ldapi ]]" register: "ldap_container" - name: Wait for LDAP initialization From 55d80f244215df6732964d03ce00170ce8fab709 Mon Sep 17 00:00:00 2001 From: Martin van Es Date: Tue, 5 May 2026 10:03:25 +0200 Subject: [PATCH 12/15] Parametrize satosa image --- roles/sram_midproxy/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/sram_midproxy/tasks/main.yml b/roles/sram_midproxy/tasks/main.yml index 5270aab51..d90d93e01 100644 --- a/roles/sram_midproxy/tasks/main.yml +++ b/roles/sram_midproxy/tasks/main.yml @@ -29,7 +29,7 @@ - name: Create the SATOSA container community.docker.docker_container: name: sram-midproxy - image: docker.io/satosa:{{ sram_midproxy_satosa_version }} + image: "{{ sram_midproxy_satosa_image }}" pull: true restart_policy: "always" state: started From 81455a5663b0f4f275c2e1070e4cb0fdd1b05728 Mon Sep 17 00:00:00 2001 From: Martin van Es Date: Wed, 6 May 2026 11:46:50 +0200 Subject: [PATCH 13/15] Add CRM to AA scopes WIP WIP --- roles/haproxy/tasks/get_acme_certs.yml | 3 +- .../invite/templates/serverapplication.yml.j2 | 1 + roles/mailpit/defaults/main.yml | 5 -- roles/mailpit/tasks/main.yml | 37 ------------- roles/sram_ldap/tasks/admins.yml | 4 +- roles/sram_ldap/tasks/main.yml | 8 ++- roles/sram_ldap/templates/ldap-backup.sh.j2 | 19 ------- roles/sram_midproxy/defaults/main.yml | 2 + roles/sram_midproxy/tasks/main.yml | 53 +++++++++++++++---- roles/sram_plsc/tasks/main.yml | 8 ++- roles/sram_redis/tasks/main.yml | 6 +++ roles/sram_sbs/tasks/main.yml | 10 ++-- roles/sram_sbs/templates/config.yml.j2 | 8 --- roles/sram_sbs/templates/disclaimer.css.j2 | 2 +- roles/sram_sbs/templates/sbs-apache.conf.j2 | 2 +- 15 files changed, 75 insertions(+), 93 deletions(-) delete mode 100644 roles/mailpit/defaults/main.yml delete mode 100644 roles/mailpit/tasks/main.yml delete mode 100644 roles/sram_ldap/templates/ldap-backup.sh.j2 diff --git a/roles/haproxy/tasks/get_acme_certs.yml b/roles/haproxy/tasks/get_acme_certs.yml index 7ef17361c..18e87bc49 100644 --- a/roles/haproxy/tasks/get_acme_certs.yml +++ b/roles/haproxy/tasks/get_acme_certs.yml @@ -6,7 +6,8 @@ ( [base_domain] + (haproxy_applications | map(attribute='vhost_name') | list) - + (haproxy_redirects | map(attribute='hostname') | list) + + (haproxy_ldap_servers | map(attribute='vhost_name') | list) + + (haproxy_redirects | map(attribute='hostname') | list) ) | unique | sort }} diff --git a/roles/invite/templates/serverapplication.yml.j2 b/roles/invite/templates/serverapplication.yml.j2 index 81e7dae5d..bf59520da 100644 --- a/roles/invite/templates/serverapplication.yml.j2 +++ b/roles/invite/templates/serverapplication.yml.j2 @@ -161,6 +161,7 @@ external-api-configuration: password: "{{ invite_attribute_aggregation_secret }}" scopes: - attribute_aggregation + - crm - username: {{ invite.lifecycle_user }} password: "{{ invite.lifecycle_secret }}" scopes: diff --git a/roles/mailpit/defaults/main.yml b/roles/mailpit/defaults/main.yml deleted file mode 100644 index f400a8d2e..000000000 --- a/roles/mailpit/defaults/main.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -mailpit_image: "axllent/mailpit" -mailpit_hostname: "mailpit.{{ base_domain }}" -mailpit_user: "mailpit" -mailpit_group: "mailpit" diff --git a/roles/mailpit/tasks/main.yml b/roles/mailpit/tasks/main.yml deleted file mode 100644 index 5d89f4a7c..000000000 --- a/roles/mailpit/tasks/main.yml +++ /dev/null @@ -1,37 +0,0 @@ ---- -- name: "Create mailpit group" - group: - name: "{{ mailpit_group }}" - state: "present" - register: "result" - -- name: "Create mailpit user" - user: - name: "{{ mailpit_user }}" - group: "{{ mailpit_group }}" - comment: "User to run Mailpit service" - shell: "/bin/false" - password: "!" - create_home: false - state: "present" - register: "result" - -- name: "Save mailpit user uid" - set_fact: - mailpit_user_uid: "{{ result.uid }}" - -- name: "Create mailpit container" - docker_container: - name: "mailpit" - image: "{{ mailpit_image }}" - restart_policy: "always" - state: "started" - user: "{{ mailpit_user_uid }}" - ports: - networks: - - name: "loadbalancer" - labels: - traefik.enable: "true" - traefik.http.routers.mailpit.rule: "Host(`{{ mailpit_hostname }}`)" - traefik.http.routers.mailpit.tls: "true" - traefik.http.services.mailpit.loadbalancer.server.port: 8025 diff --git a/roles/sram_ldap/tasks/admins.yml b/roles/sram_ldap/tasks/admins.yml index f20648460..85ef3a594 100644 --- a/roles/sram_ldap/tasks/admins.yml +++ b/roles/sram_ldap/tasks/admins.yml @@ -4,7 +4,7 @@ ldap_admins: "{{ sram_ldap_admins }}" # Find existing ldap admins -- name: Initialize admins (I) +- name: Initialize admins (Ia) community.general.ldap_search: dn: "{{ sram_ldap_services_dn.basedn }}" scope: "onelevel" @@ -17,7 +17,7 @@ register: "existing_ldap_admins_result" # ansible trips over stuff like this: we need to extract the results from the result -- name: Initialize admins (Ia) +- name: Initialize admins (Ib) set_fact: existing_ldap_admins: "{{ existing_ldap_admins_result.results }}" diff --git a/roles/sram_ldap/tasks/main.yml b/roles/sram_ldap/tasks/main.yml index c1322739e..b6da9cb18 100644 --- a/roles/sram_ldap/tasks/main.yml +++ b/roles/sram_ldap/tasks/main.yml @@ -53,13 +53,19 @@ combine({ host.key | format(item.key): host.value | format(item.value) }) }} with_dict: "{{ sram_ldap_hosts }}" +- name: "Pull ldap image" + community.docker.docker_image_pull: + name: "{{ sram_ldap_image }}" + register: "ldap_image" + - name: Create the ldap container community.docker.docker_container: name: "sram-ldap" image: "{{ sram_ldap_image }}" restart_policy: "always" state: started - # pull: true + restart: "{{ ldap_image is changed }}" + pull: never ports: - 0.0.0.0:389:389 env: diff --git a/roles/sram_ldap/templates/ldap-backup.sh.j2 b/roles/sram_ldap/templates/ldap-backup.sh.j2 deleted file mode 100644 index 2c6aa9201..000000000 --- a/roles/sram_ldap/templates/ldap-backup.sh.j2 +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -# vim:ft=sh -set -e - -if [ $UID -ne 0 ] -then - echo "Sorry, this script must run as root" - exit 1 -fi - -BACKUP_DIR="{{ldap_backup_dir}}" -BACKUP_FILE="$BACKUP_DIR/ldap_$(/bin/date +%Y-%m-%d_%H:%M)" - -mkdir -p -m 0755 "$BACKUP_DIR" - -/usr/sbin/slapcat -o ldif-wrap=no -n0 | /bin/bzip2 -c6 > "${BACKUP_FILE}.db0.ldif.bz2" -/usr/sbin/slapcat -o ldif-wrap=no -n1 | /bin/bzip2 -c6 > "${BACKUP_FILE}.db1.ldif.bz2" - -exit 0 diff --git a/roles/sram_midproxy/defaults/main.yml b/roles/sram_midproxy/defaults/main.yml index aeac841ea..585af3dd6 100644 --- a/roles/sram_midproxy/defaults/main.yml +++ b/roles/sram_midproxy/defaults/main.yml @@ -1,4 +1,6 @@ --- +sram_midproxy_user: midproxy +sram_midproxy_group: midproxy sram_midproxy_satosa_version: 8 sram_midproxy_state_encryption_key: 'secret' sram_midproxy_issuer: 'issuer' diff --git a/roles/sram_midproxy/tasks/main.yml b/roles/sram_midproxy/tasks/main.yml index d90d93e01..19125220b 100644 --- a/roles/sram_midproxy/tasks/main.yml +++ b/roles/sram_midproxy/tasks/main.yml @@ -1,39 +1,70 @@ --- +- name: "Create midproxy group" + group: + name: "{{ sram_midproxy_group }}" + state: "present" + register: "result" + +- name: "Create midproxy user" + user: + name: "{{ sram_midproxy_user }}" + group: "{{ sram_midproxy_group }}" + comment: "User to run Midproxy service" + shell: "/bin/false" + password: "!" + create_home: false + state: "present" + register: "result" + +- name: "Save midproxy user uid" + set_fact: + # --check fails on result.uid if user was never provisioned + midproxy_user_uid: "{{ result.uid | default(1000) }}" + - name: Create directory to keep configfile ansible.builtin.file: dest: "/opt/sram/midproxy" state: directory - owner: 1000 - group: 1000 - mode: "0770" + owner: "{{ sram_midproxy_user }}" + group: "{{ sram_midproxy_group }}" + # mkdir: cannot create directory '.': Permission denied + mode: "0777" - name: Copy EB SP metadata ansible.builtin.copy: - src: "{{ inventory_dir }}/files/midproxy/{{ sram_midproxy_sp_metadata }}" + src: "{{ inventory_dir }}/files/sram_midproxy/{{ sram_midproxy_sp_metadata }}" dest: "/opt/sram/midproxy/{{ sram_midproxy_sp_metadata }}" - owner: 1000 - group: 1000 - mode: "0740" + owner: "{{ sram_midproxy_user }}" + group: "{{ sram_midproxy_group }}" + # mkdir: cannot create directory '.': Permission denied + mode: "0644" - name: Copy SATOSA conf files ansible.builtin.copy: src: "{{ item }}" dest: "/opt/sram/midproxy/{{ item }}" - owner: 1000 - group: 1000 + owner: "{{ sram_midproxy_user }}" + group: "{{ sram_midproxy_group }}" with_items: - internal_attributes.yaml - proxy_conf.yaml - plugins/ +- name: "Pull satosa image" + community.docker.docker_image_pull: + name: "{{ sram_midproxy_satosa_image }}" + register: "satosa_image" + - name: Create the SATOSA container community.docker.docker_container: name: sram-midproxy image: "{{ sram_midproxy_satosa_image }}" - pull: true + pull: never restart_policy: "always" state: started - restart: true + restart: "{{ satosa_image is changed }}" + # 2026-05-21 07:25:44 +0000] [1] [ERROR] Control server error: [Errno 13] Permission denied: '/.gunicorn' + # user: "{{ midproxy_user_uid }}" networks: - name: "loadbalancer" env: diff --git a/roles/sram_plsc/tasks/main.yml b/roles/sram_plsc/tasks/main.yml index 2111b8646..365cf0866 100644 --- a/roles/sram_plsc/tasks/main.yml +++ b/roles/sram_plsc/tasks/main.yml @@ -13,13 +13,19 @@ no_log: "{{ sram_plsc_ansible_nolog }}" notify: "Restart the plsc container" +- name: "Pull plsc image" + community.docker.docker_image_pull: + name: "{{ sram_plsc_image }}" + register: "plsc_image" + - name: Create the plsc container community.docker.docker_container: name: "sram-plsc" image: "{{ sram_plsc_image }}" restart_policy: "always" state: started - pull: true + restart: "{{ plsc_image is changed }}" + pull: never mounts: - type: bind source: "{{ sram_plsc_conf_dir }}/plsc.yml" diff --git a/roles/sram_redis/tasks/main.yml b/roles/sram_redis/tasks/main.yml index b6fb8a694..c4c8286c6 100644 --- a/roles/sram_redis/tasks/main.yml +++ b/roles/sram_redis/tasks/main.yml @@ -45,12 +45,18 @@ mode: "0644" notify: "Restart redis container" +- name: "Pull redis image" + community.docker.docker_image_pull: + name: "{{ sram_redis_image }}" + register: "redis_image" + - name: "Create redis container" community.docker.docker_container: name: "sram-redis" image: "{{ sram_redis_image }}" restart_policy: "always" state: "started" + restart: "{{ redis_image is changed }}" user: "{{ redis_user_uid }}:{{ redis_group_gid }}" command: | redis-server /usr/local/etc/redis/redis.conf diff --git a/roles/sram_sbs/tasks/main.yml b/roles/sram_sbs/tasks/main.yml index a9378fa36..9c0d93d80 100644 --- a/roles/sram_sbs/tasks/main.yml +++ b/roles/sram_sbs/tasks/main.yml @@ -72,11 +72,11 @@ with_items: - "{{ sram_sbs_client_image }}" - "{{ sram_sbs_server_image }}" - register: "sram_sbs_image" + register: "sbs_images" - name: "Migration" # For some reason --check breaks this block - when: "sram_sbs_image is changed and not ansible_check_mode" + when: "sbs_images is changed and not ansible_check_mode" block: - name: "Run SBS migrations" throttle: 1 @@ -110,8 +110,6 @@ community.docker.docker_container: name: "sram-sbs-migration" state: "absent" - # TODO: fix this by only running this if "sram_sbs_image is changed" - changed_when: false - name: "Start sbs client container" community.docker.docker_container: @@ -120,6 +118,7 @@ pull: "never" restart_policy: "always" state: "started" + restart: "{{ sbs_images is changed}}" volumes: - "{{ sram_sbs_conf_dir }}/sbs-apache.conf:/etc/apache2/sites-enabled/sbs.conf:ro" - "{{ sram_sbs_conf_dir }}/disclaimer.css:/opt/sbs/client/dist/disclaimer.css:ro" @@ -136,6 +135,7 @@ image: "{{ sram_sbs_server_image }}" restart_policy: "always" state: "started" + restart: "{{ sbs_images is changed}}" env: RUNAS_UID: "{{ sbs_user_uid | string }}" RUNAS_GID: "{{ sbs_group_gid | string }}" @@ -149,7 +149,5 @@ - "/tmp/ci-runner:/tmp/ci-runner" networks: - name: "loadbalancer" - # TODO: fix this: this is only for dev etc_hosts: - oidc-op.scz-vm.net: "172.20.1.24" host.docker.internal: host-gateway diff --git a/roles/sram_sbs/templates/config.yml.j2 b/roles/sram_sbs/templates/config.yml.j2 index 0325a043f..28dcfe3b4 100644 --- a/roles/sram_sbs/templates/config.yml.j2 +++ b/roles/sram_sbs/templates/config.yml.j2 @@ -3,11 +3,7 @@ database: uri: {{ sram_sbs_db_connection_sbs }} redis: -{% if environment_shortname == 'test2' %} - uri: "redis://{{ sram_sbs_redis_user }}:{{ sram_sbs_redis_password }}@{{ sram_sbs_redis_host }}/" -{% else %} uri: "redis{% if sram_sbs_redis_ssl %}s{% endif %}://{{ sram_sbs_redis_user }}:{{ sram_sbs_redis_password }}@{{ sram_sbs_redis_host }}:{{ sram_sbs_redis_port }}/" -{% endif %} # add a per-release suffix here to invalidate sessions on new releases secret_key: {{ sram_sbs_db_secret }}{{ sram_sbs_secret_key_suffix }} @@ -54,11 +50,7 @@ eppn_scope: " {{ sram_sbs_eppn_scope }}" scim_schema_sram: "urn:mace:surf.nl:sram:scim:extension" collaboration_creation_allowed_entitlement: "urn:mace:surf.nl:sram:allow-create-co" -{% if env == "prd" %} -environment_disclaimer: "" -{% else %} environment_disclaimer: "{{ sram_sbs_disclaimer_label }}" -{% endif %} # All services in the white list can be requested in the create-restricted-co API # The default organisation is a fallback for when the administrator has no schac_home_org diff --git a/roles/sram_sbs/templates/disclaimer.css.j2 b/roles/sram_sbs/templates/disclaimer.css.j2 index 455cb97cb..ed34bdc09 100644 --- a/roles/sram_sbs/templates/disclaimer.css.j2 +++ b/roles/sram_sbs/templates/disclaimer.css.j2 @@ -1,4 +1,4 @@ -{% if env!="prd" -%} +{% if sram_sbs_disclaimer_label!="" -%} body::after { background: {{ sram_sbs_disclaimer_color }}; content: "{{ sram_sbs_disclaimer_label }}"; diff --git a/roles/sram_sbs/templates/sbs-apache.conf.j2 b/roles/sram_sbs/templates/sbs-apache.conf.j2 index ebf19db99..5db9a8c64 100644 --- a/roles/sram_sbs/templates/sbs-apache.conf.j2 +++ b/roles/sram_sbs/templates/sbs-apache.conf.j2 @@ -3,7 +3,7 @@ ServerName {{ sram_sbs_base_domain }} #CustomLog /proc/self/fd/1 common DocumentRoot /opt/sbs/client/dist -Header set Content-Security-Policy "default-src 'self'; base-uri 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-src 'none'; form-action 'self' https://*.{{ base_domain }}; frame-ancestors 'none'; block-all-mixed-content;" +Header set Content-Security-Policy "{{ httpd_csp.sram }}" Header set Permissions-Policy "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(self), gamepad=(), speaker-selection=()" RewriteEngine On From 4aa1d8c6fcb7a830ce8271953a30317635479768 Mon Sep 17 00:00:00 2001 From: Bas Zoetekouw Date: Thu, 21 May 2026 13:57:09 +0200 Subject: [PATCH 14/15] Disable HTTP caching for SBS index.html See https://github.com/SURFscz/SRAM-deploy/pull/620 --- roles/sram_sbs/templates/sbs-apache.conf.j2 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/roles/sram_sbs/templates/sbs-apache.conf.j2 b/roles/sram_sbs/templates/sbs-apache.conf.j2 index 5db9a8c64..1d19099eb 100644 --- a/roles/sram_sbs/templates/sbs-apache.conf.j2 +++ b/roles/sram_sbs/templates/sbs-apache.conf.j2 @@ -17,11 +17,14 @@ ProxyPassReverse / http://sram-sbs-server:8080/ ProxyPass /socket.io/ ws://sram-sbs-server:8080/socket.io/ ProxyPassReverse /socket.io/ ws://sram-sbs-server:8080/socket.io/ - - Header set Cache-Control: "public, max-age=31536000, immutable" + + Header set Cache-Control "no-cache, private" + + Header set Cache-Control "public, max-age=31536000, immutable" + - Header set Cache-Control: "no-cache, private" + Header set Cache-Control "no-cache, private" From 27f4578a588be8989c108ce7fabed9689a9b5055 Mon Sep 17 00:00:00 2001 From: Martin van Es Date: Fri, 26 Jun 2026 14:54:17 +0200 Subject: [PATCH 15/15] Fix merge conflicts --- .github/workflows/molecule-loadbalancer.yml | 2 +- .github/workflows/molecule-mongo.yml | 2 +- .github/workflows/syntax.yml | 2 +- .yamllint.yaml | 27 +++ README.md | 1 - environments/template/group_vars/all.yml | 1 - .../template/group_vars/mongo_servers.yml | 2 +- environments/template/group_vars/template.yml | 36 --- environments/template/inventory | 3 - .../template/secrets/secret_example.yml | 7 +- provision.yml | 7 - .../templates/serverapplication.yml.j2 | 4 +- roles/engine/defaults/main.yml | 33 +-- roles/engine/tasks/main.yml | 184 +++++++------- roles/engine/templates/parameters.yml.j2 | 4 +- roles/haproxy/defaults/main.yml | 6 + roles/haproxy/tasks/acme.yml | 18 ++ roles/hosts/tasks/main.yml | 1 - roles/invite/templates/logback.xml.j2 | 62 ++--- .../invite/templates/serverapplication.yml.j2 | 46 +--- roles/manage/defaults/main.yml | 6 + .../institution.template.json | 19 ++ .../metadata_templates/sfo.template.json | 14 ++ .../files/policies/allowed_attributes.json | 6 +- roles/manage/tasks/main.yml | 63 +++-- roles/manage/templates/application.yml.j2 | 9 + roles/manage/templates/logback.xml.j2 | 61 +++-- .../institution.schema.json.j2 | 116 +++++++++ .../oidc10_rp.schema.json.j2 | 7 +- .../saml20_sp.schema.json.j2 | 7 +- .../metadata_configuration/sfo.schema.json.j2 | 99 ++++++++ .../single_tenant_template.schema.json.j2 | 7 +- .../sram.schema.json.j2 | 7 +- roles/manage/templates/stepup_config.json.j2 | 44 ++++ roles/mariadbdocker/tasks/main.yml | 2 +- roles/mongo/README.md | 32 ++- roles/mongo/defaults/main.yml | 57 ++++- roles/mongo/tasks/clusterconfig.yml | 225 ++++++++++++++---- roles/mongo/tasks/clusterhealthcheck.yml | 88 +++++++ roles/mongo/tasks/generalconfig.yml | 52 ++++ roles/mongo/tasks/install.yml | 18 +- roles/mongo/tasks/main.yml | 63 +++-- roles/mongo/tasks/postinstall.yml | 44 ++-- roles/mongo/tasks/services.yml | 6 + roles/mongo/tasks/standaloneconfig.yml | 1 + roles/mongo/tasks/users.yml | 27 ++- roles/mongo/templates/mongod.conf.j2 | 11 +- roles/mongo/templates/mongoshrc.js.j2 | 7 +- roles/myconext/tasks/main.yml | 7 - roles/oidcng/tasks/main.yml | 7 - roles/oidcng/templates/logback.xml.j2 | 84 +++---- roles/pdp/handlers/main.yml | 8 +- roles/pdp/templates/serverapplication.yml.j2 | 6 +- roles/remove-java-app/defaults/main.yml | 1 - roles/remove-java-app/handlers/main.yml | 9 - roles/remove-java-app/tasks/main.yml | 57 ----- roles/rsyslog/tasks/main.yml | 8 + .../process_auth_log_for_environment.yml | 78 ++++++ roles/rsyslog/tasks/process_auth_logs.yml | 101 +------- roles/rsyslog/tasks/rsyslog_central.yml | 49 ++-- roles/rsyslog/templates/clean_loglogins.j2 | 144 +++++++++-- roles/rsyslog/templates/logrotate_ebauth.j2 | 4 +- .../rsyslog/templates/logrotate_stepupauth.j2 | 4 +- .../templates/parse_ebauth_to_mysql.py.j2 | 21 +- .../templates/parse_stepupauth_to_mysql.py.j2 | 22 +- roles/rsyslog/templates/sc_ruleset.conf.j2 | 2 - roles/rsyslog/templates/sc_template.conf.j2 | 2 - roles/teams/defaults/main.yml | 33 --- roles/teams/handlers/main.yml | 9 - roles/teams/tasks/main.yml | 101 -------- roles/teams/templates/logback.xml.j2 | 29 --- .../teams/templates/serverapplication.yml.j2 | 131 ---------- roles/teams/vars/main.yml | 14 -- .../welcome/files/site/images/teams-logo.png | Bin 6761 -> 0 bytes roles/welcome/templates/site/index.html | 18 -- 75 files changed, 1447 insertions(+), 1048 deletions(-) create mode 100644 .yamllint.yaml create mode 100644 roles/manage/files/metadata_templates/institution.template.json create mode 100644 roles/manage/files/metadata_templates/sfo.template.json create mode 100644 roles/manage/templates/metadata_configuration/institution.schema.json.j2 create mode 100644 roles/manage/templates/metadata_configuration/sfo.schema.json.j2 create mode 100644 roles/manage/templates/stepup_config.json.j2 create mode 100644 roles/mongo/tasks/clusterhealthcheck.yml create mode 100644 roles/mongo/tasks/generalconfig.yml create mode 100644 roles/mongo/tasks/services.yml create mode 100644 roles/mongo/tasks/standaloneconfig.yml delete mode 100644 roles/remove-java-app/defaults/main.yml delete mode 100644 roles/remove-java-app/handlers/main.yml delete mode 100644 roles/remove-java-app/tasks/main.yml create mode 100644 roles/rsyslog/tasks/process_auth_log_for_environment.yml delete mode 100644 roles/teams/defaults/main.yml delete mode 100644 roles/teams/handlers/main.yml delete mode 100644 roles/teams/tasks/main.yml delete mode 100644 roles/teams/templates/logback.xml.j2 delete mode 100644 roles/teams/templates/serverapplication.yml.j2 delete mode 100644 roles/teams/vars/main.yml delete mode 100644 roles/welcome/files/site/images/teams-logo.png diff --git a/.github/workflows/molecule-loadbalancer.yml b/.github/workflows/molecule-loadbalancer.yml index ca03e28f3..fa1afc9a3 100644 --- a/.github/workflows/molecule-loadbalancer.yml +++ b/.github/workflows/molecule-loadbalancer.yml @@ -24,7 +24,7 @@ jobs: build: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Set up Python 3.8 uses: actions/setup-python@v6 diff --git a/.github/workflows/molecule-mongo.yml b/.github/workflows/molecule-mongo.yml index f0b52d67d..f944dab16 100644 --- a/.github/workflows/molecule-mongo.yml +++ b/.github/workflows/molecule-mongo.yml @@ -18,7 +18,7 @@ jobs: build: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Set up Python 3.8 uses: actions/setup-python@v6 with: diff --git a/.github/workflows/syntax.yml b/.github/workflows/syntax.yml index 8295b151f..521fb2c73 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/syntax.yml @@ -19,7 +19,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Set up Python 3.8 uses: actions/setup-python@v6 diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 000000000..8824391dc --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,27 @@ +--- +extends: "default" + +rules: + # 80 chars should be enough, but don't fail if a line is longer + line-length: + max: 160 + level: "warning" + + quoted-strings: + quote-type: "any" + required: true + allow-quoted-quotes: false + check-keys: false + +# ansible-lint compatibility: + comments: + min-spaces-from-content: 1 + + comments-indentation: false + + braces: + max-spaces-inside: 1 + + octal-values: + forbid-implicit-octal: true + forbid-explicit-octal: true diff --git a/README.md b/README.md index a6623af49..88a3628c1 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ Every application has a seperate role to install it. The following roles can be | myconext | eduID | | profile | Profile page | | manage | Entity registration | -| teams | Group membership app | | mujina | Mujina IdP | | voot | Voot membership API | | pdp | Policy Decicions API | diff --git a/environments/template/group_vars/all.yml b/environments/template/group_vars/all.yml index a314ae546..ded950834 100644 --- a/environments/template/group_vars/all.yml +++ b/environments/template/group_vars/all.yml @@ -54,7 +54,6 @@ engine_attribute_aggregation_password: "{{ aa.eb_password }}" # Some deprovision variables are shared between applications authz_server_api_lifecycle_username: authz_server_api_lifecycle_user -teams_api_lifecycle_username: teams_api_lifecycle_user attribute_aggregator_api_lifecycle_username: attribute_aggregator_api_lifecycle_user engine_api_deprovision_user: lifecycle lifecycle_api_username: lifecycle diff --git a/environments/template/group_vars/mongo_servers.yml b/environments/template/group_vars/mongo_servers.yml index 70bb40871..d9f3e10cc 100644 --- a/environments/template/group_vars/mongo_servers.yml +++ b/environments/template/group_vars/mongo_servers.yml @@ -1,5 +1,5 @@ --- -replica_set_name: my_mongo_cluster +mongo_replica_set_name: my_mongo_cluster mongo_cluster_members: - host: "mongo3.example.com:{{ mongo_port }}" # arbiter first or change mongo_arbiter_index diff --git a/environments/template/group_vars/template.yml b/environments/template/group_vars/template.yml index dc2642d3b..a1538a319 100644 --- a/environments/template/group_vars/template.yml +++ b/environments/template/group_vars/template.yml @@ -38,8 +38,6 @@ mujina_version: "8.0.2" oidcng_version: "6.1.6" pdp_version: "7.3.0" profile_version: "3.1.4" -teams_gui_version: "9.1.3" -teams_server_version: "9.1.3" voot_version: "6.2.0" myconext_version: "8.1.12-1" dashboard_version: "13.0.11" @@ -53,14 +51,12 @@ statistics_version: "1.1.7" databases: names: - - teams - "{{ engine_database_name }}" - pdp-server - aaserver - shibboleth - eb_logins users: - - { name: teamsrw, db_name: teams, password: "{{ mysql_passwords.teams }}" } - { name: "{{ engine_database_user }}", db_name: "{{ engine_database_name }}", password: "{{ mysql_passwords.eb }}" } - { name: pdp-serverrw, db_name: pdp-server, password: "{{ mysql_passwords.pdp_server }}" } - { name: aa-serverrw, db_name: aaserver, password: "{{ mysql_passwords.aa_server }}" } @@ -100,32 +96,10 @@ engine_trusted_proxy_ips: - 192.168.1.1 - 10.0.0.1 # -engine_keys: - default: - privateFile: /etc/openconext/engineblock.pem - publicKey: engineblock.crt - publicFile: /etc/openconext/engineblock.crt - profile_apache_symfony_environment: prod # Engine's assertion signing certificate: engine_profile_idp_certificate: /etc/openconext/engineblock.crt -teams: - db_name: "teams" - db_user: "teamsrw" - db_password: "{{ mysql_passwords.teams }}" - db_host: "{{ mariadb_host }}" - group_name_context: "urn:collab:group:{{ base_domain }}:" - voot_api_user: "voot" - spdashboard_api_user: "spdashboard" - spdashboard_person_urn: "urn:collab:person:surfnet.nl:sp-dashboard-C133A36F-CFCA-4F3D-87CE-7ECE29773FE0" - product_name: "OpenConext Teams" - default_stem_name: "demo:openconext:org" - feature_invite_migration_on: False - super_admins_team_urns: - - "nl:surfnet:diensten:teams_super_users" - - "nl:surfnet:diensten:teams_super_admin_users" - engineblock: idp_url: https://engine.{{ base_domain }}/authentication/idp/single-sign-on idp_entity_id: https://engine.{{ base_domain }}/authentication/idp/metadata @@ -402,9 +376,6 @@ loadbalancing: metadata: port: 409 - teams: - port: 601 - oidc_playground: port: 619 @@ -483,13 +454,6 @@ haproxy_applications: servers: "{{docker_servers}}" restricted: yes - - name: teams - vhost_name: teams.{{ base_domain }} - ha_method: "GET" - ha_url: "/api/teams/health" - port: "{{ loadbalancing.teams.port }}" - servers: "{{docker_servers}}" - - name: oidc_playground vhost_name: "oidc-playground.{{ base_domain }}" ha_method: "GET" diff --git a/environments/template/inventory b/environments/template/inventory index f1b3dabed..b6e736941 100644 --- a/environments/template/inventory +++ b/environments/template/inventory @@ -84,9 +84,6 @@ docker2.example.com [docker_invite:children] docker_apps1 -[docker_teams:children] -docker_apps1 - [docker_pdp:children] docker_apps1 diff --git a/environments/template/secrets/secret_example.yml b/environments/template/secrets/secret_example.yml index 6dc112821..a6c052658 100644 --- a/environments/template/secrets/secret_example.yml +++ b/environments/template/secrets/secret_example.yml @@ -1,7 +1,6 @@ mysql_root_password: secret mysql_passwords: - teams: secret eb: secret pdp_server: secret aa_server: secret @@ -13,7 +12,7 @@ mongo_passwords: oidcng: secret myconext: secret -mongo_admin_password: secret +mongo_admin_password: secret # this works for first time install, if you change it later you will have to do it manually mongo_ca_passphrase: secret engine_api_metadata_push_password: secret @@ -36,7 +35,6 @@ engine_parameters_secret: secretsecretsecretsecretsecretsecret # need 32 chars profile_secret: secret -teams_authz_client_secret: secret teams_migration_secret_key: secret voot_resource_checking_secret: secret @@ -45,7 +43,6 @@ voot_oidcng_checkToken_secret: secret external_group_provider_secrets: teams: secret -teams_api_lifecycle_password: secret teams_api_spdashboard_password: secret attribute_aggregator_api_lifecycle_password: secret @@ -144,7 +141,7 @@ invite_lifecycle_secret: "secret" invite_internal_secret: "secret" invite_profile_secret: "secret" invite_sp_dashboard_secret: "secret" -invite_access_secret: "secret" +invite_access_dashboard_secret: "secret" invite_private_key_pkcs8: | -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCfpYYMgKYDICkp diff --git a/provision.yml b/provision.yml index 3b26963a5..815d62ee2 100644 --- a/provision.yml +++ b/provision.yml @@ -283,13 +283,6 @@ - role: stepupwebauthn tags: ['stepupwebauthn', 'stepup'] -- name: Deploy teams app - hosts: docker_teams - become: true - roles: - - teams - tags: ['teams'] - - name: Deploy voot app hosts: docker_voot become: true diff --git a/roles/dashboard/templates/serverapplication.yml.j2 b/roles/dashboard/templates/serverapplication.yml.j2 index 45109b554..88a96ff8d 100644 --- a/roles/dashboard/templates/serverapplication.yml.j2 +++ b/roles/dashboard/templates/serverapplication.yml.j2 @@ -27,8 +27,8 @@ spDashboard.password={{ dashboard_sp_dashboard_password }} # SAB connection details sab-rest.endpoint={{ dashboard.sab_rest_endpoint }} -sab-rest.username=cdk -sab-rest.password={{ dashboard_sab_rest_password }} +sab-rest.username={{ dashboard.sab_rest_username }} +sab-rest.password={{ dashboard.sab_rest_password }} # SAB roles admin.surfconext.idp.sabRole=SURFconextverantwoordelijke diff --git a/roles/engine/defaults/main.yml b/roles/engine/defaults/main.yml index ca58ea135..af113fd22 100644 --- a/roles/engine/defaults/main.yml +++ b/roles/engine/defaults/main.yml @@ -2,22 +2,22 @@ engine_version: "" # Be aware that if you enable this option that NPM, Node.js and Composer are installed # Feature toggles -engine_feature_encrypted_assertions: 1 -engine_feature_encrypted_assertions_require_outer_signature: 1 -engine_feature_run_all_manipulations_prior_to_consent: 0 -engine_feature_block_user_on_violation: 0 -engine_feature_enable_sso_notification: 0 -engine_feature_enable_sso_session_cookie: 0 -engine_feature_enable_consent: 1 -engine_feature_stepup_override_entityid: 0 -engine_feature_idp_initiated_flow: 1 -engine_api_feature_metadata_push: 1 -engine_api_feature_consent_listing: 1 -engine_api_feature_consent_remove: 0 -engine_api_feature_metadata_api: 1 -engine_api_feature_deprovision: 1 -engine_feature_send_user_attributes: 0 -engine_feature_enable_sbs_interrupt: 0 +engine_feature_encrypted_assertions: true +engine_feature_encrypted_assertions_require_outer_signature: true +engine_feature_run_all_manipulations_prior_to_consent: false +engine_feature_block_user_on_violation: false +engine_feature_enable_sso_notification: false +engine_feature_enable_sso_session_cookie: false +engine_feature_enable_consent: true +engine_feature_stepup_override_entityid: false +engine_feature_idp_initiated_flow: true +engine_feature_send_user_attributes: false +engine_feature_enable_sbs_interrupt: false +engine_api_feature_metadata_push: true +engine_api_feature_consent_listing: true +engine_api_feature_consent_remove: false +engine_api_feature_metadata_api: true +engine_api_feature_deprovision: true # Cutoff point for showing unfiltered IdPs on the WAYF engine_wayf_cutoff_point_for_showing_unfiltered_idps: 50 @@ -78,6 +78,7 @@ engine_stepup_gateway_sfo_entity_id: "https://{{ engine_stepup_gateway_domain }} engine_stepup_gateway_sfo_sso_location: "https://{{ engine_stepup_gateway_domain }}/second-factor-only/single-sign-on" # SBS interrupt settings +engine_sbs_base_url: "sbs.{{ base_domain }}" engine_sbs_attributes_allowed: - 'urn:mace:dir:attribute-def:eduPersonEntitlement' - 'urn:mace:dir:attribute-def:uid' diff --git a/roles/engine/tasks/main.yml b/roles/engine/tasks/main.yml index 2776c4b4c..f14b05dc1 100644 --- a/roles/engine/tasks/main.yml +++ b/roles/engine/tasks/main.yml @@ -1,24 +1,24 @@ --- -- name: Add group engine +- name: "Add group engine" ansible.builtin.group: name: "engine" - state: present - register: engine_guid + state: "present" + register: "engine_guid" -- name: Add user engine +- name: "Add user engine" ansible.builtin.user: name: "engine" group: "engine" - createhome: false - state: present - register: engine_uid + create_home: false + state: "present" + register: "engine_uid" -- name: Create some dirs +- name: "Create some dirs" ansible.builtin.file: - state: directory - dest: "{{ item }}" - owner: root - group: root + path: "{{ item }}" + state: "directory" + owner: "root" + group: "root" mode: "0755" with_items: - "{{ _engine_config_dir }}" @@ -27,7 +27,7 @@ - "{{ _engine_config_dir }}/images" - "{{ _engine_config_dir }}/languages" -- name: Place parameters.yml +- name: "Place parameters.yml" ansible.builtin.template: src: "{{ item }}.j2" dest: "{{ _engine_config_dir }}/configs/{{ item }}" @@ -39,157 +39,157 @@ - "monolog.yml" notify: "Restart engine" -- name: Check presence of environment specific attributes.json +- name: "Check presence of environment specific attributes.json" ansible.builtin.stat: path: "{{ inventory_dir }}/files/eb/attributes.json" - register: engine_attributes_json_present + register: "engine_attributes_json_present" become: false - delegate_to: localhost + delegate_to: "localhost" -- name: Copy environment specific attributes.json +- name: "Copy environment specific attributes.json" ansible.builtin.copy: src: "{{ inventory_dir }}/files/eb/attributes.json" dest: "{{ _engine_config_dir }}/configs/" mode: "0644" - owner: root - group: engine - when: engine_attributes_json_present.stat.exists + owner: "root" + group: "engine" + when: "engine_attributes_json_present.stat.exists" -- name: Check presence of language specific overrides +- name: "Check presence of language specific overrides" ansible.builtin.stat: path: "{{ inventory_dir }}/files/eb/languages/" - register: engine_overrides_present + register: "engine_overrides_present" become: false - delegate_to: localhost + delegate_to: "localhost" -- name: Copy language specific overrides +- name: "Copy language specific overrides" ansible.builtin.template: src: "{{ item }}" dest: "{{ _engine_config_dir }}/languages/" - owner: root - group: engine + owner: "root" + group: "engine" mode: "0644" - when: engine_overrides_present.stat.exists + when: "engine_overrides_present.stat.exists" with_fileglob: - "{{ inventory_dir }}/files/eb/languages/*" notify: "Restart engine" -- name: Check if we have a custom logo +- name: "Check if we have a custom logo" ansible.builtin.stat: path: "{{ inventory_dir }}/files/logo.png" - register: engine_customlogo + register: "engine_customlogo" become: false - delegate_to: localhost + delegate_to: "localhost" -- name: Install environment specific logo +- name: "Install environment specific logo" ansible.builtin.copy: src: "{{ inventory_dir }}/files/logo.png" dest: "{{ _engine_config_dir }}/images/" - owner: root - group: engine + owner: "root" + group: "engine" mode: "0644" - when: engine_customlogo.stat.exists + when: "engine_customlogo.stat.exists" -- name: Check if we have a custom favicon +- name: "Check if we have a custom favicon" ansible.builtin.stat: path: "{{ inventory_dir }}/files/favicon.ico" - register: engine_customfavicon + register: "engine_customfavicon" become: false - delegate_to: localhost + delegate_to: "localhost" -- name: Install environment specific favicon +- name: "Install environment specific favicon" ansible.builtin.copy: src: "{{ inventory_dir }}/files/favicon.ico" dest: "/opt/openconext/common/" - owner: root - group: root + owner: "root" + group: "root" mode: "0644" - when: engine_customfavicon.stat.exists + when: "engine_customfavicon.stat.exists" -- name: Check if we have a custom background back image for the feedback page +- name: "Check if we have a custom background back image for the feedback page" ansible.builtin.stat: path: "{{ inventory_dir }}/files/eb/background-back.svg" - register: engine_customfeedbackbackground + register: "engine_customfeedbackbackground" become: false - delegate_to: localhost + delegate_to: "localhost" -- name: Install environment specific background back image +- name: "Install environment specific background back image" ansible.builtin.copy: src: "{{ inventory_dir }}/files/eb/background-back.svg" dest: "{{ _engine_config_dir }}/images/" - owner: root - group: engine + owner: "root" + group: "engine" mode: "0644" - when: engine_customfeedbackbackground.stat.exists + when: "engine_customfeedbackbackground.stat.exists" -- name: Check if we have a custom background front image for the feedback page +- name: "Check if we have a custom background front image for the feedback page" ansible.builtin.stat: path: "{{ inventory_dir }}/files/eb/background-front.svg" - register: engine_customfeedbackforeground + register: "engine_customfeedbackforeground" become: false - delegate_to: localhost + delegate_to: "localhost" -- name: Install environment specific background front image +- name: "Install environment specific background front image" ansible.builtin.copy: src: "{{ inventory_dir }}/files/eb/background-front.svg" dest: "{{ _engine_config_dir }}/images/" - owner: root - group: engine + owner: "root" + group: "engine" mode: "0644" - when: engine_customfeedbackforeground.stat.exists + when: "engine_customfeedbackforeground.stat.exists" -- name: Check if we have a Stepup GW certificate +- name: "Check if we have a Stepup GW certificate" ansible.builtin.stat: path: "{{ inventory_dir }}/files/certs/stepup_gateway.pem" - register: engine_stepupgwcert + register: "engine_stepupgwcert" become: false - delegate_to: localhost + delegate_to: "localhost" -- name: Install Stepup GW certificate +- name: "Install Stepup GW certificate" ansible.builtin.copy: src: "{{ inventory_dir }}/files/certs/stepup_gateway.pem" dest: "{{ _engine_config_dir }}/certs/" - owner: root - group: engine + owner: "root" + group: "engine" mode: "0644" - when: engine_stepupgwcert.stat.exists + when: "engine_stepupgwcert.stat.exists" -- name: Copy over the engineblock keys +- name: "Copy over the engineblock keys" ansible.builtin.copy: content: "{{ item.private_key }}" dest: "{{ _engine_config_dir }}/certs/{{ item.name }}.key" - owner: root - group: engine + owner: "root" + group: "engine" mode: "0440" no_log: true loop: "{{ engine_key_and_certs }}" -- name: Copy engineblock certificates to correct location +- name: "Copy engineblock certificates to correct location" ansible.builtin.copy: src: "{{ inventory_dir }}/files/certs/{{ item.crt_name }}" dest: "{{ _engine_config_dir }}/certs/{{ item.name }}.crt" - owner: root - group: engine + owner: "root" + group: "engine" mode: "0644" no_log: true loop: "{{ engine_key_and_certs }}" -- name: Create Docker volume to contain the sessions +- name: "Create Docker volume to contain the sessions" community.docker.docker_volume: - name: engineblock_sessions - state: present + volume_name: "engineblock_sessions" + state: "present" -- name: Add the MariaDB docker network to the list of networks when MariaDB runs in Docker +- name: "Add the MariaDB docker network to the list of networks when MariaDB runs in Docker" ansible.builtin.set_fact: engine_docker_networks: - - name: loadbalancer - - name: openconext_mariadb - when: mariadb_in_docker | default(false) | bool + - name: "loadbalancer" + - name: "openconext_mariadb" + when: "mariadb_in_docker | default(false) | bool" -- name: Create the container +- name: "Create the container" community.docker.docker_container: name: "engineblock" - image: ghcr.io/openconext/openconext-engineblock/openconext-engineblock:{{ engine_version }} + image: "ghcr.io/openconext/openconext-engineblock/openconext-engineblock:{{ engine_version }}" pull: true restart_policy: "always" networks: "{{ engine_docker_networks }}" @@ -210,51 +210,51 @@ APP_SECRET: "{{ engine_parameters_secret }}" APP_DEBUG: "{{ engine_debug | bool | int | string }}" etc_hosts: - host.docker.internal: host-gateway + host.docker.internal: "host-gateway" mounts: - source: "{{ _engine_config_dir }}/configs/" target: "{{ _engine_container_config_dir }}" - type: bind + type: "bind" read_only: true - source: "{{ _engine_config_dir }}/languages/overrides.en.php" target: "/var/www/html/languages/overrides.en.php" - type: bind + type: "bind" read_only: true - source: "{{ _engine_config_dir }}/languages/overrides.nl.php" target: "/var/www/html/languages/overrides.nl.php" - type: bind + type: "bind" read_only: true - source: "{{ _engine_config_dir }}/configs/attributes.json" target: "{{ _engine_container_config_dir }}/attributes.json" - type: bind + type: "bind" read_only: true - source: "{{ _engine_config_dir }}/images/background-back.svg" target: "/var/www/html/public/images/background-back.svg" - type: bind + type: "bind" read_only: true - source: "{{ _engine_config_dir }}/images/background-front.svg" target: "/var/www/html/public/images/background-front.svg" - type: bind + type: "bind" read_only: true - source: "{{ _engine_config_dir }}/images/logo.png" target: "/var/www/html/public/images/logo.png" - type: bind + type: "bind" read_only: true - source: "{{ _engine_config_dir }}/certs/" target: "/var/www/html/certs/" - type: bind + type: "bind" read_only: true - source: "/opt/openconext/common/favicon.ico" target: "/var/www/html/public/favicon.ico" - type: bind + type: "bind" read_only: true - source: "engineblock_sessions" target: "/tmp/" - type: volume + type: "volume" healthcheck: test: ["CMD-SHELL", "curl --fail -s http://localhost/internal/health | grep -q '\"status\":\"UP\"'"] - start_period: 60s - interval: 10s - timeout: 1s - retries: 20 + start_period: "60s" + interval: "10s" + timeout: "1s" + retries: "20" register: "engine_container" diff --git a/roles/engine/templates/parameters.yml.j2 b/roles/engine/templates/parameters.yml.j2 index 0c0d077f5..526104405 100644 --- a/roles/engine/templates/parameters.yml.j2 +++ b/roles/engine/templates/parameters.yml.j2 @@ -316,8 +316,8 @@ parameters: ########################################################################################## ## SBS external authorization/attribute enrichtment ########################################################################################## - sram.api_token: "{{ sbs_engine_block_api_token | default('') }}" - sram.base_url: "https://{{ sbs_base_domain | default('sbs.example.org') }}/api/users/" + sram.api_token: "{{ engine_sbs_api_token | default('') }}" + sram.base_url: "https://{{ engine_sbs_base_url }}/api/users/" sram.authz_location: "authz_eb" sram.attributes_location: "attributes_eb" sram.interrupt_location: "interrupt" diff --git a/roles/haproxy/defaults/main.yml b/roles/haproxy/defaults/main.yml index 9833699c4..90c1ea74d 100644 --- a/roles/haproxy/defaults/main.yml +++ b/roles/haproxy/defaults/main.yml @@ -45,3 +45,9 @@ haproxy_acmedns: password: "password" subdomain: "a_subdomain" fulldomain: "a_subdomain.acme-dns.example.org" + +# on which weekday (cron, 0==sunday) to run the renewal script +haproxy_acme_cronjob_dow: 1 + +# optional monitoring url for acme cron +haproxy_acme_cronjob_monitor_url: diff --git a/roles/haproxy/tasks/acme.yml b/roles/haproxy/tasks/acme.yml index 1ee9ab315..ed6ffed89 100644 --- a/roles/haproxy/tasks/acme.yml +++ b/roles/haproxy/tasks/acme.yml @@ -54,3 +54,21 @@ register: "acme_account" become: true become_user: "acme" + +- name: Remove default cronjob for renewal + ansible.builtin.file: + path: "/var/spool/cron/crontabs/acme" + state: "absent" + +- name: Install cronjob for renewal + ansible.builtin.copy: + dest: "/etc/cron.d/acme-renew" + owner: "root" + group: "root" + mode: "0644" + content: | + MAILTO=surfconext-beheer@surf.nl + 30 07 * * {{ haproxy_acme_cronjob_dow }} acme /home/acme/.acme.sh/acme.sh --cron --home /home/acme/.acme.sh > /dev/null + {%- if haproxy_acme_cronjob_monitor_url | default('') -%} + {# #} && curl -fsS --retry 3 -o /dev/null {{ haproxy_acme_cronjob_monitor_url }} + {%- endif %} diff --git a/roles/hosts/tasks/main.yml b/roles/hosts/tasks/main.yml index 14e36b308..3c8ce29c7 100644 --- a/roles/hosts/tasks/main.yml +++ b/roles/hosts/tasks/main.yml @@ -20,7 +20,6 @@ - "aa.vm.openconext.org" - "link.vm.openconext.org" - "connect.vm.openconext.org" - - "teams.vm.openconext.org" - "manage.vm.openconext.org" - name: Set logstash in hostsfile diff --git a/roles/invite/templates/logback.xml.j2 b/roles/invite/templates/logback.xml.j2 index 95ef4fe23..f8ccf8009 100644 --- a/roles/invite/templates/logback.xml.j2 +++ b/roles/invite/templates/logback.xml.j2 @@ -2,41 +2,41 @@ - - - %d{ISO8601} %5p [%t] %logger{40}:%L - %m%n - - + + + %d{ISO8601} %5p [%t] %logger{40}:%L - %m%n + + - - host.docker.internal:514 + + host.docker.internal:514 - {"app":"invite"} - true - - [ignore] - [ignore] - [ignore] - - - - invitejson: - - - + {"app":"invite"} + true + + [ignore] + [ignore] + [ignore] + + + + invitejson: + + + - - - - - + + + + + - - - {% if invite_logback_json | bool %} - - {%endif%} - + + + {% if invite_logback_json | bool %} + + {% endif %} + diff --git a/roles/invite/templates/serverapplication.yml.j2 b/roles/invite/templates/serverapplication.yml.j2 index bf59520da..6c943cf5c 100644 --- a/roles/invite/templates/serverapplication.yml.j2 +++ b/roles/invite/templates/serverapplication.yml.j2 @@ -75,13 +75,13 @@ crypto: private-key-location: file:///private_key_pkcs8.pem cron: + delay_min: 200000 + delay_max: 1200001 user-cleaner-cron: "PT30M" - user-cleaner-cron-initial-delay: "PT10M" user-cleaner-lock-at-least-for: "PT5M" user-cleaner-lock-at-most-for: "PT28M" last-activity-duration-days: 1000 role-expiration-notifier-cron: "PT30M" - role-expiration-notifier-cron-initial-delay: "PT15M" # Set to -1 to suppress role expiry notifications role-expiration-notifier-duration-days: 5 role-expiration-notifier-lock-at-least-for: "PT5M" @@ -149,47 +149,7 @@ feature: # We don't encode in-memory passwords, but they are reused so do NOT prefix them with {noop} external-api-configuration: remote-users: - - username: {{ invite.vootuser }} - password: "{{ invite.vootsecret }}" - scopes: - - voot - - username: {{ invite.teamsuser}} - password: "{{ invite.teamssecret }}" - scopes: - - teams - - username: {{ aa.invite_username }} - password: "{{ invite_attribute_aggregation_secret }}" - scopes: - - attribute_aggregation - - crm - - username: {{ invite.lifecycle_user }} - password: "{{ invite.lifecycle_secret }}" - scopes: - - lifecycle - - username: internal - password: "{{ invite.internal_secret }}" - scopes: - - actuator - - username: {{ invite.profile_user }} - password: "{{ invite.profile_secret }}" - scopes: - - profile - - username: {{ invite.sp_dashboard_user }} - password: "{{ invite.sp_dashboard_secret }}" - organizationGUIDFallback: {{ invite.surf_idp_organization_guid }} - scopes: - - sp_dashboard - applications: - - manageId: {{ invite.sp_dashboard_manage_id }} - manageType: SAML20_SP - - username: {{ invite.access_user }} - password: "{{ invite.access_secret }}" - organizationGUIDFallback: {{ invite.surf_idp_organization_guid }} - scopes: - - access - applications: - - manageId: {{ invite.access_manage_id }} - manageType: OIDC10_RP + {{ invite.api_users | to_nice_yaml(indent=2) | indent(4, first=false) }} voot: group_urn_domain: "{{ invite.group_urn_domain }}" diff --git a/roles/manage/defaults/main.yml b/roles/manage/defaults/main.yml index 41c6f34d4..1f616ebe9 100644 --- a/roles/manage/defaults/main.yml +++ b/roles/manage/defaults/main.yml @@ -32,7 +32,13 @@ manage_tabs_enabled: - provisioning - sram - organisation + - sfo + - institution manage_docker_networks: - name: loadbalancer manage_server_restart_policy: always manage_server_restart_retries: 0 +manage_logback_json: false + +manage_stepup_raas: + - "urn:collab:person:example.com:admin" diff --git a/roles/manage/files/metadata_templates/institution.template.json b/roles/manage/files/metadata_templates/institution.template.json new file mode 100644 index 000000000..7fc560d0e --- /dev/null +++ b/roles/manage/files/metadata_templates/institution.template.json @@ -0,0 +1,19 @@ +{ + "entityid": "", + "metaDataFields": {}, + "identifier": "", + "use_ra_locations": true, + "show_raa_contact_information": true, + "verify_email": true, + "allowed_second_factors": [ + "tiqr" + ], + "number_of_tokens_per_identity": 3, + "use_ra": [], + "use_raa": [], + "select_raa": [], + "self_vet": true, + "allow_self_asserted_tokens": false, + "sso_on_2fa": false, + "stepup-client": "full" +} diff --git a/roles/manage/files/metadata_templates/sfo.template.json b/roles/manage/files/metadata_templates/sfo.template.json new file mode 100644 index 000000000..82fb90649 --- /dev/null +++ b/roles/manage/files/metadata_templates/sfo.template.json @@ -0,0 +1,14 @@ +{ + "name": "", + "entityid": "", + "metaDataFields": {}, + "public_key": "", + "acs": [], + "loa": "{{ stepup_loa_values_supported[0] }}", + "assertion_encryption_enabled": false, + "second_factor_only": true, + "second_factor_only_nameid_patterns": [], + "blacklisted_encryption_algorithms": [], + "allow_sso_on_2fa": true, + "set_sso_cookie_on_2fa": true +} diff --git a/roles/manage/files/policies/allowed_attributes.json b/roles/manage/files/policies/allowed_attributes.json index beb5c8363..3905646f7 100644 --- a/roles/manage/files/policies/allowed_attributes.json +++ b/roles/manage/files/policies/allowed_attributes.json @@ -15,7 +15,8 @@ "value": "urn:mace:dir:attribute-def:eduPersonAffiliation", "validationRegex": "^(student|staff|faculty|employee|member)$", "allowedInDenyRule": true, - "label": "Edu person affiliation" + "label": "Edu person affiliation", + "enum": true }, { "value": "urn:mace:dir:attribute-def:eduPersonScopedAffiliation", @@ -45,7 +46,8 @@ "value": "urn:collab:sab:surfnet.nl", "validationRegex": "^(Superuser|Instellingsbevoegde|OperationeelBeheerder|SURFconextbeheerder|DNS-Beheerder)$", "allowedInDenyRule": false, - "label": "SAB role" + "label": "SAB role", + "enum": true }, { "value": "urn:mace:dir:attribute-def:mail", diff --git a/roles/manage/tasks/main.yml b/roles/manage/tasks/main.yml index 9df3ecb97..50cae20fb 100644 --- a/roles/manage/tasks/main.yml +++ b/roles/manage/tasks/main.yml @@ -11,6 +11,15 @@ - "/opt/openconext/manage/metadata_templates" - "/opt/openconext/manage/policies" +- name: Copy Stepup stepup_config.json from inventory + ansible.builtin.template: + src: "stepup_config.json.j2" + dest: "/opt/openconext/manage/stepup_config.json" + owner: "root" + group: "root" + mode: "0644" + notify: restart manageserver + - name: Import the mongo CA file ansible.builtin.copy: src: "{{ inventory_dir }}/secrets/mongo/mongoca.pem" @@ -31,14 +40,14 @@ - name: Place the serverapplication configfiles ansible.builtin.template: src: "{{ item }}.j2" - dest: /opt/openconext/manage/{{ item }} - owner: root - group: root + dest: "/opt/openconext/manage/{{ item }}" + owner: "root" + group: "root" mode: "0644" with_items: - - application.yml - - logback.xml - - manage-api-users.yml + - "application.yml" + - "logback.xml" + - "manage-api-users.yml" notify: restart manageserver - name: Place old __cacert_entrypoint.sh script @@ -53,8 +62,8 @@ ansible.builtin.template: src: "metadata_configuration/{{ item }}.schema.json.j2" dest: "/opt/openconext/manage/metadata_configuration/{{ item }}.schema.json" - owner: root - group: root + owner: "root" + group: "root" mode: "0640" with_items: - "{{ manage_tabs_enabled }}" @@ -81,18 +90,17 @@ group: root mode: "0640" with_items: - - allowed_attributes.json - - extra_saml_attributes.json + - "allowed_attributes.json" + - "extra_saml_attributes.json" notify: - "restart manageserver" - name: Add the mongodb and mariadb docker network to the list of networks when MongoDB runs in Docker ansible.builtin.set_fact: manage_docker_networks: - - name: loadbalancer - - name: openconext_mongodb - - name: openconext_mariadb - when: mongodb_in_docker | default(false) | bool + - name: "loadbalancer" + - name: "openconext_mariadb" + when: mariadb_in_docker | default(false) | bool - name: Create and start the server container community.docker.docker_container: @@ -105,15 +113,22 @@ state: started networks: "{{ manage_docker_networks }}" mounts: - - source: /opt/openconext/manage/ - target: /config/ - type: bind - - source: /opt/openconext/manage/mongoca.pem - target: /certificates/mongoca.crt - type: bind - - source: /opt/openconext/manage/__cacert_entrypoint.sh - target: /__cacert_entrypoint.sh - type: bind + - source: "/opt/openconext/manage/" + target: "/config/" + type: "bind" + read_only: true + - source: "/opt/openconext/manage/mongoca.pem" + target: "/certificates/mongoca.crt" + type: "bind" + read_only: true + - source: "/opt/openconext/manage/__cacert_entrypoint.sh" + target: "/__cacert_entrypoint.sh" + type: "bind" + read_only: true + - source: "/opt/openconext/manage/stepup_config.json" + target: "/stepup_config.json" + type: "bind" + read_only: true command: "java -jar /app.jar -Xmx512m --spring.config.location=./config/" etc_hosts: host.docker.internal: host-gateway @@ -170,6 +185,8 @@ - source: /etc/localtime target: /etc/localtime type: bind + read_only: true - source: /opt/openconext/common/favicon.ico target: /var/www/favicon.ico type: bind + read_only: true diff --git a/roles/manage/templates/application.yml.j2 b/roles/manage/templates/application.yml.j2 index aec21cfdf..31085a6d7 100644 --- a/roles/manage/templates/application.yml.j2 +++ b/roles/manage/templates/application.yml.j2 @@ -53,11 +53,20 @@ push: user: {{ pdp.username }} password: "{{ pdp.password }}" enabled: {{ manage.pdp_push_enabled }} + stepup: + url: https://middleware.{{ base_domain }} + user: {{ manage.middleware_user }} + configuration_file: "file:///stepup_config.json" + password: {{ manage_middleware_password }} + enabled: {{ manage.stepup_push_enabled }} + product: name: Manage organization: {{ instance_name }} service_provider_feed_url: {{ manage_service_provider_feed_url }} + jira_base_url: https://servicedesk.surf.nl/jira/browse/ + jira_ticket_prefixes: CXT,SD supported_languages: {{ supported_language_codes }} show_oidc_rp: {{ manage_show_oidc_rp_tab }} diff --git a/roles/manage/templates/logback.xml.j2 b/roles/manage/templates/logback.xml.j2 index d1df41a7a..5a567f793 100644 --- a/roles/manage/templates/logback.xml.j2 +++ b/roles/manage/templates/logback.xml.j2 @@ -1,32 +1,49 @@ -#jinja2:lstrip_blocks: True - - - %d{ISO8601} %5p [%t] %logger{40}:%L - %m%n - - + + + %d{ISO8601} %5p [%t] %logger{40}:%L - %m%n + + - - {{ smtp_server }} - {{ noreply_email }} - {{ error_mail_to }} - {{ error_subject_prefix }}Unexpected error manage - + + {{ smtp_server }} + {{ noreply_email }} + {{ error_mail_to }} + {{ error_subject_prefix }}Unexpected error manage + - org.everit.json.schema.ValidationException - ERROR - - + org.everit.json.schema.ValidationException + ERROR + + - - +{% if manage_logback_json | bool -%} + + host.docker.internal:514 + + {"app":"manage"} + true + + [ignore] + [ignore] + [ignore] + + + +{%- endif %} - - - - + + + + + + + {% if manage_logback_json | bool -%} + + {%- endif %} + diff --git a/roles/manage/templates/metadata_configuration/institution.schema.json.j2 b/roles/manage/templates/metadata_configuration/institution.schema.json.j2 new file mode 100644 index 000000000..ac8f66e83 --- /dev/null +++ b/roles/manage/templates/metadata_configuration/institution.schema.json.j2 @@ -0,0 +1,116 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "institution", + "order": 11, + "type": "object", + "properties": { + "eid": { + "type": "number" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "entityid": { + "type": "string", + "minLength": 1 + }, + "identifier": { + "type": "string", + "info": "The unique identifier of the institution." + }, + "use_ra_locations": { + "type": "boolean" + }, + "show_raa_contact_information": { + "type": "boolean" + }, + "verify_email": { + "type": "boolean" + }, + "allowed_second_factors": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "yubikey", + "tiqr", + "webauthn", + "sms" + ] + } + }, + "number_of_tokens_per_identity": { + "type": "number" + }, + "use_ra": { + "type": "array", + "items": { + "type": "string" + } + }, + "use_raa": { + "type": "array", + "items": { + "type": "string" + } + }, + "select_raa": { + "type": "array", + "items": { + "type": "string" + } + }, + "self_vet": { + "type": "boolean" + }, + "allow_self_asserted_tokens": { + "type": "boolean" + }, + "sso_on_2fa": { + "type": "boolean" + }, + "stepup-client": { + "type": "string", + "enum": [ + "freerider", + "full" + ], + "default": "freerider" + }, + + "revisionid": { + "type": "number" + }, + "created": { + "type": [ + "string", + "null" + ] + }, + "revisionnote": { + "type": "string" + }, + "notes": { + "type": [ + "string", + "null" + ] + }, + "metaDataFields": { + "type": "object", + "properties": {}, + "patternProperties": {}, + "required": [], + "additionalProperties": false + } + }, + "required": [ + "name", + "entityid", + "identifier", + "stepup-client" + ], + "additionalProperties": false, + "indexes": [] +} diff --git a/roles/manage/templates/metadata_configuration/oidc10_rp.schema.json.j2 b/roles/manage/templates/metadata_configuration/oidc10_rp.schema.json.j2 index 31f775b8f..da12914c5 100644 --- a/roles/manage/templates/metadata_configuration/oidc10_rp.schema.json.j2 +++ b/roles/manage/templates/metadata_configuration/oidc10_rp.schema.json.j2 @@ -394,11 +394,16 @@ "type": "boolean", "info": "Set to make invisible in the dashboard for identity providers." }, - "coin:application_url": { + "coin:login_url": { "type": "string", "format": "url", "info": "The URL of the service used to log on." }, + "coin:application_url": { + "type": "string", + "format": "url", + "info": "The URL of the service with general information." + }, "coin:application_name": { "type": "string", "info": "The name of the service / application in related applications." diff --git a/roles/manage/templates/metadata_configuration/saml20_sp.schema.json.j2 b/roles/manage/templates/metadata_configuration/saml20_sp.schema.json.j2 index b55972aee..9914fbef1 100644 --- a/roles/manage/templates/metadata_configuration/saml20_sp.schema.json.j2 +++ b/roles/manage/templates/metadata_configuration/saml20_sp.schema.json.j2 @@ -486,11 +486,16 @@ "type": "boolean", "info": "Set to make invisible in the dashboard for identity providers." }, - "coin:application_url": { + "coin:login_url": { "type": "string", "format": "url", "info": "The URL of the service used to log on." }, + "coin:application_url": { + "type": "string", + "format": "url", + "info": "The URL of the service with general information." + }, "coin:application_name": { "type": "string", "info": "The name of the service / application in related applications." diff --git a/roles/manage/templates/metadata_configuration/sfo.schema.json.j2 b/roles/manage/templates/metadata_configuration/sfo.schema.json.j2 new file mode 100644 index 000000000..a96c321e9 --- /dev/null +++ b/roles/manage/templates/metadata_configuration/sfo.schema.json.j2 @@ -0,0 +1,99 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "sfo", + "order": 10, + "type": "object", + "properties": { + "eid": { + "type": "number" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "entityid": { + "type": "string", + "minLength": 1 + }, + "public_key": { + "type": "string", + "format": "certificate", + "info": "The supplied public certificate of the institution. This must be a PEM encoded certificate. DER, CRT or CER are not supported." + }, + "acs": { + "type": "array", + "items": { + "type": "string", + "format": "url" + }, + "info": "The ACS locations of this institution." + }, + "loa": { + "type": "string", + "enum": [ + {% for loa in [stepup_intrinsic_loa] + stepup_loa_values_supported %} + "{{ loa }}"{{ "," if not loop.last else ""}} + {% endfor %} + ], + "default": "{{ stepup_loa_values_supported[0] }}", + }, + "assertion_encryption_enabled": { + "type": "boolean" + }, + "second_factor_only": { + "type": "boolean" + }, + "second_factor_only_nameid_patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "blacklisted_encryption_algorithms": { + "type": "array", + "items": { + "type": "string" + } + }, + "allow_sso_on_2fa": { + "type": "boolean" + }, + "set_sso_cookie_on_2fa": { + "type": "boolean" + }, + "revisionid": { + "type": "number" + }, + "created": { + "type": [ + "string", + "null" + ] + }, + "revisionnote": { + "type": "string" + }, + "notes": { + "type": [ + "string", + "null" + ] + }, + "metaDataFields": { + "type": "object", + "properties": {}, + "patternProperties": {}, + "required": [], + "additionalProperties": false + } + }, + "required": [ + "name", + "entityid", + "public_key", + "acs", + "loa" + ], + "additionalProperties": false, + "indexes": [] +} diff --git a/roles/manage/templates/metadata_configuration/single_tenant_template.schema.json.j2 b/roles/manage/templates/metadata_configuration/single_tenant_template.schema.json.j2 index 3733b6a47..344edf725 100644 --- a/roles/manage/templates/metadata_configuration/single_tenant_template.schema.json.j2 +++ b/roles/manage/templates/metadata_configuration/single_tenant_template.schema.json.j2 @@ -379,11 +379,16 @@ "type": "boolean", "info": "Set to indicate the service in the dashboard for institutions is marked as to have disagreed to sign the aansluitovereenkomst." }, - "coin:application_url": { + "coin:login_url": { "type": "string", "format": "url", "info": "The URL of the service used to log on." }, + "coin:application_url": { + "type": "string", + "format": "url", + "info": "The URL of the service with general information." + }, "coin:application_name": { "type": "string", "info": "The name of the service / application in related applications." diff --git a/roles/manage/templates/metadata_configuration/sram.schema.json.j2 b/roles/manage/templates/metadata_configuration/sram.schema.json.j2 index c4a7dc31b..8a84ca871 100644 --- a/roles/manage/templates/metadata_configuration/sram.schema.json.j2 +++ b/roles/manage/templates/metadata_configuration/sram.schema.json.j2 @@ -342,11 +342,16 @@ "info": "Select this option to skip the consent for a user.", "default": true }, - "coin:application_url": { + "coin:login_url": { "type": "string", "format": "url", "info": "The URL of the service used to log on." }, + "coin:application_url": { + "type": "string", + "format": "url", + "info": "The URL of the service with general information." + }, "coin:application_name": { "type": "string", "info": "The name of the service / application in related applications." diff --git a/roles/manage/templates/stepup_config.json.j2 b/roles/manage/templates/stepup_config.json.j2 new file mode 100644 index 000000000..b44e34d77 --- /dev/null +++ b/roles/manage/templates/stepup_config.json.j2 @@ -0,0 +1,44 @@ +{ + "sraa": {{ manage_stepup_raas | tojson() }}, +{# part below is a literal template that is to be filled in by Manage, not by anisble -#} +{% raw -%} + "email_templates": { + "confirm_email": { + "en_GB": "

Dear {{ commonName }},

Thank you for registering your token. Please visit this link to verify your email address:

{{ verificationUrl }}

If you can not click on the URL, please copy the link and paste it in the address bar of your browser.

SURFnet

", + "nl_NL": "

Beste {{ commonName }},

Bedankt voor het registreren van je token. Klik op onderstaande link om je e-mailadres te bevestigen:

{{ verificationUrl }}

Is klikken op de link niet mogelijk? Kopieer dan de link en plak deze in de adresbalk van je browser.

SURFnet

" + }, + "registration_code_with_ras": { + "en_GB": "

Dear {{ commonName }},

Thank you for registering your token. Please visit one of the locations below within 14 days to get your token activated. After {{ expirationDate | localizeddate('full', 'none', locale) }} your activation code is no longer valid.

Please bring the following:

  • Your token
  • A valid proof of identity (passport, drivers license or national ID-card)
  • The activation code from this e-mail

Activation code: {{ registrationCode }}

Location(s) to activate your token:

{% if ras is empty %}

No RAs are known.

{% else %}
    {% for ra in ras %}
  • {{ ra.commonName }}
    {{ ra.location }}
    {{ ra.contactInformation }}
  • {% endfor %}
{% endif %}", + "nl_NL": "

Beste {{ commonName }},

Bedankt voor het registreren van je token. Ga binnen 14 dagen naar een van de onderstaande locaties om je token te laten activeren. Je activatiecode is geldig tot en met {{ expirationDate | localizeddate('full', 'none', locale) }}.

Neem daarbij het volgende mee:

  • Je token
  • Een geldig legitimatiebewijs (paspoort, rijbewijs of nationale ID-kaart)
  • De activatiecode uit deze e-mail

Activatiecode: {{ registrationCode }}

Locatie(s) om je token te activeren:

{% if ras is empty %}

Er zijn geen RAs bekend.

{% else %}
    {% for ra in ras %}
  • {{ ra.commonName }}
    {{ ra.location }}
    {{ ra.contactInformation }}
  • {% endfor %}
{% endif %}" + }, + "second_factor_verification_reminder_with_ras": { + "en_GB": "

Dear {{ commonName }},

You have registered, but not yet activated, a token. Please visit one of the locations below to get your token activated. After {{ expirationDate | localizeddate('full', 'none', locale) }} your activation code is no longer valid.

Please bring the following:

  • Your token
  • A valid proof of identity (passport, drivers license or national ID-card).
  • The activation code from this e-mail

Activation code: {{ registrationCode }}

Location(s) to activate your token:

{% if ras is empty %}

No RAs are known.

{% else %}
    {% for ra in ras %}
  • {{ ra.commonName }}
    {{ ra.location }}
    {{ ra.contactInformation }}
  • {% endfor %}
{% endif %}", + "nl_NL": "

Beste {{ commonName }},

Je hebt een token geregistreerd, maar het nog niet laten activeren. Je kunt tot en met {{ expirationDate | localizeddate('full', 'none', locale) }} bij een van de onderstaande locaties terecht om je token te laten activeren.

Neem daarbij het volgende mee:

  • Je token
  • Een geldig legitimatiebewijs (paspoort, rijbewijs of nationale ID-kaart)
  • De activatiecode uit deze e-mail

Activatiecode: {{ registrationCode }}

Locatie(s) om je token te activeren:

{% if ras is empty %}

Er zijn geen RAs bekend.

{% else %}
    {% for ra in ras %}
  • {{ ra.commonName }}
    {{ ra.location }}
    {{ ra.contactInformation }}
  • {% endfor %}
{% endif %}" + }, + "registration_code_with_ra_locations": { + "en_GB": "

Dear {{ commonName }},

Thank you for registering your token. Please visit one of the locations below within 14 days to get your token activated. After {{ expirationDate | localizeddate('full', 'none', locale) }} your activation code is no longer valid.

Please bring the following:

  • Your token
  • A valid proof of identity (passport, drivers license or national ID-card)
  • The activation code from this e-mail

Activation code: {{ registrationCode }}

Location(s) to activate your token:

{% if raLocations is empty %}

No locations known.

{% else %}
    {% for ra in raLocations %}
  • {{ ra.name }}
    {{ ra.location }}
    {{ ra.contactInformation }}
  • {% endfor %}
{% endif %}", + "nl_NL": "

Beste {{ commonName }},

Bedankt voor het registreren van je token. Ga binnen 14 dagen naar een van de onderstaande locaties om je token te laten activeren. Je activatiecode is geldig tot en met {{ expirationDate | localizeddate('full', 'none', locale) }}.

Neem daarbij het volgende mee:

  • Je token
  • Een geldig legitimatiebewijs (paspoort, rijbewijs of nationale ID-kaart)
  • De activatiecode uit deze e-mail

Activatiecode: {{ registrationCode }}

Locatie(s) om je token te activeren:

{% if raLocations is empty %}

Er zijn geen Locaties bekend.

{% else %}
    {% for ra in raLocations %}
  • {{ ra.name }}
    {{ ra.location }}
    {{ ra.contactInformation }}
  • {% endfor %}
{% endif %}" + }, + "second_factor_verification_reminder_with_ra_locations": { + "en_GB": "

Dear {{ commonName }},

You have registered, but not yet activated, a token. Please visit one of the locations below to get your token activated. After {{ expirationDate | localizeddate('full', 'none', locale) }} your activation code is no longer valid.

Please bring the following:

  • Your token
  • A valid proof of identity (passport, drivers license or national ID-card)
  • The activation code from this e-mail

Activation code: {{ registrationCode }}

Location(s) to activate your token:

{% if raLocations is empty %}

No locations known.

{% else %}
    {% for ra in raLocations %}
  • {{ ra.name }}
    {{ ra.location }}
    {{ ra.contactInformation }}
  • {% endfor %}
{% endif %}", + "nl_NL": "

Beste {{ commonName }},

Je hebt een token geregistreerd, maar het nog niet laten activeren. Je kunt tot en met {{ expirationDate | localizeddate('full', 'none', locale) }} bij een van de onderstaande locaties terecht om je token te laten activeren.

Neem daarbij het volgende mee:

  • Je token
  • Een geldig legitimatiebewijs (paspoort, rijbewijs of nationale ID-kaart)
  • De activatiecode uit deze e-mail

Activatiecode: {{ registrationCode }}

Locatie(s) om je token te activeren:

{% if raLocations is empty %}

Er zijn geen Locaties bekend.

{% else %}
    {% for ra in raLocations %}
  • {{ ra.name }}
    {{ ra.location }}
    {{ ra.contactInformation }}
  • {% endfor %}
{% endif %}" + }, + "vetted": { + "en_GB": "

Dear {{ commonName }},

Thank you for registering your token. Your token is ready to use. You can use this token for services connected to SURFconext that require two-step authentication. This e-mail contains more info on how to use your token.

Handle your token with care

  • Your token is private, do not share your token with others
  • Never leave your token unattended
  • Lock your phone, e.g. with a code or fingerprint

Token lost?
Did you lose your token? Please visit {{ selfServiceUrl }} and remove your token registration. This way no one can take advantage of your token.

Replace token
Do you want to replace your token? Please visit {{ selfServiceUrl }}, remove your activated token and start the token registration process again.

Test token
Do you want to test your token? Please visit {{ selfServiceUrl }} and select the \"Test\" button next to the token you want to test.

", + "nl_NL": "

Beste {{ commonName }},

Bedankt voor het registreren van je token. Je token is nu klaar voor gebruik. Je kunt dit token gebruiken wanneer op SURFconext aangesloten services een tweede inlogstap vereisen. In deze e-mail vind je meer informatie over het gebruik van je token.

Ga zorgvuldig om met je token

  • Je token is persoonlijk, deel deze niet met anderen
  • Laat je token nooit onbeheerd achter
  • Vergrendel je telefoon met bijvoorbeeld een code of vingerafdruk

Token verloren?
Wat moet je doen als je jouw token verloren bent? Ga naar {{ selfServiceUrl }} en verwijder je tokenregistratie. Zo kan niemand misbruik maken van jouw token.

Nieuw token aanvragen
Wil je jouw token vervangen? Log in op {{ selfServiceUrl }}, verwijder je geactiveerde token en doorloop het registratieproces opnieuw.

Token testen
Wil je de werking van je token testen? Log in op {{ selfServiceUrl }} en selecteer de \"Testen\" knop naast het token dat je wil testen.

" + }, + "second_factor_revoked": { + "en_GB": "

Dear {{ commonName }},

{% if isRevokedByRa %} The registration of your {{ tokenType }} with ID {{ tokenIdentifier }} was deleted by an administrator. {% else %} You have deleted the registration of your {{ tokenType }} token with ID {{ tokenIdentifier }}. If you did not delete your token you must immediately contact the support desk of your institution, as this may indicate that your account has been compromised. {% endif %}

You can no longer use this token to access SURFconext services that require two-step authentication.

Do you want to replace your token? Please visit {{ selfServiceUrl }} and register a new token.

", + "nl_NL": "

Beste {{ commonName }},

{% if isRevokedByRa %} De registratie van je {{ tokenType }} token met ID {{ tokenIdentifier }} is verwijderd door een beheerder. {% else %} Je hebt de registratie voor je {{ tokenType }} token met ID {{ tokenIdentifier }} verwijderd. Neem direct contact op met de helpdesk van je instelling als je dit zelf niet gedaan hebt, omdat dit kan betekenen dat je account gecompromitteerd is. {% endif %}

Je kunt dit token niet meer gebruiken om in te loggen bij op SURFconext aangesloten services die een tweede inlogstap vereisen.

Wil je een nieuw token aanvragen? Ga dan naar {{ selfServiceUrl }} en doorloop het registratieproces opnieuw.

" + }, + "recovery_token_created": { + "en_GB": "

Dear {{ commonName }},

Thank you for registering a recovery method. You can use this method if you want to reactivate a SURFsecureID token that you have lost.

Always make sure you have at least one recovery method available.

Best regards,

SURFnet

", + "nl_NL": "

Beste {{ commonName }},

Bedankt voor het registreren een herstelmethode. Je kunt deze methode gebruiken wanneer je een SURFsecureID token dat je verloren bent opnieuw wilt activeren.

Zorg er altijd voor dat je tenminste één herstelmethode beschikbaar hebt

Met vriendelijke groet,

SURF

" + }, + "recovery_token_revoked": { + "en_GB": "

Dear {{ commonName }},

{% if isRevokedByRa %} Your SURFsecureID recovery method was removed by an administrator. {% else %} Your SURFsecureID recovery method has been removed. Please contact your institution's helpdesk immediately if you did not do this yourself, as this could mean that your account has been compromised. {% endif %}

You can no longer use this recovery method to activate a SURFsecureID token.

Always make sure you have at least one recovery method available.

Best regards,

SURF

", + "nl_NL": "

Beste {{ commonName }},

{% if isRevokedByRa %} Je SURFsecureID herstelmethode is verwijderd door een beheerder. {% else %} Je SURFsecureID herstelmethode is verwijderd. Neem direct contact op met de helpdesk van je instelling als je dit niet zelf gedaan hebt, omdat dit kan betekenen dat je account gecompromitteerd is. {% endif %}

Je kunt deze herstelmethode niet meer gebruiken om een SURFsecureID token te activeren.

Zorg er altijd voor dat je tenminste één herstelmethode beschikbaar hebt.

Met vriendelijke groet,

SURF

" + } + } +} +{% endraw %} diff --git a/roles/mariadbdocker/tasks/main.yml b/roles/mariadbdocker/tasks/main.yml index 8f3d92b7e..8945d7e6f 100644 --- a/roles/mariadbdocker/tasks/main.yml +++ b/roles/mariadbdocker/tasks/main.yml @@ -30,7 +30,7 @@ - name: Create the MariaDB container community.docker.docker_container: name: openconext_mariadb - image: mariadb:10.6 + image: mariadb:10.11 state: started pull: true restart_policy: "always" diff --git a/roles/mongo/README.md b/roles/mongo/README.md index 9e96770e5..8ad896d21 100644 --- a/roles/mongo/README.md +++ b/roles/mongo/README.md @@ -14,6 +14,34 @@ Set the mongo_cluster_private_key variable encrypted in host_vars Please review the official Mongo documentation for more information. +# Mongo deployment + +To avoid surprisesyou can enable or disable cluster configuration with the boolean option mongo_configure_cluster. The role willonly initiate or reconfigure cluster if this is true (safest option is to use -e mongo_configure_cluster=true with your deployment when cluster configuration is necessary). +Another issue is the serial value, it is safest to set it to 1 in your playbook, if it is higher multiple mongo nodes can will be restarted at once and it can break your cluster. However when you want to intialise a new cluster you need to run the tasks parallel and serial needs to be as high as the amount of nodes. We handled this with a variable serial with the name serial_number in our playbook with a default 1. If cluster initialisation or reconfiguration is necessary use -e "serial_number=" + +Another option is creating separate playbooks for cluster adjustments/creation and for configuring non cluster related mongo settings. + + +See also https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_strategies.html#setting-the-batch-size-with-serial + +# Cluster reconfiguration + +Warning: the cluster reconfiguration option in the mongodb_replicationset module is experimental. and you can only add or remove one node at a time. + # Todo -- [ ] Add the possibility for adding and removing cluster members -- [ ] Add the possibility for a standalone mongo server +- [x] Check mongo_replication_roles and give a clear fail message when not set +- [ ] Add option to change the already existing admin user, for now change the password manually and change it in the ansible config accordingly +- [x] Add the possibility for adding and removing cluster members +- [x] Add the possibility for a standalone mongo server +- [x] Cluster changes can be enabled or disabled +- [ ] Reconfigure cluster always reports changed +- [ ] Initialise cluster always reports changed +- [ ] check mode for writeconcern change tasks does not report change () same for any other mongodb_shell task "remote module (community.mongodb.mongodb_shell) does not support check mode"} +- [X] Clearer error messaging for even number of votes +- [X] Role refuses to add users when a new cluster is built (3 nodes) (cannot add users on a broken cluster) +- [X] it would be helpfull if role (for example primary) is not defined in host_vars but in the mongo_cluster_members array +- [X] removing primary from the cluster will not work but the error is unclear, this is related to the todo above +- [ ] is it necessary to make votes configurable? +- [X] preflight check are cluster members in the inventory and monog_servers group +- [ ] Standalone mongo also requires cluster certificates, not logical although it doens't hurt +- [ ] Changes to mongo users are executed but not reported as an ansible change diff --git a/roles/mongo/defaults/main.yml b/roles/mongo/defaults/main.yml index a58b2a320..d96741302 100644 --- a/roles/mongo/defaults/main.yml +++ b/roles/mongo/defaults/main.yml @@ -13,35 +13,37 @@ mongo_servers: [] # Set this in group_vars # Not all mongo servers in the inventory are cluster members, so we use a separate list for this. # Set this in group_vars of your environment(s). The arbiter should go first, or change the mongo_arbiter_index. # mongo_cluster_members: -# - host: "mongoarbiter.example.com:27017" +# - host: "mongoarbiter.example.com" # priority: 1 # can vote, cannot become primary -# - host: "mongo2.example.com:27017" +# port: 27017 +# - host: "mongo2.example.com" # priority: 2 -# - host: "mongo1.example.com:27017" +# port: 27017 +# - host: "mongo1.example.com" # priority: 3 -# mongo_arbiter_index: 0 - -# The replication role -# mongo_replication_role: # Set this in host_vars, it can have the values: "primary", "secondary" or arbiter +# port: 27017 # Todo: there is a link between mongo_replication_role and priority (arbiter is priority 1, primary the highest) so # setting them separately is not ideal. # The port for mongo server -mongod_port: 27017 +mongo_port: 27017 # The password for admin user -mongo_admin_pass: "{{ mongo_admin_password }}" # Set this in secrets +# mongo_admin_password: # set this in secrets + +# Are we using a cluster? +mongo_mode: "cluster" # cluster or standalone # The name of the replication set -replica_set_name: "{{ instance_name }}" # Set this in group_vars +mongo_replica_set_name: "{{ instance_name }}" # Set this in group_vars # Add a database mongo: users: - - { name: managerw, db_name: metadata, password: "{{ mongo_passwords.manage }}" } - - { name: oidcsrw, db_name: oidc, password: "{{ mongo_passwords.oidcng }}" } - - { name: myconextrw, db_name: myconext, password: "{{ mongo_passwords.myconext }}" } + - { name: managerw, db_name: metadata, password: "{{ mongo_passwords.manage }}", role: "readWrite" } + - { name: oidcsrw, db_name: oidc, password: "{{ mongo_passwords.oidcng }}", role: "readWrite"} + - { name: myconextrw, db_name: myconext, password: "{{ mongo_passwords.myconext }}", role: "readWrite" } # Listen on all addresses by default mongo_bind_listen_address: "0.0.0.0" @@ -53,3 +55,32 @@ mongo_pki_dir: "/etc/pki/mongo" # Users and groups mongo_group: "mongod" + +# Paths +mongo_config_file: "/etc/mongod.conf" +mongo_data_path: "/var/lib/mongo" + +mongo_pymongo_version: 4.16.0 + +# cluster members +# set in group_vars +# mongo_cluster_members: +# - host: mongo1.example.com +# priority: 3 +# votes: 1 +# port: 27017 +# - host: mongo2.example.com +# priority: 2 +# votes: 1 +# port: 27017 +# - host: mongo3.example.com +# priority: 1 +# votes: 1 +# port: 27017 +# arbiterOnly: true + +mongo_cluster_write_concern: "majority" +mongo_cluster_write_timeout: 5000 + +# to avoid surprises only initiate or reconfigure cluster if this is true (safest option is to use -e mongo_configure_cluster=true with your deployment when cluster configuration is necessary) +mongo_configure_cluster: false diff --git a/roles/mongo/tasks/clusterconfig.yml b/roles/mongo/tasks/clusterconfig.yml index ced96e9b9..dd819d3da 100644 --- a/roles/mongo/tasks/clusterconfig.yml +++ b/roles/mongo/tasks/clusterconfig.yml @@ -1,60 +1,191 @@ --- -# todo this weorks only for new deployments -# rewrite so mongo config can be changed and cluster members can be added or removed -- name: Check if hosts are in clustered - ansible.builtin.command: mongosh --port {{ mongod_port }} --quiet --eval 'db.isMaster().hosts' - register: check_cluster - changed_when: false - check_mode: false - -- name: Debug check_cluster variable +# In this task file the cluster is configured + +# priority moet matchen met replication role, of replication role uit cluster mebers halen? +# todo write concern zetten + +# Do some preflight checks +- name: Check some cluster related variables + when: mongo_mode == "cluster" + block: + - name: Fail on undefined mongo_replica_set_name + when: mongo_replica_set_name is not defined + ansible.builtin.fail: + msg: "Something is wrong, mongo_mode was set to cluster but mongo_replica_set_name is undefined." + +- name: Debug replica settings ansible.builtin.debug: - msg: "{{ check_cluster }}" + msg: "Replica set name {{ mongo_replica_set_name }}" verbosity: 2 -- name: Debug mongo_cluster_members variable +# Loop over cluster members and check their presence in mong_servers group and their mode (not standalone) + +- name: Check if mongo_cluster_members exist in inventory group + ansible.builtin.assert: + that: + - item.host in groups['mongo_servers'] + fail_msg: "Server '{{ item.host }}' is not in the mongo_servers inventory group" + success_msg: "Server '{{ item.host }}' found in mongo_servers inventory group" + run_once: true + loop: "{{ mongo_cluster_members }}" + +# Loop over cluster members and check for primary + +- name: Set primary host fact + ansible.builtin.set_fact: + mongo_primary_host: "{{ (mongo_cluster_members | max(attribute='priority')).host }}" + +- name: Debug primary settings ansible.builtin.debug: - msg: "{{ mongo_cluster_members }}" + msg: "Primary is {{ mongo_primary_host }}" verbosity: 2 -- name: Debug mongo_replication_role variable +# What is the replication role of the current host +- name: Debug replication role settings ansible.builtin.debug: - msg: "{{ mongo_replication_role }}" + msg: "This nodes replication role is {{ mongo_replication_role }}" verbosity: 2 -- name: Initial cluster initialisation - community.mongodb.mongodb_replicaset: - login_host: localhost - login_user: admin - login_port: "{{ mongod_port }}" - login_password: "{{ mongo_admin_password }}" - replica_set: "{{ replica_set_name }}" - members: "{{ mongo_cluster_members }}" - arbiter_at_index: "{{ mongo_arbiter_index | default(0) }}" - validate: false - run_once: true - when: mongo_replication_role == 'primary' +# Cannot initialise a cluster without starting....... +- name: Enable and start mongod + ansible.builtin.service: + name: mongod.service + enabled: true + state: started -- name: Wait until cluster health is ok - community.mongodb.mongodb_status: - login_user: admin - login_password: "{{ mongo_admin_password }}" - login_database: admin - login_port: "{{ mongod_port }}" - validate: default - poll: 5 - interval: 12 - replica_set: "{{ replica_set_name }}" +# Initialise cluster block +- name: Initialise or reconfigure cluster block when: mongo_replication_role == 'primary' + block: + - name: Check if replica set is already initialised + community.mongodb.mongodb_shell: + login_host: localhost + login_user: admin + login_port: "{{ mongo_port }}" + login_password: "{{ mongo_admin_password }}" + eval: "rs.status().ok" + db: admin + register: rs_already_init + ignore_errors: true -- name: Add the admin user - community.mongodb.mongodb_user: - database: admin - name: admin - password: "{{ mongo_admin_password }}" - login_port: "{{ mongod_port }}" - roles: root - state: present - when: check_cluster.stdout == "" - no_log: true - run_once: true + - name: Debug cluster initialization check + ansible.builtin.debug: + msg: "{{ rs_already_init }}" + verbosity: 2 + + # This should be possible with community.mongodb.mongodb_replicaset + # But we keep getting authenticatione error so leave it like this for now + - name: Initialise replica set if necessary + community.mongodb.mongodb_shell: + login_host: localhost + login_user: admin + login_port: "{{ mongo_port }}" + login_password: "{{ mongo_admin_password }}" + eval: | + rs.initiate({ + _id: "{{ mongo_replica_set_name }}", + members: [ + {% for m in mongo_cluster_members %} + { _id: {{ loop.index0 }}, host: "{{ m.host }}:{{ m.port }}", priority: {{ m.priority }}, votes: {{ m.votes }}{% if m.arbiterOnly is defined and m.arbiterOnly and m.arbiterOnly == true %}, arbiterOnly: true {% endif %} }{{ "," if not loop.last else "" }} + {% endfor %} + ] + }) + db: admin + when: rs_already_init.failed + register: rs_init + + - name: Debug cluster initialization + ansible.builtin.debug: + msg: "{{ rs_init }}" + verbosity: 2 + + - name: Format members list + ansible.builtin.set_fact: + mongo_cluster_members_formatted: "{{ mongo_cluster_members_formatted | default([]) + [m | combine({'host': m.host ~ ':' ~ (m.port | string)}) | dict2items | rejectattr('key', 'eq', 'port') | list | items2dict] }}" + loop: "{{ mongo_cluster_members }}" + loop_control: + loop_var: m + + - name: Debug members list + ansible.builtin.debug: + msg: "{{ mongo_cluster_members }}" + verbosity: 2 + + - name: Debug formatted members list + ansible.builtin.debug: + msg: "{{ mongo_cluster_members_formatted }}" + verbosity: 2 + + # Reconfigure cluster + # todo: this always returns changed even when nothing changes + - name: Reconfigure cluster if necessary + community.mongodb.mongodb_replicaset: + login_host: localhost + login_user: admin + login_password: "{{ mongo_admin_password }}" + login_port: "{{ mongo_port }}" + reconfigure: true + replica_set: "{{ mongo_replica_set_name }}" + members: "{{ mongo_cluster_members_formatted }}" + register: rs_reconfigure + + - name: Debug cluster reconfiguration + ansible.builtin.debug: + msg: "{{ rs_reconfigure }}" + verbosity: 2 + + - name: Wait for the replicaset to stabilise + community.mongodb.mongodb_status: + replica_set: "{{ mongo_replica_set_name }}" + login_host: localhost + login_user: admin + login_password: "{{ mongo_admin_password }}" + login_port: "{{ mongo_port }}" + poll: 5 + interval: 30 + validate: minimal # default fails on even number of servers and although this is not a great situation, it is sometimes the temporary situation because we can onlye add or remove 1 node at a time + + # Cluster settings that cannot be changed with mongodb_replicaset + + - name: Get current default write concern + community.mongodb.mongodb_shell: + login_host: localhost + login_port: 27017 + login_user: admin + login_password: "{{ mongo_admin_password }}" + eval: "db.adminCommand({ getDefaultRWConcern: 1 })" + register: current_write_concern + changed_when: false + + - name: Debug write concern check + ansible.builtin.debug: + msg: "{{ current_write_concern.transformed_output.defaultWriteConcern }}" + verbosity: 2 + when: current_write_concern.transformed_output.defaultWriteConcern is defined + + - name: Set default write concern + when: > + current_write_concern.transformed_output.defaultWriteConcern is defined + and + (current_write_concern.transformed_output.defaultWriteConcern.w | string != mongo_cluster_write_concern | default('majority') | string + or + current_write_concern.transformed_output.defaultWriteConcern.wtimeout | int != mongo_cluster_write_timeout | default(5000) | int) + or current_write_concern.transformed_output.defaultWriteConcern is not defined + block: + - name: "set write concern majority" + when: mongo_cluster_write_concern == "majority" + community.mongodb.mongodb_shell: + login_host: localhost + login_user: admin + login_password: "{{ mongo_admin_password }}" + login_port: "{{ mongo_port }}" + eval: "db.adminCommand({ setDefaultRWConcern: 1, defaultWriteConcern: { w: \"{{ mongo_cluster_write_concern | default('majority') }}\", wtimeout: {{ mongo_cluster_write_timeout | default(5000) }} } })" + # could not get this to work with either majority with quotes or number without quotes so for now an ugly fix + - name: "set write concern numeric" + when: mongo_cluster_write_concern != "majority" + community.mongodb.mongodb_shell: + login_host: localhost + login_user: admin + login_password: "{{ mongo_admin_password }}" + login_port: "{{ mongo_port }}" + eval: "db.adminCommand({ setDefaultRWConcern: 1, defaultWriteConcern: { w: {{ mongo_cluster_write_concern | default('majority') }}, wtimeout: {{ mongo_cluster_write_timeout | default(5000) }} } })" diff --git a/roles/mongo/tasks/clusterhealthcheck.yml b/roles/mongo/tasks/clusterhealthcheck.yml new file mode 100644 index 000000000..aa129ed5b --- /dev/null +++ b/roles/mongo/tasks/clusterhealthcheck.yml @@ -0,0 +1,88 @@ +--- +# task file to check if cluster is up and running + +- name: Cluster check when we are not in cluster config mode + when: not mongo_configure_cluster | bool + block: + # Get the replicaset status and fail on minimal (everything but even number of nodes) + - name: Check replicaset status + community.mongodb.mongodb_status: + login_host: localhost + login_user: admin + login_port: "{{ mongo_port }}" + login_password: "{{ mongo_admin_password }}" + replica_set: "{{ mongo_replica_set_name }}" + poll: 3 + interval: 10 + register: replica_status + ignore_errors: true + + - name: Debug replica set status + ansible.builtin.debug: + msg: "{{ replica_status }}" + verbosity: 2 + + # Message for non cluster config mode + - name: Fail when there is no cluster reconfiguration options set + ansible.builtin.fail: + msg: "Your mongo cluster is broken or non existent and mongo_configure_cluster is disabled, consider enabling it and fix your cluster. The error: {{ replica_status.msg }}." + when: + - replica_status.failed + +- name: Cluster check when we are in cluster config mode + when: mongo_configure_cluster | bool + block: + # Get the replicaset status and fail on minimal (everything but even number of nodes) + - name: Check replicaset status minimal + community.mongodb.mongodb_status: + login_host: localhost + login_user: admin + login_port: "{{ mongo_port }}" + login_password: "{{ mongo_admin_password }}" + replica_set: "{{ mongo_replica_set_name }}" + poll: 3 + interval: 10 + validate: minimal + register: replica_status_minimal + ignore_errors: true + + # Message for cluster config mode gone wrong + - name: Fail when you misconfigured your replica cluster + ansible.builtin.fail: + msg: "Your mongo cluster is broken, error: {{ replica_status_minimal.msg }}." + when: + - replica_status_minimal.failed + + # Get the replicaset status votes + - name: Check replicaset status votes + community.mongodb.mongodb_status: + login_host: localhost + login_user: admin + login_port: "{{ mongo_port }}" + login_password: "{{ mongo_admin_password }}" + replica_set: "{{ mongo_replica_set_name }}" + poll: 3 + interval: 10 + validate: votes + register: replica_status_votes + ignore_errors: true + + # Message for cluster config mode wrong amount of votes + - name: Fail when you misconfigured your replica cluster + ansible.builtin.fail: + msg: | + Your mongo cluster doesn't have the right amount of members, + perhaps you are adding new nodes one by one, + in that case add the next node to cluster_members and run the play again. + The error message is: {{ replica_status_votes.msg }}." + when: + - replica_status_votes.failed + + # In non cluster config mode we use replica_status + # and here replica_status_votes and replica_status_minimal + # for better error messages, but we need a general replica_status + # for for example users.yml, so lets set it here + - name: Set a value for replica_status + ansible.builtin.set_fact: + replica_status: + failed: False diff --git a/roles/mongo/tasks/generalconfig.yml b/roles/mongo/tasks/generalconfig.yml new file mode 100644 index 000000000..7bc094d4f --- /dev/null +++ b/roles/mongo/tasks/generalconfig.yml @@ -0,0 +1,52 @@ +--- + +- name: Enable and start mongod for the first time + ansible.builtin.service: + name: mongod.service + enabled: true + state: started + +- name: Check if mongodb authentication is activated + ansible.builtin.shell: + cmd: "mongosh 'mongodb://127.0.0.1:{{ mongo_port }}/admin' --eval 'db.runCommand({ usersInfo: 1 })'" + register: mongo_authentication_disabled + changed_when: false + ignore_errors: true + check_mode: false # This can safely run in check mode because it is not changing anything + failed_when: mongo_authentication_disabled.rc > 1 # rc=1 means command failed because authentication is enabled, we need to know that but we don't need to see an error + +- name: Debug mongodb authentication check + ansible.builtin.debug: + msg: "{{ mongo_authentication_disabled }}" + verbosity: 2 + +- name: configure primary or standalone + when: mongo_mode == "standalone" or mongo_replication_role == "primary" + block: + # first run add admin user without logging in + - name: Add the admin user + community.mongodb.mongodb_user: + login_database: admin + database: admin + name: admin + password: "{{ mongo_admin_password }}" + login_port: "{{ mongo_port }}" + roles: root + state: present + # todo enable no_log: true + when: mongo_authentication_disabled.rc == 0 + +# Config for standalone and replication server +- name: Install mongodb.conf file + ansible.builtin.template: + src: "mongod.conf.j2" + dest: "/etc/mongod.conf" + owner: root + group: root + mode: "0644" + backup: true + notify: Restart mongod + +# restart mongo right away with authentication enabled +- name: Flush handlers + ansible.builtin.meta: flush_handlers \ No newline at end of file diff --git a/roles/mongo/tasks/install.yml b/roles/mongo/tasks/install.yml index 673d465e3..9103a2ca8 100644 --- a/roles/mongo/tasks/install.yml +++ b/roles/mongo/tasks/install.yml @@ -1,6 +1,5 @@ --- - name: Create the repository for mongodb - when: ansible_os_family == 'RedHat' ansible.builtin.template: src: "mongo.repo.j2" dest: "/etc/yum.repos.d/mongo.repo" @@ -8,7 +7,6 @@ mode: "0640" - name: Install the mongodb package and some helper packages - when: ansible_os_family == 'RedHat' ansible.builtin.yum: name: - mongodb-org @@ -17,7 +15,7 @@ - name: Install pymongo ansible.builtin.pip: - name: pymongo + name: pymongo=={{ mongo_pymongo_version }} - name: Install kernel settings script ansible.builtin.copy: @@ -52,17 +50,3 @@ value: 128000 state: present -- name: Install mongodb.conf file - ansible.builtin.template: - src: "mongod.conf.j2" - dest: "/etc/mongod.conf" - owner: root - group: root - mode: "0644" - notify: Restart mongod - -- name: Enable and start mongod - ansible.builtin.service: - name: mongod.service - enabled: true - state: started diff --git a/roles/mongo/tasks/main.yml b/roles/mongo/tasks/main.yml index b0a30b31f..d55d8fa8a 100644 --- a/roles/mongo/tasks/main.yml +++ b/roles/mongo/tasks/main.yml @@ -1,8 +1,26 @@ --- +# Main task file mongo role + +- name: Message for non redhat family servers + when: ansible_facts['os_family'] != 'RedHat' + ansible.builtin.fail: + msg: "Sorry, this role only works on RedHat family servers" + - name: Install and configure mongo on redhat family servers - when: ansible_os_family == 'RedHat' + when: + - ansible_facts['os_family'] == 'RedHat' block: - - name: Use temporarily python3 as remote interpreter, this fixes pymongo + - name: Debug standalone or cluster mode + ansible.builtin.debug: + msg: "{{ mongo_mode }}" + verbosity: 2 + + - name: Debug cluster reconfiguration is allowed + ansible.builtin.debug: + msg: "{{ mongo_configure_cluster }}" + verbosity: 2 + + - name: Use temporarily python3 as remote interpreter, this fixes pymongo # todo is this still necessary? ansible.builtin.set_fact: ansible_python_interpreter: "/usr/bin/python3" tags: mongo_users @@ -11,30 +29,47 @@ ansible.builtin.include_tasks: file: install.yml - - ansible.builtin.meta: flush_handlers - - name: Include Certificate tasks ansible.builtin.include_tasks: file: certs.yml - # - name: Include cluster installation tasks - # ansible.builtin.include_tasks: - # file: clusterconfig.yml + - name: Include General config tasks + ansible.builtin.include_tasks: + file: generalconfig.yml + + - name: Include cluster configuration tasks + ansible.builtin.include_tasks: + file: clusterconfig.yml + when: + - mongo_mode == "cluster" + - mongo_configure_cluster | bool # safest option is to set this to false and enable with -e mongo_configure_cluster=true + + - name: Include cluster health check tasks + ansible.builtin.include_tasks: + file: clusterhealthcheck.yml + when: + - mongo_mode == "cluster" + - mongo_replication_role == 'primary' + + - name: Include Service tasks + ansible.builtin.include_tasks: + file: services.yml - name: Include user creation ansible.builtin.include_tasks: file: users.yml + # Cannot add users on a broken cluster + when: > + (mongo_mode == 'cluster' and mongo_replication_role is defined and mongo_replication_role == 'primary' and not replica_status.failed) + or mongo_mode == 'standalone' - name: Include postinstallation tasks ansible.builtin.include_tasks: file: postinstall.yml - - name: Use python2 again as remote interpreter + - name: Use python2 again as remote interpreter on centos 7 ansible.builtin.set_fact: ansible_python_interpreter: "/usr/bin/python" - when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7' - -- name: Message for non redhat family servers - when: ansible_os_family != 'RedHat' - ansible.builtin.debug: - msg: "Sorry, this role only works on RedHat family servers" + when: + - ansible_facts['distribution'] == 'CentOS' + - ansible_facts['distribution_major_version'] == '7' diff --git a/roles/mongo/tasks/postinstall.yml b/roles/mongo/tasks/postinstall.yml index e474a0b1e..027d05f1c 100644 --- a/roles/mongo/tasks/postinstall.yml +++ b/roles/mongo/tasks/postinstall.yml @@ -15,29 +15,33 @@ group: root mode: "0700" -- name: Install the backup script - ansible.builtin.template: - src: "backup_mongo.pl.j2" - dest: "/usr/local/sbin/backup_mongo.pl" - mode: "0700" - owner: root - group: root - when: mongo_replication_role != 'arbiter' +- name: Configure backup + when: mongo_replication_role is not defined or mongo_replication_role != 'arbiter' + block: + - name: Install the backup script + ansible.builtin.template: + src: "backup_mongo.pl.j2" + dest: "/usr/local/sbin/backup_mongo.pl" + mode: "0700" + owner: root + group: root + - name: Create cron symlink for backup script + ansible.builtin.file: + src: "/usr/local/sbin/backup_mongo.pl" + dest: "/etc/cron.daily/mongodb_backup" + state: link + mode: "0700" + owner: root -- name: Create cron symlink for backup script - ansible.builtin.file: - src: "/usr/local/sbin/backup_mongo.pl" - dest: "/etc/cron.daily/mongodb_backup" - state: link - mode: "0700" - owner: root - when: mongo_replication_role != 'arbiter' +- name: Debug mongo_cluster_members + debug: + msg: "{{ item.host }}" + verbosity: 2 + loop: + "{{ mongo_cluster_members }}" + when: mongo_mode == "cluster" -# TODO: this template gets mongo_servers from -# the inventory, maybe change that to group vars -# this is not on an per app basis. These are mongoservers -# in the same cluster. - name: Create mongosh config file ansible.builtin.template: src: mongoshrc.js.j2 diff --git a/roles/mongo/tasks/services.yml b/roles/mongo/tasks/services.yml new file mode 100644 index 000000000..5caa4c54c --- /dev/null +++ b/roles/mongo/tasks/services.yml @@ -0,0 +1,6 @@ +--- +- name: Enable and start mongod + ansible.builtin.service: + name: mongod.service + enabled: true + state: started \ No newline at end of file diff --git a/roles/mongo/tasks/standaloneconfig.yml b/roles/mongo/tasks/standaloneconfig.yml new file mode 100644 index 000000000..a8ec00126 --- /dev/null +++ b/roles/mongo/tasks/standaloneconfig.yml @@ -0,0 +1 @@ +# todo mag weg? \ No newline at end of file diff --git a/roles/mongo/tasks/users.yml b/roles/mongo/tasks/users.yml index a218bac46..5afa45151 100644 --- a/roles/mongo/tasks/users.yml +++ b/roles/mongo/tasks/users.yml @@ -1,13 +1,32 @@ -- name: Create mongo database users # requires pymongo 4+ +- name: Create mongo database users cluster # requires pymongo 4+ + when: + - mongo_mode == "cluster" + - mongo_replication_role == "primary" community.mongodb.mongodb_user: login_database: admin database: "{{ item.db_name }}" login_user: admin - login_password: "{{ mongo_admin_pass }}" + login_password: "{{ mongo_admin_password }}" name: "{{ item.name }}" password: "{{ item.password }}" - roles: readWrite - replica_set: "{{ replica_set_name }}" + roles: "{{ item.role | default('readWrite')}}" + replica_set: "{{ mongo_replica_set_name }}" + no_log: true + run_once: true + with_items: "{{ mongo.users }}" + changed_when: false + tags: mongo_users + +- name: Create mongo database users single server # requires pymongo 4+ + when: mongo_mode != "cluster" + community.mongodb.mongodb_user: + login_database: admin + database: "{{ item.db_name }}" + login_user: admin + login_password: "{{ mongo_admin_password }}" + name: "{{ item.name }}" + password: "{{ item.password }}" + roles: "{{ item.role | default('readWrite') }}" no_log: true run_once: true with_items: "{{ mongo.users }}" diff --git a/roles/mongo/templates/mongod.conf.j2 b/roles/mongo/templates/mongod.conf.j2 index f5e990add..93fe38ba1 100644 --- a/roles/mongo/templates/mongod.conf.j2 +++ b/roles/mongo/templates/mongod.conf.j2 @@ -1,8 +1,5 @@ systemLog: - destination: file - logRotate: reopen - logAppend: true - path: /var/log/mongodb/mongod.log + destination: syslog net: bindIp: {{ mongo_bind_listen_address }} @@ -14,10 +11,12 @@ net: allowConnectionsWithoutCertificates: true storage: - dbPath: /var/lib/mongo + dbPath: {{ mongo_data_path }} +{% if mongo_replica_set_name is defined and mongo_mode == "cluster" %} replication: - replSetName: {{ replica_set_name }} + replSetName: {{ mongo_replica_set_name }} +{% endif %} security: authorization: enabled diff --git a/roles/mongo/templates/mongoshrc.js.j2 b/roles/mongo/templates/mongoshrc.js.j2 index 9faf2cdb0..c8b4c92f4 100644 --- a/roles/mongo/templates/mongoshrc.js.j2 +++ b/roles/mongo/templates/mongoshrc.js.j2 @@ -1,2 +1,5 @@ -db = connect("mongodb://admin:{{ mongo_admin_password }}@{% for mongo_server in mongo_servers %}{{ mongo_server }}:{{ mongod_port }}{% if not loop.last %},{% endif %}{% endfor %}?ssl=true&tlsCAFile=/etc/pki/mongo/mongoca.pem") - +{% if mongo_mode == "cluster" %} +db = connect("mongodb://admin:{{ mongo_admin_password }}@{% for mongo_server in mongo_cluster_members %}{{ mongo_server.host }}:{{ mongo_server.port }}{% if not loop.last %},{% endif %}{% endfor %}?ssl=true&tlsCAFile=/etc/pki/mongo/mongoca.pem") +{% else %} +db = connect("mongodb://admin:{{ mongo_admin_password }}@{{ ansible_facts['fqdn'] }}:{{ mongo_port }}?ssl=true&tlsCAFile=/etc/pki/mongo/mongoca.pem") +{% endif %} diff --git a/roles/myconext/tasks/main.yml b/roles/myconext/tasks/main.yml index 326668702..097ad191b 100644 --- a/roles/myconext/tasks/main.yml +++ b/roles/myconext/tasks/main.yml @@ -116,13 +116,6 @@ group: "root" mode: "0755" -- name: Add the mongodb docker network to the list of networks when MongoDB runs in Docker - ansible.builtin.set_fact: - myconext_docker_networks: - - name: loadbalancer - - name: openconext_mongodb - when: mongodb_in_docker | default(false) | bool - - name: Create and start the server container community.docker.docker_container: name: myconextserver diff --git a/roles/oidcng/tasks/main.yml b/roles/oidcng/tasks/main.yml index a306fa7f1..981cee255 100644 --- a/roles/oidcng/tasks/main.yml +++ b/roles/oidcng/tasks/main.yml @@ -88,13 +88,6 @@ group: "root" mode: "0755" -- name: Add the mongodb docker network to the list of networks when MongoDB runs in Docker - ansible.builtin.set_fact: - oidcng_docker_networks: - - name: loadbalancer - - name: openconext_mongodb - when: mongodb_in_docker | default(false) | bool - - name: Create and start the server container community.docker.docker_container: name: oidcngserver diff --git a/roles/oidcng/templates/logback.xml.j2 b/roles/oidcng/templates/logback.xml.j2 index 7b38d5627..8ca540620 100644 --- a/roles/oidcng/templates/logback.xml.j2 +++ b/roles/oidcng/templates/logback.xml.j2 @@ -2,54 +2,54 @@ - - - %d{ISO8601} %5p [%t] %logger{40}:%L - %m%n - - + + + %d{ISO8601} %5p [%t] %logger{40}:%L - %m%n + + - - host.docker.internal:514 + + host.docker.internal:514 - {"app":"oidcng"} - true - - [ignore] - [ignore] - [ignore] - - + {"app":"oidcng"} + true + + [ignore] + [ignore] + [ignore] + + - - {{ smtp_server }} - {{ noreply_email }} - {{ error_mail_to }} - {{ error_subject_prefix }}Unexpected error oidcng - + + {{ smtp_server }} + {{ noreply_email }} + {{ error_mail_to }} + {{ error_subject_prefix }}Unexpected error oidcng + - com.nimbusds.oauth2.sdk.ParseException - org.springframework.security.authentication.BadCredentialsException - oidc.exceptions.UnauthorizedException - oidc.exceptions.RedirectMismatchException - org.springframework.dao.EmptyResultDataAccessException - java.lang.IllegalArgumentException - ERROR - - + com.nimbusds.oauth2.sdk.ParseException + org.springframework.security.authentication.BadCredentialsException + oidc.exceptions.UnauthorizedException + oidc.exceptions.RedirectMismatchException + org.springframework.dao.EmptyResultDataAccessException + java.lang.IllegalArgumentException + ERROR + + - - - - -{%if oidcng_logback_email |bool %} - -{%endif%} - -{%if oidcng_logback_json |bool %} - -{%endif%} - + + + + + {% if oidcng_logback_email |bool %} + + {% endif %} + + {% if oidcng_logback_json |bool %} + + {% endif %} + diff --git a/roles/pdp/handlers/main.yml b/roles/pdp/handlers/main.yml index c6179bb34..9ce5432e7 100644 --- a/roles/pdp/handlers/main.yml +++ b/roles/pdp/handlers/main.yml @@ -1,9 +1,9 @@ - name: restart pdpserver community.docker.docker_container: - name: pdpserver - state: started + name: "pdp" + state: "started" restart: true # avoid restarting it creates unexpected data loss according to docker_container_module notes comparisons: - '*': ignore - when: pdpservercontainer is success and pdpservercontainer is not change + '*': "ignore" + when: "pdpservercontainer is success and pdpservercontainer is not change" diff --git a/roles/pdp/templates/serverapplication.yml.j2 b/roles/pdp/templates/serverapplication.yml.j2 index 762c2dbdc..a6d33a914 100644 --- a/roles/pdp/templates/serverapplication.yml.j2 +++ b/roles/pdp/templates/serverapplication.yml.j2 @@ -41,9 +41,9 @@ email: voot: serviceUrl: https://voot.{{ base_domain }} sab: - password: {{ aa.sab_rest_password }} - userName: {{ aa.sab_rest_username }} - endpoint: {{ aa.sab_rest_endpoint }} + password: {{ pdp.sab_rest_password }} + userName: {{ pdp.sab_rest_username }} + endpoint: {{ pdp.sab_rest_endpoint }} policies: cachePolicies: {{ pdp.cache_policies }} manage: diff --git a/roles/remove-java-app/defaults/main.yml b/roles/remove-java-app/defaults/main.yml deleted file mode 100644 index c2ec013e2..000000000 --- a/roles/remove-java-app/defaults/main.yml +++ /dev/null @@ -1 +0,0 @@ -java_apps_to_remove: [] diff --git a/roles/remove-java-app/handlers/main.yml b/roles/remove-java-app/handlers/main.yml deleted file mode 100644 index 17ddb0097..000000000 --- a/roles/remove-java-app/handlers/main.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -- name: daemon_reload - systemd: - daemon_reload: yes - -- name: restart httpd - systemd: - name: httpd - state: restarted diff --git a/roles/remove-java-app/tasks/main.yml b/roles/remove-java-app/tasks/main.yml deleted file mode 100644 index b80baffbd..000000000 --- a/roles/remove-java-app/tasks/main.yml +++ /dev/null @@ -1,57 +0,0 @@ ---- -- name: Remove the httpd configuration - file: - path: /etc/httpd/conf.d/{{ item | replace('-','_') }}.conf - state: absent - notify: - - restart httpd - with_items: - - "{{ java_apps_to_remove }}" - - -- name: Disable and stop the app - service: - name: "{{ item }}" - enabled: no - state: stopped - with_items: - - "{{ java_apps_to_remove }}" - -- name: Remove the app dir and its contents - file: - path: /opt/{{ item }}/ - state: absent - with_items: - - "{{ java_apps_to_remove }}" - -- name: Remove the www dir - file: - path: /var/www/{{ item }}/ - state: absent - with_items: - - "{{ java_apps_to_remove }}" - -- name: Remove the logs - file: - path: /var/log/{{ item }}/ - state: absent - with_items: - - "{{ java_apps_to_remove }}" - -- name: Force a daemon reload before removing the systemd file - systemd: - daemon_reload: yes - -- name: Remove the systemd service file - file: - path: "/etc/systemd/system/{{ item }}.service" - state: absent - notify: - - daemon_reload - with_items: - - "{{ java_apps_to_remove }}" - register: systemd_removed - -- name: Reset failed services - command: "systemctl reset-failed" - diff --git a/roles/rsyslog/tasks/main.yml b/roles/rsyslog/tasks/main.yml index a531fd677..0eef86d46 100644 --- a/roles/rsyslog/tasks/main.yml +++ b/roles/rsyslog/tasks/main.yml @@ -4,8 +4,16 @@ - rsyslog - rsyslog-gnutls - rsyslog-relp + state: present + notify: + - "restart rsyslog" + +- name: Install rsyslog and python modules + ansible.builtin.package: + name: - python3-dateutil state: present + when: "'sysloghost' in group_names and ansible_distribution_major_version > '7'" notify: - "restart rsyslog" diff --git a/roles/rsyslog/tasks/process_auth_log_for_environment.yml b/roles/rsyslog/tasks/process_auth_log_for_environment.yml new file mode 100644 index 000000000..de97e1af5 --- /dev/null +++ b/roles/rsyslog/tasks/process_auth_log_for_environment.yml @@ -0,0 +1,78 @@ +--- + +- name: Create log_logins table for each log_login environment + community.mysql.mysql_db: + name: "{{ rsyslog_environment.db_loglogins_name }}" + login_user: "{{ rsyslog_environment.db_loglogins_user }}" + login_password: "{{ rsyslog_environment.db_loglogins_password }}" + login_host: "{{ rsyslog_environment.db_loglogins_host }}" + state: import + target: /var/tmp/log_logins.sql + changed_when: false + +- name: Create lastseen table for each log_login environment + community.mysql.mysql_db: + name: "{{ rsyslog_environment.db_lastseen_name }}" + login_user: "{{ rsyslog_environment.db_lastseen_user }}" + login_password: "{{ rsyslog_environment.db_lastseen_password }}" + login_host: "{{ rsyslog_environment.db_lastseen_host }}" + state: import + target: /var/tmp/lastseen.sql + changed_when: false + +- name: Create a python script that parses eb log_logins per environment + ansible.builtin.template: + src: parse_ebauth_to_mysql.py.j2 + dest: /usr/local/sbin/parse_ebauth_to_mysql_{{ rsyslog_environment.name }}.py + mode: 0740 + owner: root + group: root + +- name: Create a python script that parses stepup log_logins per environment + ansible.builtin.template: + src: parse_stepupauth_to_mysql.py.j2 + dest: /usr/local/sbin/parse_stepupauth_to_mysql_{{ rsyslog_environment.name }}.py + mode: 0740 + owner: root + group: root + +- name: Put log_logins logrotate scripts for eb + ansible.builtin.template: + src: logrotate_ebauth.j2 + dest: /etc/logrotate.d/logrotate_ebauth_{{ rsyslog_environment.name }} + mode: 0644 + owner: root + group: root + +- name: Put log_logins logrotate scripts for stepup + ansible.builtin.template: + src: logrotate_stepupauth.j2 + dest: /etc/logrotate.d/logrotate_stepupauth_{{ rsyslog_environment.name }} + mode: 0644 + owner: root + group: root + +- name: Create logdirectory for log_logins cleanup script + ansible.builtin.file: + path: "{{ rsyslog_dir }}/apps/{{ rsyslog_environment.name }}/loglogins_cleanup/" + state: directory + owner: root + group: "{{ rsyslog_read_group }}" + mode: 0750 + +- name: Put log_logins cleanup script + ansible.builtin.template: + src: clean_loglogins.j2 + dest: /usr/local/sbin/clean_loglogins_{{ rsyslog_environment.name }} + owner: root + group: root + mode: 0700 + +- name: Create cronjobs to run the log_logins script + ansible.builtin.cron: + name: Delete old {{ rsyslog_environment.name }} log_login data + user: root + minute: "20" + hour: "02" + job: "/usr/local/sbin/clean_loglogins_{{ rsyslog_environment.name }}" + cron_file: loglogins_cleanup_{{ rsyslog_environment.name }} diff --git a/roles/rsyslog/tasks/process_auth_logs.yml b/roles/rsyslog/tasks/process_auth_logs.yml index 804bf629b..e1cb22365 100644 --- a/roles/rsyslog/tasks/process_auth_logs.yml +++ b/roles/rsyslog/tasks/process_auth_logs.yml @@ -9,103 +9,16 @@ - log_logins.sql - lastseen.sql -- name: Create log_logins table for each log_login environment - community.mysql.mysql_db: - name: "{{ item.db_loglogins_name }}" - login_user: "{{ item.db_loglogins_user }}" - login_password: "{{ item.db_loglogins_password }}" - login_host: "{{ item.db_loglogins_host }}" - state: import - target: /var/tmp/log_logins.sql - changed_when: false - with_items: "{{ rsyslog_environments }}" - when: item.db_loglogins_name is defined - -- name: Create lastseen table for each log_login environment - community.mysql.mysql_db: - name: "{{ item.db_lastseen_name }}" - login_user: "{{ item.db_lastseen_user }}" - login_password: "{{ item.db_lastseen_password }}" - login_host: "{{ item.db_lastseen_host }}" - state: import - target: /var/tmp/lastseen.sql - changed_when: false - with_items: "{{ rsyslog_environments }}" - when: item.db_loglogins_name is defined - - name: add python mysql module for parse_ebauth_to_mysql script apt: name: python3-mysqldb state: present when: ansible_os_family == "Debian" -- name: Create a python script that parses eb log_logins per environment - ansible.builtin.template: - src: parse_ebauth_to_mysql.py.j2 - dest: /usr/local/sbin/parse_ebauth_to_mysql_{{ item.name }}.py - mode: 0740 - owner: root - group: root - with_items: "{{ rsyslog_environments }}" - when: item.db_loglogins_name is defined - -- name: Create a python script that parses stepup log_logins per environment - ansible.builtin.template: - src: parse_stepupauth_to_mysql.py.j2 - dest: /usr/local/sbin/parse_stepupauth_to_mysql_{{ item.name }}.py - mode: 0740 - owner: root - group: root - with_items: "{{ rsyslog_environments }}" - when: item.db_loglogins_name is defined - -- name: Put log_logins logrotate scripts for eb - ansible.builtin.template: - src: logrotate_ebauth.j2 - dest: /etc/logrotate.d/logrotate_ebauth_{{ item.name }} - mode: 0644 - owner: root - group: root - with_items: "{{ rsyslog_environments }}" - when: item.db_loglogins_name is defined - -- name: Put log_logins logrotate scripts for stepup - ansible.builtin.template: - src: logrotate_stepupauth.j2 - dest: /etc/logrotate.d/logrotate_stepupauth_{{ item.name }} - mode: 0644 - owner: root - group: root - with_items: "{{ rsyslog_environments }}" - when: item.db_loglogins_name is defined - -- name: Create logdirectory for log_logins cleanup script - ansible.builtin.file: - path: "{{ rsyslog_dir }}/apps/{{ item.name }}/loglogins_cleanup/" - state: directory - owner: root - group: "{{ rsyslog_read_group }}" - mode: 0750 - with_items: "{{ rsyslog_environments }}" - when: item.db_loglogins_name is defined - -- name: Put log_logins cleanup script - ansible.builtin.template: - src: clean_loglogins.j2 - dest: /usr/local/sbin/clean_loglogins_{{ item.name }} - owner: root - group: root - mode: 0700 - with_items: "{{ rsyslog_environments }}" - when: item.db_loglogins_name is defined - -- name: Create cronjobs to run the log_logins script - ansible.builtin.cron: - name: Delete old {{ item.name }} log_login data - user: root - minute: "20" - hour: "02" - job: "/usr/local/sbin/clean_loglogins_{{ item.name }}" - cron_file: loglogins_cleanup_{{ item.name }} - with_items: "{{ rsyslog_environments }}" - when: item.db_loglogins_name is defined +- name: Process auth logs for each rsyslog environment + ansible.builtin.include_tasks: process_auth_log_for_environment.yml + loop: "{{ rsyslog_environments }}" + loop_control: + loop_var: rsyslog_environment + label: "{{ rsyslog_environment.name }}" + when: rsyslog_environment.db_loglogins_name is defined diff --git a/roles/rsyslog/tasks/rsyslog_central.yml b/roles/rsyslog/tasks/rsyslog_central.yml index 7dbdbac1a..1efa858f8 100644 --- a/roles/rsyslog/tasks/rsyslog_central.yml +++ b/roles/rsyslog/tasks/rsyslog_central.yml @@ -1,44 +1,44 @@ --- # The server uses a different set of keys as the client -- name: put rsyslog server key - copy: - content: "{{ rsyslogserverkey }}" +- name: Put rsyslog server key + ansible.builtin.copy: + content: "{{ rsyslogserverkey }}" dest: "/etc/pki/rsyslog/rsyslogserver.key" - mode: 0400 + mode: "0400" owner: root - name: Create the group that is allowed to read the logs - group: + ansible.builtin.group: name: "{{ rsyslog_read_group }}" state: present - name: Create directory to save the logs - file: + ansible.builtin.file: path: "{{ rsyslog_dir }}" owner: root group: "{{ rsyslog_read_group }}" mode: "0750" - recurse: true + # recurse: true # this makes everything very slow and not sure if it is necessary - name: Put rsyslog client certificate - copy: + ansible.builtin.copy: src: "{{ inventory_dir }}/files/certs/rsyslog/rsyslogserver.crt" dest: "/etc/pki/rsyslog/rsyslogserver.crt" - mode: 0644 + mode: "0644" owner: root group: adm -- name: place rsyslog CA file - copy: +- name: Place rsyslog CA file + ansible.builtin.copy: src: "{{ inventory_dir }}/files/certs/rsyslog_ca.pem" - dest: "{{ rsyslog_ca }}" - mode: 0644 + dest: "{{ rsyslog_ca }}" + mode: "0644" - name: Create directories to keep configuration file - file: + ansible.builtin.file: path: "/etc/rsyslog.d/{{ item }}" owner: root - mode: 0755 + mode: "0755" state: directory with_items: - listeners @@ -46,39 +46,46 @@ - templates - name: Create template configurations - template: + ansible.builtin.template: src: sc_template.conf.j2 dest: /etc/rsyslog.d/templates/{{ item.name }}.conf backup: true with_items: "{{ rsyslog_environments }}" + loop_control: + label: "{{ item.name }}" + notify: - "restart rsyslog" - name: Create ruleset configurations - template: + ansible.builtin.template: src: sc_ruleset.conf.j2 dest: /etc/rsyslog.d/rulesets/{{ item.name }}.conf backup: true with_items: "{{ rsyslog_environments }}" + loop_control: + label: "{{ item.name }}" notify: - "restart rsyslog" - name: Create sc listener configurations - template: + ansible.builtin.template: src: listener.conf.j2 dest: /etc/rsyslog.d/listeners/{{ item.name }}.conf backup: true with_items: "{{ rsyslog_environments }}" + loop_control: + label: "{{ item.name }}" notify: - "restart rsyslog" - name: Create logrotate file for apps and host logs - template: + ansible.builtin.template: src: centralsyslog.j2 dest: /etc/logrotate.d/centralsyslog -- name: Put ryslog config file - template: +- name: Put rsyslog config file + ansible.builtin.template: src: "rsyslog.conf.j2" dest: "/etc/rsyslog.conf" notify: diff --git a/roles/rsyslog/templates/clean_loglogins.j2 b/roles/rsyslog/templates/clean_loglogins.j2 index 0ef2ebe56..7296b01ff 100644 --- a/roles/rsyslog/templates/clean_loglogins.j2 +++ b/roles/rsyslog/templates/clean_loglogins.j2 @@ -1,26 +1,126 @@ #!/bin/bash -# Script to clean up the log_logins from mySQL -LOGFILE="{{ rsyslog_dir }}/apps/{{ item.name }}/loglogins_cleanup/loglogins_cleanup.log" -echo `date '+%h %d %H:%M:%S'` Starting cleanup of log_logins | tee -a $LOGFILE -LOGINSTAMP=$(date -d "-{{ loglogins_max_age }} months" +%Y-%m-%d) -OLDESTTIMESTAMP=$(mysql -u {{ item.db_loglogins_user }} -p{{ item.db_loglogins_password }} -h {{ item.db_loglogins_host }} {{ item.db_loglogins_name }} -se "select (DATE_FORMAT(loginstamp,'%Y-%m-%d')) from log_logins order by loginstamp asc limit 1") -if [ -z "$OLDESTTIMESTAMP" ] - then echo "No logins found in log_logins" | tee -a $LOGFILE - exit +# Script to remove logins that are older than LOG_LOGINS_MAX_AGE months from the log_logins table +# in the ${DB_LOG_LOGINS_NAME} database on the ${DB_LOG_LOGINS_HOST} host +# As a failsafe, the script will refuse to run if more than ${MAX_DELETE_DAYS} days worth of logins would be removed, +# unless the --force option is provided. +# The script is intended to be run daily from a cron job. + +MAX_DELETE_DAYS=5 +LOG_LOGINS_MAX_AGE="{{ loglogins_max_age }}" # Number of MONTHS of logins to keep +RSYSLOG_DIR="{{ rsyslog_dir }}" +RSYSLOG_ENVIRONMENT_NAME="{{ rsyslog_environment.name }}" +DB_LOG_LOGINS_NAME="{{ rsyslog_environment.db_loglogins_name }}" +DB_LOG_LOGINS_USER="{{ rsyslog_environment.db_loglogins_user }}" +DB_LOG_LOGINS_PASSWORD="{{ rsyslog_environment.db_loglogins_password }}" +DB_LOG_LOGINS_HOST="{{ rsyslog_environment.db_loglogins_host }}" +NOREPLY_EMAIL="{{ noreply_email }}" +ERROR_MAIL_TO="{{ error_mail_to }}" +ANSIBLE_HOSTNAME="{{ ansible_hostname }}" + +LOG_FILE="${RSYSLOG_DIR}/apps/${RSYSLOG_ENVIRONMENT_NAME}/loglogins_cleanup/loglogins_cleanup.log" +FORCE=0 # Whether to run if more than ${MAX_DELETE_DAYS} days of log data would be removed. + +SCRIPT_NAME=$0 + +for ARG in "$@"; do + case "$ARG" in + --force) + FORCE=1 + ;; + *) + echo "Usage: $0 [--force]" + exit 1 + ;; + esac +done + +log_message() { + echo "$(date '+%h %d %H:%M:%S')" "$@" | tee -a "$LOG_FILE" +} + +send_mail() { + local message="$*" + + log_message "Mailing error to ${ERROR_MAIL_TO}" + + echo "$message" | mail \ + -r "${NOREPLY_EMAIL}" \ + -s "log_login script on ${ANSIBLE_HOSTNAME} needs attention" \ + "${ERROR_MAIL_TO}" +} + + +log_message "Starting cleanup of old log_logins data" + +LOGIN_STAMP=$(date -d "-${LOG_LOGINS_MAX_AGE} months" +%Y-%m-%d) +log_message "Removing all logins older than ${LOG_LOGINS_MAX_AGE} months (before ${LOGIN_STAMP})" + +log_message "Using database: ${DB_LOG_LOGINS_USER}@${DB_LOG_LOGINS_HOST}:${DB_LOG_LOGINS_NAME}" + +MYSQL_DB=( + "mysql" + "-u" "${DB_LOG_LOGINS_USER}" + "-p${DB_LOG_LOGINS_PASSWORD}" + "-h" "${DB_LOG_LOGINS_HOST}" +) + +# Find the oldest timestamp in the log_logins table: +# SELECT (DATE_FORMAT(loginstamp,'%Y-%m-%d')) FROM log_logins ORDER BY loginstamp ASC LIMIT 1 +QUERY=( + "${MYSQL_DB[@]}" + # -s: silent; -e: query + "-se" "SELECT (DATE_FORMAT(loginstamp,'%Y-%m-%d')) FROM log_logins ORDER BY loginstamp ASC LIMIT 1" + "${DB_LOG_LOGINS_NAME}" +) +# DEBUG: Print query +# echo "${QUERY[@]}" +if ! OLDEST_TIMESTAMP=$( "${QUERY[@]}" ); then + log_message "Error running mysql query (OLDEST_TIMESTAMP)" + send_mail "Error running mysql query (OLDEST_TIMESTAMP)" + exit 1 +fi +if [ -z "$OLDEST_TIMESTAMP" ]; then + log_message "No logins found in log_logins. Nothing to cleanup." + send_mail "No logins found in log_logins. Nothing to cleanup." + # The log_logins table is empty. Not treating this as an error. Exit normally, + exit 0 +fi + +# If we are going to delete more than ${MAX_DELETE_DAYS} days, something might be wrong +# Warn and exit if this is the case, unless FORCE==1 +LOGIN_STAMP_UNIX=$(date -d "$LOGIN_STAMP" +%s) +OLDEST_TIMESTAMP_UNIX=$(date -d "$OLDEST_TIMESTAMP" +%s) +TIMESTAMP_DIFF=$(( (LOGIN_STAMP_UNIX-OLDEST_TIMESTAMP_UNIX)/3600/24 )) +log_message "The oldest timestamp in the database is $OLDEST_TIMESTAMP" +if [ "$TIMESTAMP_DIFF" -lt "0" ]; then + log_message "There is no data to delete. We're done." + exit 0 fi -# If we are going to delete more than 5 days, something is wrong -LOGINSTAMPUNIX=$(date -d $LOGINSTAMP +%s) -OLDESTTIMESTAMPUNIX=$(date -d $OLDESTTIMESTAMP +%s) -TIMESTAMPDIFF=$(( ($LOGINSTAMPUNIX-$OLDESTTIMESTAMPUNIX)/3600/24 )) -echo `date '+%h %d %H:%M:%S'` We will delete $TIMESTAMPDIFF days of log_logins data | tee -a $LOGFILE -if [ "$TIMESTAMPDIFF" -gt 5 ] - then - echo `date '+%h %d %H:%M:%S'` Something is up, you need to delete more than 5 days worth of logindata | tee -a $LOGFILE - - echo "The log_login cleanup script wants to delete more than 5 days of logins on the {{ ansible_hostname }}. Please investigate" | mail -r "{{ noreply_email }}" -s "log_login script on {{ ansible_hostname }} needs attention" "{{ error_mail_to }}" - exit -else - DELETEDROWS=$(mysql -u {{ item.db_loglogins_user }} -p{{ item.db_loglogins_password }} -h {{ item.db_loglogins_host }} -sNe "delete from log_logins where loginstamp < '$LOGINSTAMP'; select row_count();" {{ item.db_loglogins_name }}) - echo `date '+%h %d %H:%M:%S'` We have deleted $DELETEDROWS rows. | tee -a $LOGFILE +log_message "We will delete $TIMESTAMP_DIFF days of log_logins data" +if [ "$TIMESTAMP_DIFF" -gt "$MAX_DELETE_DAYS" ] && [ "$FORCE" -ne 1 ]; then + log_message "Something is up, you need to delete more than ${MAX_DELETE_DAYS} days worth of login data" + send_mail "The ${SCRIPT_NAME} script wants to delete more than ${MAX_DELETE_DAYS} days of logins on ${ANSIBLE_HOSTNAME}. Please investigate" + exit 0 +fi +if [ "$TIMESTAMP_DIFF" -gt "$MAX_DELETE_DAYS" ] && [ "$FORCE" -eq 1 ]; then + log_message "Force option provided; deleting more than ${MAX_DELETE_DAYS} days worth of login data" fi + +# Delete rows +# DELETE FROM log_logins WHERE loginstamp < '$LOGIN_STAMP'; SELECT row_count();" +QUERY=( + "${MYSQL_DB[@]}" + # -s: silent; -N: Don't write column names in results; -e: query; + "-sNe" "DELETE FROM log_logins WHERE loginstamp < '${LOGIN_STAMP}'; SELECT row_count();" + "${DB_LOG_LOGINS_NAME}" +) +# DEBUG: Print query +# echo "${QUERY[@]}" +if ! DELETED_ROWS=$( "${QUERY[@]}" ); then + log_message "Error running mysql query (DELETED_ROWS)" + send_mail "Error running mysql query (DELETED_ROWS)" + exit 1 +fi + +log_message "We have deleted $DELETED_ROWS rows. Done." diff --git a/roles/rsyslog/templates/logrotate_ebauth.j2 b/roles/rsyslog/templates/logrotate_ebauth.j2 index f05ab9bed..b8c460157 100644 --- a/roles/rsyslog/templates/logrotate_ebauth.j2 +++ b/roles/rsyslog/templates/logrotate_ebauth.j2 @@ -1,4 +1,4 @@ -{{ rsyslog_dir }}/log_logins/{{ item.name }}/eb-authentication.log +{{ rsyslog_dir }}/log_logins/{{ rsyslog_environment.name }}/eb-authentication.log { missingok daily @@ -10,7 +10,7 @@ delaycompress create 0640 root {{ rsyslog_read_group }} postrotate - /usr/local/sbin/parse_ebauth_to_mysql_{{ item.name }}.py > /dev/null + /usr/local/sbin/parse_ebauth_to_mysql_{{ rsyslog_environment.name }}.py > /dev/null systemctl kill -s HUP rsyslog.service endscript } diff --git a/roles/rsyslog/templates/logrotate_stepupauth.j2 b/roles/rsyslog/templates/logrotate_stepupauth.j2 index be1a50652..aa5bf4ead 100644 --- a/roles/rsyslog/templates/logrotate_stepupauth.j2 +++ b/roles/rsyslog/templates/logrotate_stepupauth.j2 @@ -1,4 +1,4 @@ -{{ rsyslog_dir }}/log_logins/{{ item.name }}/stepup-authentication.log +{{ rsyslog_dir }}/log_logins/{{ rsyslog_environment.name }}/stepup-authentication.log { missingok daily @@ -10,7 +10,7 @@ delaycompress create 0640 root {{ rsyslog_read_group }} postrotate - /usr/local/sbin/parse_stepupauth_to_mysql_{{ item.name }}.py > /dev/null + /usr/local/sbin/parse_stepupauth_to_mysql_{{ rsyslog_environment.name }}.py > /dev/null systemctl kill -s HUP rsyslog.service endscript } diff --git a/roles/rsyslog/templates/parse_ebauth_to_mysql.py.j2 b/roles/rsyslog/templates/parse_ebauth_to_mysql.py.j2 index 7e0bc7bcb..c71a1ba81 100644 --- a/roles/rsyslog/templates/parse_ebauth_to_mysql.py.j2 +++ b/roles/rsyslog/templates/parse_ebauth_to_mysql.py.j2 @@ -1,7 +1,12 @@ #!/usr/bin/python3 -# This script parses the files produced by engineblock and inserts them into a mySQL table where the SURFconext stats module will analyse the data further -# This script is intended to be used during logrotate -# It picks up all files starting with ebauth- (all rotated files) and parses them +# This script parses the authentication log files produced by engineblock and inserts them into two tables: +# - log_logins: contains login events from engineblock and stepup-gateway, and is there for use manually querying logins. +# There is no further processing done on this table. +# A daily cronjob runs clean_loglogins.j2 to remove old logins from the log_logins table. +# - last_login: contains the last login date for each user. This is used for deprovisioning (lifecycle) +# +# This script is intended to be used during logrotate with the delaycompress option set. +# It picks up all files starting with ebauth- that do not end in .gz and parses them import os import sys @@ -10,11 +15,11 @@ import json import MySQLdb from dateutil.parser import parse -mysql_host="{{ item.db_loglogins_host }}" -mysql_user="{{ item.db_loglogins_user }}" -mysql_password="{{ item.db_loglogins_password }}" -mysql_db="{{ item.db_loglogins_name }}" -workdir="{{ rsyslog_dir }}/log_logins/{{ item.name}}/" +mysql_host="{{ rsyslog_environment.db_loglogins_host }}" +mysql_user="{{ rsyslog_environment.db_loglogins_user }}" +mysql_password="{{ rsyslog_environment.db_loglogins_password }}" +mysql_db="{{ rsyslog_environment.db_loglogins_name }}" +workdir="{{ rsyslog_dir }}/log_logins/{{ rsyslog_environment.name}}/" db = MySQLdb.connect(mysql_host,mysql_user,mysql_password,mysql_db ) cursor = db.cursor() diff --git a/roles/rsyslog/templates/parse_stepupauth_to_mysql.py.j2 b/roles/rsyslog/templates/parse_stepupauth_to_mysql.py.j2 index 843fe44bc..8c98f29d2 100644 --- a/roles/rsyslog/templates/parse_stepupauth_to_mysql.py.j2 +++ b/roles/rsyslog/templates/parse_stepupauth_to_mysql.py.j2 @@ -1,8 +1,12 @@ #!/usr/bin/python3 -# This script parses rotated stepup-authentication.log files produced by engineblock. -# It filters for successful logins (authentication_result:OK) and inserts the data -# into the log_logins and last_login MySQL tables. -# This script is intended to be run separately during logrotate. +# This script parses the authentication log files produced by stepup-gateway and inserts them into two tables: +# - log_logins: contains login events from engineblock and stepup-gateway, and is there for use manually querying logins. +# There is no further processing done on this table. +# A daily cronjob runs clean_loglogins.j2 to remove old logins from the log_logins table. +# - last_login: contains the last login date for each user. This is used for deprovisioning (lifecycle) +# +# This script is intended to be used during logrotate with the delaycompress option set. +# It picks up all files starting with stepup-authentication.log- that do not end in .gz and parses them import os import sys @@ -11,11 +15,11 @@ import MySQLdb from dateutil.parser import parse # Configuration variables (to be injected by Ansible/Jinja2) -mysql_host="{{ item.db_loglogins_host }}" -mysql_user="{{ item.db_loglogins_user }}" -mysql_password="{{ item.db_loglogins_password }}" -mysql_db="{{ item.db_loglogins_name }}" -workdir="{{ rsyslog_dir }}/log_logins/{{ item.name}}/" +mysql_host="{{ rsyslog_environment.db_loglogins_host }}" +mysql_user="{{ rsyslog_environment.db_loglogins_user }}" +mysql_password="{{ rsyslog_environment.db_loglogins_password }}" +mysql_db="{{ rsyslog_environment.db_loglogins_name }}" +workdir="{{ rsyslog_dir }}/log_logins/{{ rsyslog_environment.name}}/" # Establish database connection try: diff --git a/roles/rsyslog/templates/sc_ruleset.conf.j2 b/roles/rsyslog/templates/sc_ruleset.conf.j2 index 86a0e5457..34d5392dd 100644 --- a/roles/rsyslog/templates/sc_ruleset.conf.j2 +++ b/roles/rsyslog/templates/sc_ruleset.conf.j2 @@ -19,8 +19,6 @@ if $programname == "engineblock" and $msg contains '{"channel":"authentication"' :programname, isequal, "pdp" { action(type="omfile" DynaFile="pdp-{{ item.name }}" {{ rsyslog_dir_file_modes }} ) stop } if $programname == "profile" and $msg startswith "{" then { action(type="omfile" DynaFile="profile-{{ item.name }}" {{ rsyslog_dir_file_modes }} ) stop } :programname, isequal, "profile" { action(type="omfile" DynaFile="apache-profile-{{ item.name }}" {{ rsyslog_dir_file_modes }} ) stop } -:programname, isequal, "teamsserver" { action(type="omfile" DynaFile="teams-{{ item.name }}" {{ rsyslog_dir_file_modes }} ) stop } -:programname, isequal, "teamsgui" { action(type="omfile" DynaFile="apache-teams-{{ item.name }}" {{ rsyslog_dir_file_modes }} ) stop } :programname, isequal, "vootserver" { action(type="omfile" DynaFile="voot-{{ item.name }}" {{ rsyslog_dir_file_modes }} ) stop } :programname, isequal, "mariadbd" { action(type="omfile" DynaFile="galera-{{ item.name }}" {{ rsyslog_dir_file_modes }} ) stop } :programname, isequal, "garb-systemd" { action(type="omfile" DynaFile="haproxy-{{ item.name }}" {{ rsyslog_dir_file_modes }} ) stop } diff --git a/roles/rsyslog/templates/sc_template.conf.j2 b/roles/rsyslog/templates/sc_template.conf.j2 index d6b765f0a..3a47df0a6 100644 --- a/roles/rsyslog/templates/sc_template.conf.j2 +++ b/roles/rsyslog/templates/sc_template.conf.j2 @@ -14,8 +14,6 @@ $template pdpanalytics-{{ item.name }}, "{{ rsyslog_dir }}/apps/{{ item.name }}/ $template apache-pdp-{{ item.name }}, "{{ rsyslog_dir }}/apps/{{ item.name }}/pdp/apache.log" $template profile-{{ item.name }}, "{{ rsyslog_dir }}/apps/{{ item.name }}/profile/profile.log" $template apache-profile-{{ item.name }}, "{{ rsyslog_dir }}/apps/{{ item.name }}/profile/apache.log" -$template teams-{{ item.name }}, "{{ rsyslog_dir }}/apps/{{ item.name }}/teams/teams.log" -$template apache-teams-{{ item.name }}, "{{ rsyslog_dir }}/apps/{{ item.name }}/teams/apache.log" $template voot-{{ item.name }}, "{{ rsyslog_dir }}/apps/{{ item.name }}/voot/voot.log" $template apache-voot-{{ item.name }}, "{{ rsyslog_dir }}/apps/{{ item.name }}/voot/apache.log" $template galera-{{ item.name }}, "{{ rsyslog_dir }}/apps/{{ item.name }}/galera/galera.log" diff --git a/roles/teams/defaults/main.yml b/roles/teams/defaults/main.yml deleted file mode 100644 index c0e6deeb3..000000000 --- a/roles/teams/defaults/main.yml +++ /dev/null @@ -1,33 +0,0 @@ ---- -teams_dir: /opt/teams -teams_cronjobmaster: true -teams_help_link_en: https://example.org -teams_help_link_nl: https://example.org -teams_help_link_pt: https://example.org -teams_tos_en: https://example.org -teams_tos_nl: https://example.org -teams_tos_pt: https://example.org -teams_main_link: https://www.openconext.org -teams_organization: "{{ instance_name }}" -teams_api_lifecycle_username: teams_api_lifecycle_user -teams_oauth2_token_url: "https://connect.{{ base_domain }}/oidc/token" -teams_authz_client_id: "teams.{{ base_domain }}" -teams_manage_provision_oidcrp_name_en: "Teams client credentials client for VOOT access" -teams_manage_provision_oidcrp_description_en: "OAuth client to access VOOT for group information" -teams_manage_provision_oidcrp_grants: "client_credentials" -teams_manage_provision_oidcrp_state: "prodaccepted" -teams_manage_provision_oidcrp_scopes: "groups" -teams_manage_provision_oidcrp_allowed_resource_servers: '{"name": "{{ voot.oidcng_checkToken_clientId }}"}' -teams_manage_provision_samlsp_client_id: "https://teams.{{ base_domain }}/shibboleth" -teams_manage_provision_samlsp_name_en: "{{ instance_name }} Teams" -teams_manage_provision_samlsp_description_en: "{{ instance_name }} Teams application for group memberships" -teams_manage_provision_samlsp_acs_location: "https://teams.{{ base_domain }}/Shibboleth.sso/SAML2/POST" -teams_manage_provision_samlsp_metadata_url: "https://teams.{{ base_domain }}/Shibboleth.sso/Metadata" -teams_manage_provision_samlsp_sp_cert: "" -teams_manage_provision_samlsp_trusted_proxy: false -teams_manage_provision_samlsp_sign: false -teams_spring_flyway_enabled: true -teams_docker_networks: - - name: "loadbalancer" -teams_server_restart_policy: always -teams_server_restart_retries: 0 diff --git a/roles/teams/handlers/main.yml b/roles/teams/handlers/main.yml deleted file mode 100644 index d866b5d27..000000000 --- a/roles/teams/handlers/main.yml +++ /dev/null @@ -1,9 +0,0 @@ -- name: restart teamsserver - community.docker.docker_container: - name: teamsserver - state: started - restart: true - # avoid restarting it creates unexpected data loss according to docker_container_module notes - comparisons: - '*': ignore - when: teamsserverontainer is success and teamsserverontainer is not change diff --git a/roles/teams/tasks/main.yml b/roles/teams/tasks/main.yml deleted file mode 100644 index 498c99d4c..000000000 --- a/roles/teams/tasks/main.yml +++ /dev/null @@ -1,101 +0,0 @@ ---- -- name: Create directory to keep configfile - ansible.builtin.file: - dest: "/opt/openconext/teams" - state: directory - owner: root - group: root - mode: "0770" - -- name: Place the serverapplication configfiles - ansible.builtin.template: - src: "{{ item }}.j2" - dest: /opt/openconext/teams/{{ item }} - owner: root - group: root - mode: "0644" - with_items: - - serverapplication.yml - - logback.xml - notify: restart teamsserver - -- name: Add the MariaDB docker network to the list of networks when MariaDB runs in Docker - ansible.builtin.set_fact: - teams_docker_networks: - - name: loadbalancer - - name: openconext_mariadb - when: mariadb_in_docker | default(false) | bool - -- name: Create and start the server container - community.docker.docker_container: - name: teamsserver - env: - TZ: "{{ timezone }}" - image: ghcr.io/openconext/openconext-teams-ng/teams-server:{{ teams_server_version }} - pull: true - restart_policy: "{{ teams_server_restart_policy }}" - restart_retries: "{{ teams_server_restart_retries }}" # Only for restart policy on-failure - state: started - networks: "{{ teams_docker_networks }}" - mounts: - - source: /opt/openconext/teams/serverapplication.yml - target: /application.yml - type: bind - - source: /opt/openconext/teams/logback.xml - target: /logback.xml - type: bind - command: "-Xmx512m --spring.config.location=./" - etc_hosts: - host.docker.internal: host-gateway - healthcheck: - test: - [ - "CMD", - "wget", - "-no-verbose", - "--tries=1", - "--spider", - "http://localhost:8080/internal/health", - ] - interval: 10s - timeout: 10s - retries: 3 - start_period: 10s - register: teamsserverontainer - -- name: Create the gui container - community.docker.docker_container: - name: teamsgui - image: ghcr.io/openconext/openconext-teams-ng/teams-gui:{{ teams_gui_version }} - pull: true - restart_policy: "always" - state: started - networks: - - name: "loadbalancer" - labels: - traefik.http.routers.teamsgui.rule: "Host(`teams.{{ base_domain }}`)" - traefik.http.routers.teamsgui.tls: "true" - traefik.enable: "true" - healthcheck: - test: ["CMD", "curl", "--fail", "http://localhost/internal/health"] - interval: 10s - timeout: 10s - retries: 3 - start_period: 10s - hostname: teams - mounts: - - source: /etc/localtime - target: /etc/localtime - type: bind - - source: /opt/openconext/common/favicon.ico - target: /var/www/favicon.ico - type: bind - env: - HTTPD_CSP: "{{ httpd_csp.strict_with_static_img }}" - HTTPD_SERVERNAME: "teams.{{ base_domain }}" - OPENCONEXT_INSTANCENAME: "{{ instance_name }}" - OPENCONEXT_ENGINE_LOGOUT_URL: "https://engine.{{ base_domain }}/logout" - OPENCONEXT_HELP_EMAIL: "{{ support_email }}" - SHIB_ENTITYID: "https://teams.{{ base_domain }}/shibboleth" - SHIB_REMOTE_ENTITYID: "https://engine.{{ base_domain }}/authentication/idp/metadata" - SHIB_REMOTE_METADATA: "{{ shibboleth_metadata_sources.engine }}" diff --git a/roles/teams/templates/logback.xml.j2 b/roles/teams/templates/logback.xml.j2 deleted file mode 100644 index b9c559d4f..000000000 --- a/roles/teams/templates/logback.xml.j2 +++ /dev/null @@ -1,29 +0,0 @@ -#jinja2:lstrip_blocks: True - - - - - - %d{ISO8601} %5p [%t] %logger{40}:%L - %m%n - - - - - {{ smtp_server }} - {{ noreply_email }} - {{ error_mail_to }} - {{ error_subject_prefix }}Unexpected error teams - - - - ERROR - - - - - - - - - - diff --git a/roles/teams/templates/serverapplication.yml.j2 b/roles/teams/templates/serverapplication.yml.j2 deleted file mode 100644 index f1a4088be..000000000 --- a/roles/teams/templates/serverapplication.yml.j2 +++ /dev/null @@ -1,131 +0,0 @@ -# The logging configuration. -logging: - config: file:///logback.xml - level: - org.hibernate.SQL: INFO - -api: - lifecycle: - username: {{ teams_api_lifecycle_username }} - password: "{{ teams_api_lifecycle_password }}" - -secure_cookie: true - -server: - port: 8080 - error: - path: "/error" - servlet: - session: - timeout: 28800 - cookie: - secure: true - server-header: no - -config: - support-email: {{ support_email }} - help-link-en: {{ teams_help_link_en }} - help-link-nl: {{ teams_help_link_nl }} - help-link-pt: {{ teams_help_link_pt }} - help-tos-en: {{ teams_tos_en }} - help-tos-nl: {{ teams_tos_nl }} - help-tos-pt: {{ teams_tos_pt }} - main-link: {{ teams_main_link }} - organization: {{ teams_organization }} - sponsor: {{ sponsor_name }} - supported_language_codes: {{ supported_language_codes }} - -features: - invite-migration-on: {{ teams.feature_invite_migration_on }} - -security: - user: - name: "{{ teams.voot_api_user }}" - password: "{{ external_group_provider_secrets.teams }}" - -sp_dashboard: - user-name: "{{ teams.spdashboard_api_user }}" - password: "{{ teams_api_spdashboard_password }}" - person-urn: "{{ teams.spdashboard_person_urn }}" - name: "SP Dashboard" - email: "{{ support_email }}" - -# Is this node in a load-balanced topology responsible for cleaning up resources (See ExpiredInvitationsRemover) -cron: - node-cron-job-responsible: {{ teams_cronjobmaster }} - expression: "0 0/15 * * * ?" - -teams: - default-stem-name: "{{ teams.default_stem_name }}" - group-name-context: "{{ teams.group_name_context }}" - product-name: "{{ teams.product_name }}" - non-guest-member-of: "{{ guest_qualifier }}" - -super_admins_team: - urns: - {% for value in teams.super_admins_team_urns %} -- "{{ value }}" - {% endfor %} - -voot: - serviceUrl: https://voot.{{ base_domain }} - accessTokenUri: "{{ teams_oauth2_token_url }}" - clientId: "{{ teams_authz_client_id }}" - clientSecret: "{{ teams_authz_client_secret }}" - scopes: "{{ teams_manage_provision_oidcrp_scopes }}" - -invite: - url: "https://invite.{{ base_domain }}/api/external/v1/teams" - user: "{{ invite.teamsuser }}" - password: "{{ invite.teamssecret }}" - -spring: - session: - store-type: jdbc - jdbc: - schema: classpath:org/springframework/session/jdbc/schema-mysql.sql - initialize-schema: always - cleanup-cron: "{% if teams_cronjobmaster %}0 13 * * * *{% else %}-{% endif %}" - jpa: - open-in-view: true - properties: - hibernate: - naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy - datasource: - driver-class-name: org.mariadb.jdbc.Driver - url: jdbc:mariadb://{{ teams.db_host }}/{{ teams.db_name }}?socketTimeout=30000 - username: {{ teams.db_user }} - password: "{{ teams.db_password }}" - mail: - host: {{ smtp_server }} - port: 25 - main: - banner-mode: "off" - flyway: - enabled: {{ teams_spring_flyway_enabled }} - validate-on-migrate: false - table: schema_version - security: - user: - name: na - password: na - -management: - health: - mail: - enabled: true - endpoints: - web: - exposure: - include: "health,info" - base-path: "/internal" - endpoint: - info: - enabled: true - info: - git: - mode: full - -email: - from: {{ instance_name }} Teams <{{ noreply_email }}> - base-url: https://teams.{{ base_domain }} diff --git a/roles/teams/vars/main.yml b/roles/teams/vars/main.yml deleted file mode 100644 index 207ea9b7c..000000000 --- a/roles/teams/vars/main.yml +++ /dev/null @@ -1,14 +0,0 @@ -manage_provision_oidcrp_client_id: "{{ teams_authz_client_id }}" -manage_provision_oidcrp_secret: "{{ teams_authz_client_secret }}" -manage_provision_oidcrp_name_en: "{{ teams_manage_provision_oidcrp_name_en }}" -manage_provision_oidcrp_description_en: "{{ teams_manage_provision_oidcrp_description_en }}" -manage_provision_oidcrp_grants: "{{ teams_manage_provision_oidcrp_grants }}" -manage_provision_oidcrp_allowed_resource_servers: "{{ teams_manage_provision_oidcrp_allowed_resource_servers }}" -manage_provision_samlsp_client_id: "{{ teams_manage_provision_samlsp_client_id }}" -manage_provision_samlsp_name_en: "{{ teams_manage_provision_samlsp_name_en }}" -manage_provision_samlsp_description_en: "{{ teams_manage_provision_samlsp_description_en }}" -manage_provision_samlsp_acs_location: "{{ teams_manage_provision_samlsp_acs_location }}" -manage_provision_samlsp_metadata_url: "{{ teams_manage_provision_samlsp_metadata_url }}" -manage_provision_samlsp_sp_cert: "{{ teams_manage_provision_samlsp_sp_cert }}" -manage_provision_samlsp_trusted_proxy: "{{ teams_manage_provision_samlsp_trusted_proxy }}" -manage_provision_samlsp_sign: "{{ teams_manage_provision_samlsp_sign }}" diff --git a/roles/welcome/files/site/images/teams-logo.png b/roles/welcome/files/site/images/teams-logo.png deleted file mode 100644 index 30f06955321435c84f587e0ea49b4e9437ef8ab3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6761 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 diff --git a/roles/welcome/templates/site/index.html b/roles/welcome/templates/site/index.html index 1be3199c1..0601527c7 100644 --- a/roles/welcome/templates/site/index.html +++ b/roles/welcome/templates/site/index.html @@ -30,24 +30,6 @@

- {% if ( not minimal_install ) %} -
  • -

    - - Teams - -

    - -

    - Manage team members -

    -
    - - - -
    -
  • - {% endif %}

    OpenConext Administration