Merge pull request 'neoloc/mediaproxy' (#92) from neoloc/mediaproxy into develop

Reviewed-on: https://git.query.consul/unkinben/puppet-prod/pulls/92
This commit is contained in:
Ben Vincent 2024-07-06 23:24:49 +10:00
commit b5c7b310ee
18 changed files with 599 additions and 18 deletions

View File

@ -129,6 +129,10 @@ lookup_options:
profiles::ceph::client::keyrings: profiles::ceph::client::keyrings:
merge: merge:
strategy: deep strategy: deep
profiles::nginx::simpleproxy::locations:
merge:
strategy: deep
facts_path: '/opt/puppetlabs/facter/facts.d' facts_path: '/opt/puppetlabs/facter/facts.d'

View File

@ -1,4 +1,7 @@
--- ---
hiera_include:
- profiles::nginx::simpleproxy
profiles::yum::global::repos: profiles::yum::global::repos:
ceph-reef: ceph-reef:
name: ceph-reef name: ceph-reef
@ -18,3 +21,79 @@ profiles::base::groups::local:
gid: 20000 gid: 20000
allowdupe: false allowdupe: false
forcelocal: true forcelocal: true
ldap_host: 'ldap.service.consul'
ldap_basedn: 'dc=main,dc=unkin,dc=net'
profiles::nginx::simpleproxy::locations:
# authentication proxy
authproxy:
ensure: 'present'
server: "%{lookup('profiles::nginx::simpleproxy::nginx_vhost')}"
ssl_only: true
internal: true
location: '= /auth-proxy'
proxy: "http://%{lookup('profiles::nginx::simpleproxy::proxy_host')}:8888"
proxy_set_header:
- 'Content-Length ""'
- "X-Ldap-URL ldap://%{lookup('ldap_host')}"
- 'X-Ldap-Starttls "false"'
- "X-Ldap-BaseDN %{lookup('ldap_basedn')}"
- "X-Ldap-BindDN %{lookup('ldap_binddn')}"
- "X-Ldap-BindPass %{lookup('ldap_bindpass')}"
- 'X-CookieName "nginxauth"'
- 'Cookie nginxauth=$cookie_nginxauth'
- "X-Ldap-Template %{lookup('ldap_template')}"
- 'X-Ldap-Realm "Restricted"'
proxy_cache: 'cache'
proxy_cache_valid: '200 10m'
proxy_cache_key: '"$http_authorization$cookie_nginxauth"'
location_cfg_append:
proxy_pass_request_body: 'off'
# health checks by consul
arrstack_web_consul:
ensure: 'present'
server: "%{lookup('profiles::nginx::simpleproxy::nginx_vhost')}"
ssl_only: true
location: '/consul/health'
proxy: "http://%{lookup('profiles::nginx::simpleproxy::proxy_host')}:%{lookup('profiles::nginx::simpleproxy::proxy_port')}"
proxy_set_header:
- 'Host $host'
- 'X-Forwarded-For $proxy_add_x_forwarded_for'
- 'X-Forwarded-Host $host'
- 'X-Forwarded-Proto $scheme'
- 'Upgrade $http_upgrade'
- 'Connection $http_connection'
proxy_redirect: 'off'
proxy_http_version: '1.1'
location_allow:
- 127.0.0.1
- "%{facts.networking.ip}"
location_deny:
- all
# authorised access from external
arrstack_web_external:
ensure: 'present'
server: "%{lookup('profiles::nginx::simpleproxy::nginx_vhost')}"
ssl_only: true
location: '/'
auth_request: '/auth-proxy'
proxy: "http://%{lookup('profiles::nginx::simpleproxy::proxy_host')}:%{lookup('profiles::nginx::simpleproxy::proxy_port')}"
proxy_set_header:
- 'Host $host'
- 'X-Forwarded-For $proxy_add_x_forwarded_for'
- 'X-Forwarded-Host $host'
- 'X-Forwarded-Proto $scheme'
- 'Upgrade $http_upgrade'
- 'Connection $http_connection'
proxy_redirect: 'off'
proxy_http_version: '1.1'
# location for api, which should be accessible without authentication
arrstack_api:
ensure: 'present'
server: "%{lookup('profiles::nginx::simpleproxy::nginx_vhost')}"
ssl_only: true
location: '~ /api'
proxy: "http://%{lookup('profiles::nginx::simpleproxy::proxy_host')}:%{lookup('profiles::nginx::simpleproxy::proxy_port')}"
location_cfg_append:
client_max_body_size: '20m'

View File

@ -1,7 +1,6 @@
--- ---
hiera_include: hiera_include:
- jellyfin - jellyfin
- profiles::nginx::simpleproxy
# manage jellyfin # manage jellyfin
jellyfin::params::service_enable: true jellyfin::params::service_enable: true

View File

@ -1,2 +1,3 @@
--- ---
lidarr::api_key: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAeIT5i5yJ/KCmEBEgF8r36dl2RK/0/LQWPl6bgth7KOdtfNynhH4bCxembrJwzXasT1KBrPWYmTc2IObBz2tqu7BIHoioI2y+GVs2ulhx63lrfeDI/I4QFs5EOh9fIoyOxlIkvKm+p0WVfaegKOKM63XHHvG2TmBwTypEHB1IXaCMVl87tY+3xmMEaiqVPik3llqLCog1rmRLbIQx+whAFPtlhHur0ozfdYLKiM57YHAsQpGgASYkAAjvZuKabOrRZsIhhsHCb4JQ/evvIrhkviK7nP4xHdeqRSJgdEDmIldr2FW3uHCzuq033K3T7HNc3HbUM/5lC0ygP8sZnnM8rDBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBAyfQkaBPJJWVsc2FGiyCyMgDAYuYDAwBBAJzfVZ4RFrQyi48VZeS8MTjf2HNAXBYoYgTtdZAk9i+pIV22p9ee+KsU=] lidarr::api_key: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAeIT5i5yJ/KCmEBEgF8r36dl2RK/0/LQWPl6bgth7KOdtfNynhH4bCxembrJwzXasT1KBrPWYmTc2IObBz2tqu7BIHoioI2y+GVs2ulhx63lrfeDI/I4QFs5EOh9fIoyOxlIkvKm+p0WVfaegKOKM63XHHvG2TmBwTypEHB1IXaCMVl87tY+3xmMEaiqVPik3llqLCog1rmRLbIQx+whAFPtlhHur0ozfdYLKiM57YHAsQpGgASYkAAjvZuKabOrRZsIhhsHCb4JQ/evvIrhkviK7nP4xHdeqRSJgdEDmIldr2FW3uHCzuq033K3T7HNc3HbUM/5lC0ygP8sZnnM8rDBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBAyfQkaBPJJWVsc2FGiyCyMgDAYuYDAwBBAJzfVZ4RFrQyi48VZeS8MTjf2HNAXBYoYgTtdZAk9i+pIV22p9ee+KsU=]
ldap_bindpass: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAEDEyk6fBBnrjZvfK8MnUVOTWxhFGtgY34/2CuIq55MoVLsk2ZgVrL7Kt+94bqFhwEB67kuNpMGXqTgW5ose2yWs5iVSJLECsf9C+tvGBGwaV35LNwP5S3aQmFagyTpZZz9QlGKC7818jlXz7vZWDtiUhy5TGMHeyS0fdjCveavtZR28A+ZrvWjJeLdN47mmvYwYfFnQBs3kSgkl5KyMVhFWSFOSLeHsuEzCVXHoQ1jQG+2TV5m18wV0RR/sOju2E+vsulqlDgCyifgoiry4GzJeKNrNDI2bifzHCAi6yZqHL/klyqbGTnKLlA4xKoXsHF+xEwcoq4S9JDLAdWeH1SDBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCdvh4yn8knozcYhinybRq3gDAwTKv8VakQG7XK/mcEplwtoiKqLnj9IIGdIUh1zPi2Sg48ET5rfZyl0p7ddIYoHjU=]

View File

@ -1,7 +1,7 @@
--- ---
hiera_include: hiera_include:
- lidarr - lidarr
- profiles::nginx::simpleproxy - profiles::nginx::ldapauth
# manage lidarr # manage lidarr
lidarr::params::user: lidarr lidarr::params::user: lidarr
@ -27,9 +27,13 @@ profiles::nginx::simpleproxy::nginx_aliases:
profiles::nginx::simpleproxy::proxy_port: 8000 profiles::nginx::simpleproxy::proxy_port: 8000
profiles::nginx::simpleproxy::proxy_host: 127.0.0.1 profiles::nginx::simpleproxy::proxy_host: 127.0.0.1
profiles::nginx::simpleproxy::proxy_path: '/' profiles::nginx::simpleproxy::proxy_path: '/'
profiles::nginx::simpleproxy::use_default_location: false
nginx::client_max_body_size: 20M
ldap_binddn: 'cn=svc_lidarr,ou=services,ou=users,dc=main,dc=unkin,dc=net'
ldap_template: '(memberOf=ou=lidarr_access,ou=groups,dc=main,dc=unkin,dc=net)'
# configure consul service # configure consul service
nginx::client_max_body_size: 10M
consul::services: consul::services:
lidarr: lidarr:
service_name: 'lidarr' service_name: 'lidarr'
@ -41,7 +45,7 @@ consul::services:
checks: checks:
- id: 'lidarr_http_check' - id: 'lidarr_http_check'
name: 'Lidarr HTTP Check' name: 'Lidarr HTTP Check'
http: "https://%{facts.networking.fqdn}:443" http: "https://%{facts.networking.fqdn}:443/consul/health"
method: 'GET' method: 'GET'
tls_skip_verify: true tls_skip_verify: true
interval: '10s' interval: '10s'

View File

@ -1,2 +1,3 @@
--- ---
prowlarr::api_key: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAdAzvi5Z2cX7KWdMlMfR5N+Jz9Pmh3k9yvPgM1JnTM8ZODs5VyQf/d3goWJ5Fn+jcjVqQ+aBga2CHfbdjgg5dGC19Jr8CmxVkYpMVb+e6Md4LEglUD6g70LK8JHB1FAM0fqW82/zqBL73KFKcu71Hpbf9YylJD4LXCr/k4D7hPX3tgEOzFn1iGl/DqxJFWnorj0btk3/2AmA3AMjvFy4r39PwbMfr2jNFSmAdJa7j7W+ESyE08Cc795VORIa/lbrT0ZfBMGXqzNTIpcdJ7uabcrH0qHNM8FPh4eHBzGMqLvIba487bs2TUb8eIivwT2EAwmGDWX1QkG2o6lGyO8PyqzBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBBO8BQpHvHYOA2tjyxpjGw4gDATwt1wP0aPFPnbRoqPdwClfOzbWmtbT/rCBmCQH0HkyA8sqr2I2qlOsuJukCjBDHo=] prowlarr::api_key: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAdAzvi5Z2cX7KWdMlMfR5N+Jz9Pmh3k9yvPgM1JnTM8ZODs5VyQf/d3goWJ5Fn+jcjVqQ+aBga2CHfbdjgg5dGC19Jr8CmxVkYpMVb+e6Md4LEglUD6g70LK8JHB1FAM0fqW82/zqBL73KFKcu71Hpbf9YylJD4LXCr/k4D7hPX3tgEOzFn1iGl/DqxJFWnorj0btk3/2AmA3AMjvFy4r39PwbMfr2jNFSmAdJa7j7W+ESyE08Cc795VORIa/lbrT0ZfBMGXqzNTIpcdJ7uabcrH0qHNM8FPh4eHBzGMqLvIba487bs2TUb8eIivwT2EAwmGDWX1QkG2o6lGyO8PyqzBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBBO8BQpHvHYOA2tjyxpjGw4gDATwt1wP0aPFPnbRoqPdwClfOzbWmtbT/rCBmCQH0HkyA8sqr2I2qlOsuJukCjBDHo=]
ldap_bindpass: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAhduPAqoZuq/xeRs4f/KX4r88evPMogQX79yofLAB5Qqdr48s2X0BAa1iiw0vMdL6Tf0uc794WJN5MP2Yp365Vk1yhwgqH92rt5hKPI+wBN5uak2iLgLzLWsp0HOx7d1ukDWBbj0lI6G5LiofsL3KJbbTnkovn06L4PRJXgn44+ynfywiCl2tPy2294DhfooeM6/Cy+t9lA6blzHLCOHtt/rBKmk1GT2y3YBCPhRfOumWXQWnv4Q+f6KkQkvpfPyAFYNiQxQYBv5bGwLnwiDk3xQnPM4FfcutVuAOKjsoeMa+K1KShDFyEfBxIER8JSpigj2/khstyihcVW0Xrod3uDBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCRqqRwMThwn1F/6byFhTWxgDAfucfkFhmqxBv/u5H+wWnjvK5EH7eU/fECrajYPBW/cmsYjLgXlwrAzFGqWze3AZc=]

View File

@ -1,7 +1,7 @@
--- ---
hiera_include: hiera_include:
- prowlarr - prowlarr
- profiles::nginx::simpleproxy - profiles::nginx::ldapauth
# manage prowlarr # manage prowlarr
prowlarr::params::user: prowlarr prowlarr::params::user: prowlarr
@ -27,9 +27,13 @@ profiles::nginx::simpleproxy::nginx_aliases:
profiles::nginx::simpleproxy::proxy_port: 8000 profiles::nginx::simpleproxy::proxy_port: 8000
profiles::nginx::simpleproxy::proxy_host: 127.0.0.1 profiles::nginx::simpleproxy::proxy_host: 127.0.0.1
profiles::nginx::simpleproxy::proxy_path: '/' profiles::nginx::simpleproxy::proxy_path: '/'
profiles::nginx::simpleproxy::use_default_location: false
nginx::client_max_body_size: 20M
ldap_binddn: 'cn=svc_prowlarr,ou=services,ou=users,dc=main,dc=unkin,dc=net'
ldap_template: '(memberOf=ou=prowlarr_access,ou=groups,dc=main,dc=unkin,dc=net)'
# configure consul service # configure consul service
nginx::client_max_body_size: 10M
consul::services: consul::services:
prowlarr: prowlarr:
service_name: 'prowlarr' service_name: 'prowlarr'
@ -41,7 +45,7 @@ consul::services:
checks: checks:
- id: 'prowlarr_http_check' - id: 'prowlarr_http_check'
name: 'Prowlarr HTTP Check' name: 'Prowlarr HTTP Check'
http: "https://%{facts.networking.fqdn}:443" http: "https://%{facts.networking.fqdn}:443/consul/health"
method: 'GET' method: 'GET'
tls_skip_verify: true tls_skip_verify: true
interval: '10s' interval: '10s'

View File

@ -1,2 +1,3 @@
--- ---
radarr::api_key: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEALtNnNr2N7DpP9zx5anmQavFmsTLIyPkpJGCkJpUTHMYFSScS/3FOUuufajk4Cmu4FbPswp/N/U1nHO8oLF6xNQ+H77+xXuKPalW/3R1IRqGoczwsAfstJ6nYF+PLjjeK2TDP+KMs3Eg2+nrXB7NOVOP88RvDLyZq93Wn9qR+1VG6Y2gLqGSJArZpNilV5ygUYRgbMeckjqfLynYBXtgDQQLYNhxDO6WGRRv+0X773nmOdrWFAUjqF6/K+Ejjk5ZbaqnGyjljMstSrhg7NWxtMRbCjeMpjUjUS4Hn/Vayg2M2Ag2s87gsE1e4QFa6KP7GVRu3swvyZ3D54Ba/xrebxzBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBDD6gIEfNGPXA8zv/vysgxJgDADMi7Fx5q+aqTMeqcKLg1AukTlCnJ62zykm6RNGdS0KlpJsvTSmWF4So3v/9BsKdk=] radarr::api_key: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEALtNnNr2N7DpP9zx5anmQavFmsTLIyPkpJGCkJpUTHMYFSScS/3FOUuufajk4Cmu4FbPswp/N/U1nHO8oLF6xNQ+H77+xXuKPalW/3R1IRqGoczwsAfstJ6nYF+PLjjeK2TDP+KMs3Eg2+nrXB7NOVOP88RvDLyZq93Wn9qR+1VG6Y2gLqGSJArZpNilV5ygUYRgbMeckjqfLynYBXtgDQQLYNhxDO6WGRRv+0X773nmOdrWFAUjqF6/K+Ejjk5ZbaqnGyjljMstSrhg7NWxtMRbCjeMpjUjUS4Hn/Vayg2M2Ag2s87gsE1e4QFa6KP7GVRu3swvyZ3D54Ba/xrebxzBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBDD6gIEfNGPXA8zv/vysgxJgDADMi7Fx5q+aqTMeqcKLg1AukTlCnJ62zykm6RNGdS0KlpJsvTSmWF4So3v/9BsKdk=]
ldap_bindpass: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAf6LbAXy3EsJqkf4KO8xikpXfSVIVOD2obnwZLPEiCfbC0iJyZ2Uc/RbHM9NnIpg8dgEnYGgaBM9HC+kFKYFP4skLsPQ0mpqOv13WbOAHOphhyBxWc/n4Eg4HqMAiHsC4qA9Sj5j4F29tNiMmpNM0eqDXD6YIrajV5IF+SKQQzZrNSrcDrzjqENaMnJWVc2gj7Zb9kPB1LdM5ZfljpPtfQHXHhh9ShTDKuWMx46nc+UYaRcJqHEns7OExwpsEAtA1SSunD81PTijJ6Bn1Y6cAsZoBot5YgFGaWtgQ4jvEqj7EbG8gDJuHMSwa9RzA9SMsVwEh2g1pko7wYbBR/arV7jBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBAhZlVaEdaLNh60gAY0KwEKgDAHPoXfFRq0I2K2J+NFgNFm/A00jYGVRfuPsX6VZfPaJ499dtMiJifrK3Kfk1oXpvk=]

View File

@ -1,7 +1,7 @@
--- ---
hiera_include: hiera_include:
- radarr - radarr
- profiles::nginx::simpleproxy - profiles::nginx::ldapauth
# manage radarr # manage radarr
radarr::params::user: radarr radarr::params::user: radarr
@ -28,9 +28,13 @@ profiles::nginx::simpleproxy::nginx_aliases:
profiles::nginx::simpleproxy::proxy_port: 8000 profiles::nginx::simpleproxy::proxy_port: 8000
profiles::nginx::simpleproxy::proxy_host: 127.0.0.1 profiles::nginx::simpleproxy::proxy_host: 127.0.0.1
profiles::nginx::simpleproxy::proxy_path: '/' profiles::nginx::simpleproxy::proxy_path: '/'
profiles::nginx::simpleproxy::use_default_location: false
nginx::client_max_body_size: 20M
ldap_binddn: 'cn=svc_radarr,ou=services,ou=users,dc=main,dc=unkin,dc=net'
ldap_template: '(memberOf=ou=radarr_access,ou=groups,dc=main,dc=unkin,dc=net)'
# configure consul service # configure consul service
nginx::client_max_body_size: 10M
consul::services: consul::services:
radarr: radarr:
service_name: 'radarr' service_name: 'radarr'
@ -42,7 +46,7 @@ consul::services:
checks: checks:
- id: 'radarr_http_check' - id: 'radarr_http_check'
name: 'radarr HTTP Check' name: 'radarr HTTP Check'
http: "https://%{facts.networking.fqdn}:443" http: "https://%{facts.networking.fqdn}:443/consul/health"
method: 'GET' method: 'GET'
tls_skip_verify: true tls_skip_verify: true
interval: '10s' interval: '10s'

View File

@ -1,2 +1,3 @@
--- ---
readarr::api_key: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAlJ5RLp6pVTGQgtbzO5cQSrHBMg80S1ImFprHDeWC3GPN2KbheM80b1FKxvN+oVUJ8/kfiV6zstLOoYPUJQfmJNa/Xe95W/5+9hH2IS/oQ0yVdfLOjRq//qp+mVvSJ7JrtOyYSIrU3HjxaD+eXTPYp4UEJKfdSmGyDr7XuCOVIZe0Lu7OHczs8VKrowN99RJZ589HoMqrqCZWPlx14l/uNFjYdK/w6VcUWoo9y/5z1jtsNIObV8kSAYQQLwSr3tmjJdEE3au4sjeMOOJDpGcd5aJRWpKp12+8oHdVR5BV5326aCb13tkp6Td0jq/W9J2Jyv05vUdpP3PnVH9mHPDh6TBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBDA+2mMNGwfYM+mRoVTQiZMgDBanhVFmpYe42vZgMBKpNcNRjTnoCl27RpxD3KnjYwkE1zw/NeEOLoSZ1Try3GrlaA=] readarr::api_key: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAlJ5RLp6pVTGQgtbzO5cQSrHBMg80S1ImFprHDeWC3GPN2KbheM80b1FKxvN+oVUJ8/kfiV6zstLOoYPUJQfmJNa/Xe95W/5+9hH2IS/oQ0yVdfLOjRq//qp+mVvSJ7JrtOyYSIrU3HjxaD+eXTPYp4UEJKfdSmGyDr7XuCOVIZe0Lu7OHczs8VKrowN99RJZ589HoMqrqCZWPlx14l/uNFjYdK/w6VcUWoo9y/5z1jtsNIObV8kSAYQQLwSr3tmjJdEE3au4sjeMOOJDpGcd5aJRWpKp12+8oHdVR5BV5326aCb13tkp6Td0jq/W9J2Jyv05vUdpP3PnVH9mHPDh6TBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBDA+2mMNGwfYM+mRoVTQiZMgDBanhVFmpYe42vZgMBKpNcNRjTnoCl27RpxD3KnjYwkE1zw/NeEOLoSZ1Try3GrlaA=]
ldap_bindpass: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAcOegaqGEsivQAlhSYvaiVUij4QJ/kterSg+wX/7P/oWjSN1oSNAFfco6lg5fYUsR7HDKZ4IwYuO1Q/q8hOxYkqzYrH/MIAhZatfQxyxriKuekxqOMuXgwrWCzAexQL0Fb4s5gcHQ4fwy5OxsM1CxFXnSSm1eYNXl5IERd//c0dFoIcshiGlOCFsj8Ne9mookFTJQDZrxM4VMXaVb+Fl9mOyy1ppDBKHTP/1ise/6LIUi+9YngamAWLIrsur5KvR45kvRoxDNLfJZasAqhD/5QLceDdSxqKBDur57QzmoPo2lFdT4WlzphKOdyHZtYOYr7BPdbtCqkXTWdkXvqFlRxjBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBCXhYe6zoRW7/OxVrZnAEoTgDDPFTz5S4nZWiwzjdT7Yd88Ii6I/v6ckaKTx0gd0pZKsVZkFYQBBhIfqFS2ho0UG3Y=]

View File

@ -1,7 +1,7 @@
--- ---
hiera_include: hiera_include:
- readarr - readarr
- profiles::nginx::simpleproxy - profiles::nginx::ldapauth
# manage readarr # manage readarr
readarr::params::user: readarr readarr::params::user: readarr
@ -27,9 +27,13 @@ profiles::nginx::simpleproxy::nginx_aliases:
profiles::nginx::simpleproxy::proxy_port: 8000 profiles::nginx::simpleproxy::proxy_port: 8000
profiles::nginx::simpleproxy::proxy_host: 127.0.0.1 profiles::nginx::simpleproxy::proxy_host: 127.0.0.1
profiles::nginx::simpleproxy::proxy_path: '/' profiles::nginx::simpleproxy::proxy_path: '/'
profiles::nginx::simpleproxy::use_default_location: false
nginx::client_max_body_size: 20M
ldap_binddn: 'cn=svc_readarr,ou=services,ou=users,dc=main,dc=unkin,dc=net'
ldap_template: '(memberOf=ou=readarr_access,ou=groups,dc=main,dc=unkin,dc=net)'
# configure consul service # configure consul service
nginx::client_max_body_size: 10M
consul::services: consul::services:
readarr: readarr:
service_name: 'readarr' service_name: 'readarr'
@ -41,7 +45,7 @@ consul::services:
checks: checks:
- id: 'readarr_http_check' - id: 'readarr_http_check'
name: 'Readarr HTTP Check' name: 'Readarr HTTP Check'
http: "https://%{facts.networking.fqdn}:443" http: "https://%{facts.networking.fqdn}:443/consul/health"
method: 'GET' method: 'GET'
tls_skip_verify: true tls_skip_verify: true
interval: '10s' interval: '10s'

View File

@ -1 +1,2 @@
sonarr::api_key: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEANhwc0Vk0htkacwqGA/ZEVR7hpC1V2+nyP3lxyqkVNWERdcIsoMjlfwp2QNoLVirALY10Kon1wKXiRLT+QOqF9aTapS2Vb2YH3ZujR5yT6T1z4e0o4EA3IlNJZemVIziIqrK8+8zZzVafYdOYwMwpbc5EzoJPBtdqNHbDaQu4bRTCp2yMISTOzFjZpOoEJlRyC8YrffNU2xzA5mh5Cw+00MdIfPd8enrCnA4b1ddqP/IsfEYRt91ANQBULwKC5wenKdJAN5qKSKW6KU9TUM5YvGvUPgTvcYgXNf3/INL6G3/HwISTE5A7S6lqwYLyRTT1fMHtgDIqS936DPqCdAZtzjBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBBZ4e3jAM0zvV43IrCzQOGIgDBwOWgm+wJG+jAU8r1awWDvzQXgF3h65nAKfoOuGEztsjeBEruU4EmZy6llLbfSSb8=] sonarr::api_key: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEANhwc0Vk0htkacwqGA/ZEVR7hpC1V2+nyP3lxyqkVNWERdcIsoMjlfwp2QNoLVirALY10Kon1wKXiRLT+QOqF9aTapS2Vb2YH3ZujR5yT6T1z4e0o4EA3IlNJZemVIziIqrK8+8zZzVafYdOYwMwpbc5EzoJPBtdqNHbDaQu4bRTCp2yMISTOzFjZpOoEJlRyC8YrffNU2xzA5mh5Cw+00MdIfPd8enrCnA4b1ddqP/IsfEYRt91ANQBULwKC5wenKdJAN5qKSKW6KU9TUM5YvGvUPgTvcYgXNf3/INL6G3/HwISTE5A7S6lqwYLyRTT1fMHtgDIqS936DPqCdAZtzjBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBBZ4e3jAM0zvV43IrCzQOGIgDBwOWgm+wJG+jAU8r1awWDvzQXgF3h65nAKfoOuGEztsjeBEruU4EmZy6llLbfSSb8=]
ldap_bindpass: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAF5/sP43SJX/FB6cAS0GPqxC78JS7jSNKoUqWB/IXkv8uXYiClqk+Xw4nFx8EtknNn628DHBY3vCLQ59Xk89p0fyimP70m3BM6or5iRGdCqEAOzL399GbYX8WFHjyQRBmGdaLR5h2r5UnmjuPpDtV+fgsqxo4gNpXnuQ+46ZZPQce/dzHzux+4aEK4Q6UbD/ZZSQklD6zEUV+Agj6E9cQlJPBiHtTyUdXOYHYlJN1HhFPXu3C6KIz63YioVCah1n6T/rJtPQ07pVUfizIaJiPpzcUN91+ZWyyjUPo0bRGZtYKVs/uCAYXht4F5ttKQDaGa3wd4a5IdtacmEWQWFqk3TBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBDtB/68xkjxK3nrNh0MilACgDDt85aBvSp/Oj9EY67eNPr+JcnQ7WfyuqAYAnmYqfbQI8MDtYAx7br0+inJXvQ1BvI=]

View File

@ -1,7 +1,7 @@
--- ---
hiera_include: hiera_include:
- sonarr - sonarr
- profiles::nginx::simpleproxy - profiles::nginx::ldapauth
# manage sonarr # manage sonarr
sonarr::params::user: sonarr sonarr::params::user: sonarr
@ -27,9 +27,13 @@ profiles::nginx::simpleproxy::nginx_aliases:
profiles::nginx::simpleproxy::proxy_port: 8000 profiles::nginx::simpleproxy::proxy_port: 8000
profiles::nginx::simpleproxy::proxy_host: 127.0.0.1 profiles::nginx::simpleproxy::proxy_host: 127.0.0.1
profiles::nginx::simpleproxy::proxy_path: '/' profiles::nginx::simpleproxy::proxy_path: '/'
profiles::nginx::simpleproxy::use_default_location: false
nginx::client_max_body_size: 20M
ldap_binddn: 'cn=svc_sonarr,ou=services,ou=users,dc=main,dc=unkin,dc=net'
ldap_template: '(memberOf=ou=sonarr_access,ou=groups,dc=main,dc=unkin,dc=net)'
# configure consul service # configure consul service
nginx::client_max_body_size: 10M
consul::services: consul::services:
sonarr: sonarr:
service_name: 'sonarr' service_name: 'sonarr'
@ -41,7 +45,7 @@ consul::services:
checks: checks:
- id: 'sonarr_http_check' - id: 'sonarr_http_check'
name: 'Sonarr HTTP Check' name: 'Sonarr HTTP Check'
http: "https://%{facts.networking.fqdn}:443" http: "https://%{facts.networking.fqdn}:443/consul/health"
method: 'GET' method: 'GET'
tls_skip_verify: true tls_skip_verify: true
interval: '10s' interval: '10s'

View File

@ -0,0 +1,72 @@
class profiles::nginx::ldapauth (
Stdlib::AbsolutePath $bin_path = '/usr/local/bin/nginx-ldap-auth',
Stdlib::AbsolutePath $env_path = '/etc/default/nginx-ldap-auth',
String $user = 'nginx-ldap-auth',
String $group = 'nginx-ldap-auth',
Boolean $systempkgs = false,
String $version = 'system',
Hash $packages = {
'python3.11-ldap' => { ensure => 'present' }
}
){
if $::facts['python3_version'] {
$python_version = $version ? {
'system' => $::facts['python3_version'],
default => $version,
}
ensure_resources('package', $packages)
# Deploy the default configuration file using a template
file { $env_path:
ensure => file,
content => template('profiles/ldapauth/nginx-ldap-auth.default.erb'),
}
# Deploy the daemon script using a template
file { $bin_path:
ensure => file,
content => template('profiles/ldapauth/nginx-ldap-auth-daemon.py.erb'),
mode => '0755',
}
# Manage user and group
group { $group:
ensure => present,
system => true,
}
user { $user:
ensure => present,
comment => 'nginx-ldap-auth helper',
gid => $group,
shell => '/sbin/nologin',
system => true,
require => Group[$group],
}
# Create log directory for nginx-ldap-auth
file { '/var/log/nginx-ldap-auth':
ensure => directory,
owner => $user,
group => $group,
mode => '0755',
require => User[$user],
}
# Ensure the systemd service is enabled and started
systemd::unit_file { 'nginx-ldap-auth.service':
content => template('profiles/ldapauth/nginx-ldap-auth.service.erb'),
enable => true,
active => true,
require => [
File[$bin_path],
File[$env_path],
User[$user],
],
}
}
}

View File

@ -12,6 +12,8 @@ class profiles::nginx::simpleproxy (
Stdlib::Port $proxy_port = 80, Stdlib::Port $proxy_port = 80,
Stdlib::Host $proxy_host = $facts['networking']['ip'], Stdlib::Host $proxy_host = $facts['networking']['ip'],
String $proxy_path = '/', String $proxy_path = '/',
Boolean $use_default_location = true,
Hash $locations = {},
) { ) {
# if nginx_version isnt set, install nginx # if nginx_version isnt set, install nginx
@ -83,7 +85,7 @@ class profiles::nginx::simpleproxy (
$defaults = { $defaults = {
'listen_port' => $listen_port, 'listen_port' => $listen_port,
'server_name' => $server_names, 'server_name' => $server_names,
'use_default_location' => true, 'use_default_location' => $use_default_location,
'access_log' => "/var/log/nginx/${nginx_vhost}_access.log", 'access_log' => "/var/log/nginx/${nginx_vhost}_access.log",
'error_log' => "/var/log/nginx/${nginx_vhost}_error.log", 'error_log' => "/var/log/nginx/${nginx_vhost}_error.log",
'autoindex' => 'on', 'autoindex' => 'on',
@ -98,11 +100,25 @@ class profiles::nginx::simpleproxy (
$nginx_parameters = merge($defaults, $extras_hash) $nginx_parameters = merge($defaults, $extras_hash)
# manage the nginx class # manage the nginx class
include 'nginx' class { 'nginx':
proxy_cache_path => {
'/var/cache/nginx/cache' => 'cache:128m',
},
proxy_cache_levels => '1:2',
proxy_cache_keys_zone => 'cache:128m',
proxy_cache_max_size => '1024m',
proxy_cache_inactive => '10m',
proxy_temp_path => '/var/cache/nginx/cache_temp',
}
# create the nginx vhost with the merged parameters # create the nginx vhost with the merged parameters
create_resources('nginx::resource::server', { $nginx_vhost => $nginx_parameters }) create_resources('nginx::resource::server', { $nginx_vhost => $nginx_parameters })
# create nginx locations
if $use_default_location == false {
create_resources('nginx::resource::location', $locations)
}
# manage selinux # manage selinux
if $::facts['os']['selinux']['config_mode'] == 'enforcing' { if $::facts['os']['selinux']['config_mode'] == 'enforcing' {

View File

@ -0,0 +1,351 @@
#!/bin/sh
''''[ -z $LOG ] && export LOG=/dev/stdout # '''
''''which python3.11 >/dev/null && exec python3.11 -u "$0" "$@" >> $LOG 2>&1 # '''
# Copyright (C) 2014-2022 Nginx, Inc.
import sys
import os
import signal
import base64
import ldap
from ldap.filter import escape_filter_chars
import argparse
if sys.version_info.major == 2:
from Cookie import BaseCookie
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
elif sys.version_info.major == 3:
from http.cookies import BaseCookie
from http.server import HTTPServer, BaseHTTPRequestHandler
if not hasattr(__builtins__, "basestring"): basestring = (str, bytes)
#Listen = ('localhost', 8888)
#Listen = "/tmp/auth.sock" # Also uncomment lines in 'Requests are
# processed with UNIX sockets' section below
# -----------------------------------------------------------------------------
# Different request processing models: select one
# -----------------------------------------------------------------------------
# Requests are processed in separate thread
import threading
if sys.version_info.major == 2:
from SocketServer import ThreadingMixIn
elif sys.version_info.major == 3:
from socketserver import ThreadingMixIn
class AuthHTTPServer(ThreadingMixIn, HTTPServer):
pass
# -----------------------------------------------------------------------------
# Requests are processed in separate process
#from SocketServer import ForkingMixIn
#class AuthHTTPServer(ForkingMixIn, HTTPServer):
# pass
# -----------------------------------------------------------------------------
# Requests are processed with UNIX sockets
#import threading
#from SocketServer import ThreadingUnixStreamServer
#class AuthHTTPServer(ThreadingUnixStreamServer, HTTPServer):
# pass
# -----------------------------------------------------------------------------
class AuthHandler(BaseHTTPRequestHandler):
# Return True if request is processed and response sent, otherwise False
# Set ctx['user'] and ctx['pass'] for authentication
def do_GET(self):
ctx = self.ctx
ctx['action'] = 'input parameters check'
for k, v in self.get_params().items():
ctx[k] = self.headers.get(v[0], v[1])
if ctx[k] == None:
self.auth_failed(ctx, 'required "%s" header was not passed' % k)
return True
ctx['action'] = 'performing authorization'
auth_header = self.headers.get('Authorization')
auth_cookie = self.get_cookie(ctx['cookiename'])
if auth_cookie != None and auth_cookie != '':
auth_header = "Basic " + auth_cookie
self.log_message("using username/password from cookie %s" %
ctx['cookiename'])
else:
self.log_message("using username/password from authorization header")
if auth_header is None or not auth_header.lower().startswith('basic '):
self.send_response(401)
self.send_header('WWW-Authenticate', 'Basic realm="' + ctx['realm'] + '"')
self.send_header('Cache-Control', 'no-cache')
self.end_headers()
return True
ctx['action'] = 'decoding credentials'
try:
auth_decoded = base64.b64decode(auth_header[6:])
if sys.version_info.major == 3: auth_decoded = auth_decoded.decode("utf-8")
user, passwd = auth_decoded.split(':', 1)
except:
self.auth_failed(ctx)
return True
ctx['pass'] = passwd
ctx['user'] = ldap.filter.escape_filter_chars(user)
# Continue request processing
return False
def get_cookie(self, name):
cookies = self.headers.get('Cookie')
if cookies:
authcookie = BaseCookie(cookies).get(name)
if authcookie:
return authcookie.value
else:
return None
else:
return None
# Log the error and complete the request with appropriate status
def auth_failed(self, ctx, errmsg = None):
msg = 'Error while ' + ctx['action']
if errmsg:
msg += ': ' + errmsg
ex, value, trace = sys.exc_info()
if ex != None:
msg += ": " + str(value)
if ctx.get('url'):
msg += ', server="%s"' % ctx['url']
if ctx.get('user'):
msg += ', login="%s"' % ctx['user']
self.log_error(msg)
self.send_response(401)
self.send_header('WWW-Authenticate', 'Basic realm="' + ctx['realm'] + '"')
self.send_header('Cache-Control', 'no-cache')
self.end_headers()
def get_params(self):
return {}
def log_message(self, format, *args):
if len(self.client_address) > 0:
addr = BaseHTTPRequestHandler.address_string(self)
else:
addr = "-"
if not hasattr(self, 'ctx'):
user = '-'
else:
user = self.ctx['user']
sys.stdout.write("%s - %s [%s] %s\n" % (addr, user,
self.log_date_time_string(), format % args))
def log_error(self, format, *args):
self.log_message(format, *args)
# Verify username/password against LDAP server
class LDAPAuthHandler(AuthHandler):
# Parameters to put into self.ctx from the HTTP header of auth request
params = {
# parameter header default
'realm': ('X-Ldap-Realm', 'Restricted'),
'url': ('X-Ldap-URL', None),
'starttls': ('X-Ldap-Starttls', 'false'),
'disable_referrals': ('X-Ldap-DisableReferrals', 'false'),
'basedn': ('X-Ldap-BaseDN', None),
'template': ('X-Ldap-Template', '(cn=%(username)s)'),
'binddn': ('X-Ldap-BindDN', ''),
'bindpasswd': ('X-Ldap-BindPass', ''),
'cookiename': ('X-CookieName', '')
}
@classmethod
def set_params(cls, params):
cls.params = params
def get_params(self):
return self.params
# GET handler for the authentication request
def do_GET(self):
ctx = dict()
self.ctx = ctx
ctx['action'] = 'initializing basic auth handler'
ctx['user'] = '-'
if AuthHandler.do_GET(self):
# request already processed
return
ctx['action'] = 'empty password check'
if not ctx['pass']:
self.auth_failed(ctx, 'attempt to use empty password')
return
try:
# check that uri and baseDn are set
# either from cli or a request
if not ctx['url']:
self.log_message('LDAP URL is not set!')
return
if not ctx['basedn']:
self.log_message('LDAP baseDN is not set!')
return
ctx['action'] = 'initializing LDAP connection'
ldap_obj = ldap.initialize(ctx['url']);
# Python-ldap module documentation advises to always
# explicitely set the LDAP version to use after running
# initialize() and recommends using LDAPv3. (LDAPv2 is
# deprecated since 2003 as per RFC3494)
#
# Also, the STARTTLS extension requires the
# use of LDAPv3 (RFC2830).
ldap_obj.protocol_version=ldap.VERSION3
# Establish a STARTTLS connection if required by the
# headers.
if ctx['starttls'] == 'true':
ldap_obj.start_tls_s()
# See https://www.python-ldap.org/en/latest/faq.html
if ctx['disable_referrals'] == 'true':
ldap_obj.set_option(ldap.OPT_REFERRALS, 0)
ctx['action'] = 'binding as search user'
ldap_obj.bind_s(ctx['binddn'], ctx['bindpasswd'], ldap.AUTH_SIMPLE)
ctx['action'] = 'preparing search filter'
searchfilter = ctx['template'] % {'username': ctx['user']}
self.log_message(('searching on server "%s" with base dn ' + \
'"%s" with filter "%s"') %
(ctx['url'], ctx['basedn'], searchfilter))
ctx['action'] = 'running search query'
results = ldap_obj.search_s(ctx['basedn'], ldap.SCOPE_SUBTREE,
searchfilter, ['objectclass'], 1)
ctx['action'] = 'verifying search query results'
nres = len(results)
if nres < 1:
self.auth_failed(ctx, 'no objects found')
return
if nres > 1:
self.log_message("note: filter match multiple objects: %d, using first" % nres)
user_entry = results[0]
ldap_dn = user_entry[0]
if ldap_dn == None:
self.auth_failed(ctx, 'matched object has no dn')
return
self.log_message('attempting to bind using dn "%s"' % (ldap_dn))
ctx['action'] = 'binding as an existing user "%s"' % ldap_dn
ldap_obj.bind_s(ldap_dn, ctx['pass'], ldap.AUTH_SIMPLE)
self.log_message('Auth OK for user "%s"' % (ctx['user']))
# Successfully authenticated user
self.send_response(200)
self.end_headers()
except:
self.auth_failed(ctx)
def exit_handler(signal, frame):
global Listen
if isinstance(Listen, basestring):
try:
os.unlink(Listen)
except:
ex, value, trace = sys.exc_info()
sys.stderr.write('Failed to remove socket "%s": %s\n' %
(Listen, str(value)))
sys.stderr.flush()
sys.exit(0)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="""Simple Nginx LDAP authentication helper.""")
# Group for listen options:
group = parser.add_argument_group("Listen options")
group.add_argument('--host', metavar="hostname",
default="localhost", help="host to bind (Default: localhost)")
group.add_argument('-p', '--port', metavar="port", type=int,
default=8888, help="port to bind (Default: 8888)")
# ldap options:
group = parser.add_argument_group(title="LDAP options")
group.add_argument('-u', '--url', metavar="URL",
default="ldap://localhost:389",
help=("LDAP URI to query (Default: ldap://localhost:389)"))
group.add_argument('-s', '--starttls', metavar="starttls",
default="false",
help=("Establish a STARTTLS protected session (Default: false)"))
group.add_argument('--disable-referrals', metavar="disable_referrals",
default="false",
help=("Sets ldap.OPT_REFERRALS to zero (Default: false)"))
group.add_argument('-b', metavar="baseDn", dest="basedn", default='',
help="LDAP base dn (Default: unset)")
group.add_argument('-D', metavar="bindDn", dest="binddn", default='',
help="LDAP bind DN (Default: anonymous)")
group.add_argument('-w', metavar="passwd", dest="bindpw", default='',
help="LDAP password for the bind DN (Default: unset)")
group.add_argument('-f', '--filter', metavar='filter',
default='(cn=%(username)s)',
help="LDAP filter (Default: cn=%%(username)s)")
# http options:
group = parser.add_argument_group(title="HTTP options")
group.add_argument('-R', '--realm', metavar='"Restricted Area"',
default="Restricted", help='HTTP auth realm (Default: "Restricted")')
group.add_argument('-c', '--cookie', metavar="cookiename",
default="", help="HTTP cookie name to set in (Default: unset)")
args = parser.parse_args()
global Listen
Listen = (args.host, args.port)
auth_params = {
'realm': ('X-Ldap-Realm', args.realm),
'url': ('X-Ldap-URL', args.url),
'starttls': ('X-Ldap-Starttls', args.starttls),
'disable_referrals': ('X-Ldap-DisableReferrals', args.disable_referrals),
'basedn': ('X-Ldap-BaseDN', args.basedn),
'template': ('X-Ldap-Template', args.filter),
'binddn': ('X-Ldap-BindDN', args.binddn),
'bindpasswd': ('X-Ldap-BindPass', args.bindpw),
'cookiename': ('X-CookieName', args.cookie)
}
LDAPAuthHandler.set_params(auth_params)
server = AuthHTTPServer(Listen, LDAPAuthHandler)
signal.signal(signal.SIGINT, exit_handler)
signal.signal(signal.SIGTERM, exit_handler)
sys.stdout.write("Start listening on %s:%d...\n" % Listen)
sys.stdout.flush()
server.serve_forever()

View File

@ -0,0 +1,18 @@
#
# these are used with systemd too
# so please keep options names inside variables
#
#URL="--url ldap://example.com:389"
#BASE="-b dc=nodomain"
#BIND_DN="-D cn=admin,dc=nodomain"
#BIND_PASS="-w secret"
#COOKIE="-c nginxauth"
#FILTER="-f (cn=%(username)s)"
#REALM="-R 'Restricted Area'"
# these are used with init scripts only
LOG=/var/log/nginx-ldap-auth/daemon.log
RUNDIR=/var/run/nginx-ldap-auth/
PIDFILE=/var/run/nginx-ldap-auth/nginx-ldap-auth.pid
USER=<%= @user %>
GROUP=<%= @group %>

View File

@ -0,0 +1,17 @@
[Unit]
Description=LDAP authentication helper for Nginx
After=network.target network-online.target
[Service]
Type=simple
User=<%= @user %>
Group=<%= @group %>
WorkingDirectory=/var/run
EnvironmentFile=<%= @env_path %>
ExecStart=<%= @bin_path %> $URL $BASE $BIND_DN $BIND_PASS $COOKIE $FILTER $REALM
KillMode=process
KillSignal=SIGINT
Restart=on-failure
[Install]
WantedBy=multi-user.target