{"version":"https://jsonfeed.org/version/1.1","title":"Jaryl Chng's Knowledge Base","home_page_url":"https://kb.jarylchng.com","feed_url":"https://kb.jarylchng.com/json/","description":"<p>Welcome to the index page of my knowledge base, if you haven't done so, do visit my website at <a href=\"https://jarylchng.com\" rel=\"noopener noreferrer\" target=\"_blank\">https://jarylchng.com</a>.</p><p>I will mainly use this site to document stuff, most of which will likely be in the public domain.</p>","icon":"https://kb-static.jarylchng.com/kb-jarylchng-com/production/images/channel-c68f1f55f856ab833b4365991609dbec.png","favicon":"https://kb-static.jarylchng.com/kb-jarylchng-com/production/images/favicon-b94914f57599a477f9f72dab6bc71001.png","authors":[{"name":"Jaryl Chng"}],"language":"en-us","items":[{"id":"1MzzsuokbLV","title":"Linux Docker - \"Native\" Jellyfin Push 2FA MFA with LLDAP, Duo and DuoAuthProxy","attachments":[{"url":"https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/video-398b8f79d8e76c39dd60285dd980bf2e.mp4","mime_type":"video/mp4","size_in_byte":1304209,"duration_in_seconds":9}],"url":"https://kb.jarylchng.com/i/linux-docker-native-jellyfin-push-2fa-mfa-with-1MzzsuokbLV/","content_html":"<p>I wanted multifactor authentication on Jellyfin especially for my administrators which is unfortunately a missing feature natively.</p><p>Unfortunately configuring Authelia Forward Authentication or an SSO solution with OpenID Connect (OIDC) or Security Assertion Markup Language (SAML) would break native apps on non-PC devices due to drastically changing the authentication flow.</p><p>I wanted a solution that taps on Jellyfin's native authentication flow and only introduces a \"pause\" after clicking the log in button, which can be handled by any client, while waiting for the Duo push to be approved.</p><p>While this solution does not save a lot more space, I was inspired by <a href=\"https://forum.jellyfin.org/t-jellyfin-authentik-duo-2fa-solution-tutorial\" rel=\"noopener noreferrer\" target=\"_blank\">Jellyfin, Authentik, DUO. 2FA solution</a> but wanted a smaller, non Authentik, alternative.</p><p>Also, Docker Hub images for <a href=\"https://duo.com/docs/authproxy-reference\" rel=\"noopener noreferrer\" target=\"_blank\">DuoAuthProxy</a> were all outdated, so this was also an opportunity to bring it into my <a href=\"https://gitlab.com/users/jarylc/projects\" rel=\"noopener noreferrer\" target=\"_blank\">list of automatically updating images powered by Gitlab CI/CD</a>.</p><p><a href=\"https://hub.docker.com/r/minimages/duoauthproxy\" rel=\"noopener noreferrer\" target=\"_blank\">Just looking for the Alpine-based DuoAuthProxy docker image built by me? Click here!</a></p><p>Note: Since we will be accessing everything locally in this guide, even all within the docker network, I will not be going through setting up LDAP transit encryption (i.e. Secure LDAPS and StartTLS) as it would be out of scope.</p><h2>More Demos</h2><h3>Android app login video</h3><p> Your browser does not support the video element.</p><p><br></p><h2>Prerequisites</h2><ol><li><a href=\"https://jellyfin.org/\" rel=\"noopener noreferrer\" target=\"_blank\">Jellyfin</a> on Docker set-up prior</li><li>A free/paid account with <a href=\"https://duo.com/\" rel=\"noopener noreferrer\" target=\"_blank\">Duo</a> and access to the admin portal</li></ol><h2>Steps</h2><h3>Starting LLDAP</h3><p>Replace the following accordingly</p><ul><li>dc=example,dc=com (people normally follow their domain (i.e. example.com for this))</li><li>REPLACE_ME_WITH_YOUR_OWN_JWT_SECRET (this can be any long string really, string length is debatable and out of scope)</li><li>REPLACE_ME_WITH_YOUR_OWN_KEY_SEED (this can be any long string really, string length is debatable and out of scope)</li></ul><pre class=\"ql-syntax\" spellcheck=\"false\">docker run -d \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --name=LLDAP \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --network auth \\\n        -p 17170:17170 \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -e LLDAP_LDAP_BASE_DN=dc=example,dc=com \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -e LLDAP_JWT_SECRET=REPLACE_ME_WITH_YOUR_OWN_JWT_SECRET \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -e LLDAP_KEY_SEED=REPLACE_ME_WITH_YOUR_OWN_KEY_SEED \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -v /path/to/data:/data \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --restart unless-stopped \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lldap/lldap:stable\n</pre><p>Reference: <a href=\"https://github.com/lldap/lldap\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/lldap/lldap</a></p><h3>Configuring LLDAP</h3><p>Visit the frontend at port 17170 and create a service user for DuoAuthProxy accordingly, you can replace the values if needed.</p><p><img src=\"https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-0453045526fe05a2d49b0bd950cbd409.png\"></p><p>For the sake of this example, the password of the svc-duoauthproxy user is `password`</p><p>Make sure to add the user to either lldap_strict_readonly (read-only) or lldap_password_manager (allow password change on Jellyfin) groups</p><p><img src=\"https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-752a821b48ce5a1afb8cc3c730861dbc.png\"></p><h3>Creating Duo application</h3><p>Access your <a href=\"https://admin.duosecurity.com\" rel=\"noopener noreferrer\" target=\"_blank\">Duo admin console</a></p><p>Go to Applications -&gt; Protect an Application</p><p>Search for `proxy` and protect an `LDAP Proxy` application</p><p><img src=\"https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-19c285167451cd3104c2f320eebc5e0b.png\"></p><p>Copy the credentials to prepare for the next step</p><p><img src=\"https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-93fcb6188216f32127a7ddf081b5718e.png\"></p><h3>Configuring DuoAuthProxy</h3><p>If you are using <a href=\"https://hub.docker.com/r/minimages/duoauthproxy\" rel=\"noopener noreferrer\" target=\"_blank\">my image</a>, it is necessary to craft the configuration file first</p><p>Replace the following keys accordingly</p><ul><li>bind_dn</li><li>service_account_username</li><li>service_account_password</li><li>search_dn</li><li>ikey</li><li>skey</li><li>api_host</li></ul><pre class=\"ql-syntax\" spellcheck=\"false\">; Complete documentation about the Duo Auth Proxy can be found here:\n; https://duo.com/docs/authproxy_reference\n\n[main]\nlog_stdout=true\n\n[ad_client]\nhost=LLDAP\nport=3890\nauth_type=plain\nbind_dn=uid=svc-duoauthproxy,ou=people,dc=example,dc=com\nservice_account_username=svc-duoauthproxy\nservice_account_password=password\nsearch_dn=ou=people,dc=example,dc=com\nusername_attribute=uid\nat_attribute=mail\n\n[ldap_server_auto]\nikey=DIXXXXXXXXXXXXXXXXXX\nskey=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\napi_host=api-XXXXXXXX.duosecurity.com\nfailmode=secure\nclient=ad_client\nport=1812\nexempt_primary_bind=false\nexempt_ou_1=uid=svc-duoauthproxy,ou=people,dc=example,dc=com\n</pre><p>Reference: <a href=\"https://duo.com/docs/authproxy_reference\" rel=\"noopener noreferrer\" target=\"_blank\">https://duo.com/docs/authproxy_reference</a></p><h3>Starting DuoAuthProxy</h3><pre class=\"ql-syntax\" spellcheck=\"false\">docker run -d \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --name=DuoAuthProxy \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --network auth \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -v /path/to/authproxy.cfg:/app/conf/authproxy.cfg \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --restart unless-stopped \\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; minimages/duoauthproxy\n</pre><p>Feel free to use alternatives as well or even <a href=\"https://duo.com/docs/authproxy-reference#installation\" rel=\"noopener noreferrer\" target=\"_blank\">host it natively or generate your own image using the official instructions</a>.</p><h3>Preparing Jellyfin</h3><p>I would assume you would already have Jellyfin set up, add Jellyfin to the auth network by re-running the container while adding `--network auth` argument to the docker run</p><h3>Installing LDAP Authentication Jellyfin Plugin</h3><p>Note: I'm currently using the unstable version of Jellyfin (10.9.0) as of posting, I had to use the unstable plugins repository.</p><p><img src=\"https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-20a91c182831f4b80999ec519c7eb3c2.png\"><img src=\"https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-87410bcb3ef2dfbc2be257927231d614.png\"></p><p>Please be reminded to restart Jellyfin after installing the plugin.</p><h3>Setting up LDAP Authentication Jellyfin Plugin</h3><p>Set the following configuration parameters for LDAP Server Settings section</p><ul><li>LDAP Server: DuoAuthProxy</li><li>LDAP Port: 1812</li><li>Secure LDAP: unchecked</li><li>StartTLS: unchecked</li><li>Skip SSL/TLS Verification: checked</li><li>Allow Password Change: (optional, but ensure the service account have the lldap_password_manager group if checked)</li><li>LDAP Bind User: uid=svc-duoauthproxy,ou=people,dc=example,dc=com</li><li>LDAP Bind User Password: password</li><li>LDAP Base DN for searches: ou=people,dc=example,dc=com</li></ul><p>At this point click `Save and Test LDAP Server Settings` to test connectivity, it should pass if all your settings are good.</p><p>Set the following configuration parameters for LDAP User Settings section</p><ul><li>LDAP Search Filter: (uid=*)</li><li>LDAP Search Attributes: uid, mail</li><li>LDAP Uid Attribute: uid</li><li>LDAP Username Attribute: uid</li><li>LDAP Password Attribute: userPassword</li><li>LDAP Admin Filter: (memberof=cn=lldap_admin,ou=example,dc=com)</li></ul><p>Note you should change your LDAP Search Filter and LDAP Admin Filter according to your needs, reference: <a href=\"https://github.com/lldap/lldap/blob/main/example_configs/jellyfin.md\" rel=\"noopener noreferrer\" target=\"_blank\">https://github.com/lldap/lldap/blob/main/example_configs/jellyfin.md</a></p><p>At this point click `Save and Test LDAP Filter Settings` and check if there are more than 0 users and admins found, it should if all your settings are good.</p><p>Afterwhich, enter `admin` in `Test Login Name` field and `Save Search Attribute Settings and Query User` button to do a final lookup check and save.</p><p>Finally, configure Jellyfin User Settings sections to your needs and don't forget to hit the big blue `Save` button.</p><h3>(If Jellyfin has existing users) Switch users' Authentication Provider to LDAP-Authentication</h3><p><img src=\"https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-fb7d2f5a7d9b7f612cdcc169dd69fce5.png\"></p><h3>Test the new authentication flow</h3><p>The new authentication flow should be immediately active, you can log out and test it.</p><p>If not, you can try another restart of Jellyfin first.</p><h2>Afterword</h2><p>Of course, this guide will not be a one-size fit all like the Authentik solution was for me.</p><p>A lot of settings can and should be customized to your needs.</p><p>This guide only follows through the most basic setup.</p>","content_text":"I wanted multifactor authentication on Jellyfin especially for my administrators\nwhich is unfortunately a missing feature natively.\n\nUnfortunately configuring Authelia Forward Authentication or an SSO solution\nwith OpenID Connect (OIDC) or Security Assertion Markup Language (SAML) would\nbreak native apps on non-PC devices due to drastically changing the\nauthentication flow.\n\nI wanted a solution that taps on Jellyfin's native authentication flow and only\nintroduces a \"pause\" after clicking the log in button, which can be handled by\nany client, while waiting for the Duo push to be approved.\n\nWhile this solution does not save a lot more space, I was inspired by Jellyfin,\nAuthentik, DUO. 2FA solution but wanted a smaller, non Authentik, alternative.\n\nAlso, Docker Hub images for DuoAuthProxy were all outdated, so this was also an\nopportunity to bring it into my list of automatically updating images powered by\nGitlab CI/CD.\n\nJust looking for the Alpine-based DuoAuthProxy docker image built by me? Click\nhere!\n\nNote: Since we will be accessing everything locally in this guide, even all\nwithin the docker network, I will not be going through setting up LDAP transit\nencryption (i.e. Secure LDAPS and StartTLS) as it would be out of scope.\n\n\nMORE DEMOS\n\n\nANDROID APP LOGIN VIDEO\n\nYour browser does not support the video element.\n\n\n\n\n\nPREREQUISITES\n\n 1. Jellyfin on Docker set-up prior\n 2. A free/paid account with Duo and access to the admin portal\n\n\nSTEPS\n\n\nSTARTING LLDAP\n\nReplace the following accordingly\n\n * dc=example,dc=com (people normally follow their domain (i.e. example.com for\n   this))\n * REPLACE_ME_WITH_YOUR_OWN_JWT_SECRET (this can be any long string really,\n   string length is debatable and out of scope)\n * REPLACE_ME_WITH_YOUR_OWN_KEY_SEED (this can be any long string really, string\n   length is debatable and out of scope)\n\ndocker run -d \\\n        --name=LLDAP \\\n        --network auth \\\n        -p 17170:17170 \\\n        -e LLDAP_LDAP_BASE_DN=dc=example,dc=com \\\n        -e LLDAP_JWT_SECRET=REPLACE_ME_WITH_YOUR_OWN_JWT_SECRET \\\n        -e LLDAP_KEY_SEED=REPLACE_ME_WITH_YOUR_OWN_KEY_SEED \\\n        -v /path/to/data:/data \\\n        --restart unless-stopped \\\n        lldap/lldap:stable\n\n\nReference: https://github.com/lldap/lldap\n\n\nCONFIGURING LLDAP\n\nVisit the frontend at port 17170 and create a service user for DuoAuthProxy\naccordingly, you can replace the values if needed.\n\n[https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-0453045526fe05a2d49b0bd950cbd409.png]\n\nFor the sake of this example, the password of the svc-duoauthproxy user is\n`password`\n\nMake sure to add the user to either lldap_strict_readonly (read-only) or\nlldap_password_manager (allow password change on Jellyfin) groups\n\n[https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-752a821b48ce5a1afb8cc3c730861dbc.png]\n\n\nCREATING DUO APPLICATION\n\nAccess your Duo admin console\n\nGo to Applications -> Protect an Application\n\nSearch for `proxy` and protect an `LDAP Proxy` application\n\n[https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-19c285167451cd3104c2f320eebc5e0b.png]\n\nCopy the credentials to prepare for the next step\n\n[https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-93fcb6188216f32127a7ddf081b5718e.png]\n\n\nCONFIGURING DUOAUTHPROXY\n\nIf you are using my image, it is necessary to craft the configuration file first\n\nReplace the following keys accordingly\n\n * bind_dn\n * service_account_username\n * service_account_password\n * search_dn\n * ikey\n * skey\n * api_host\n\n; Complete documentation about the Duo Auth Proxy can be found here:\n; https://duo.com/docs/authproxy_reference\n\n[main]\nlog_stdout=true\n\n[ad_client]\nhost=LLDAP\nport=3890\nauth_type=plain\nbind_dn=uid=svc-duoauthproxy,ou=people,dc=example,dc=com\nservice_account_username=svc-duoauthproxy\nservice_account_password=password\nsearch_dn=ou=people,dc=example,dc=com\nusername_attribute=uid\nat_attribute=mail\n\n[ldap_server_auto]\nikey=DIXXXXXXXXXXXXXXXXXX\nskey=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\napi_host=api-XXXXXXXX.duosecurity.com\nfailmode=secure\nclient=ad_client\nport=1812\nexempt_primary_bind=false\nexempt_ou_1=uid=svc-duoauthproxy,ou=people,dc=example,dc=com\n\n\nReference: https://duo.com/docs/authproxy_reference\n\n\nSTARTING DUOAUTHPROXY\n\ndocker run -d \\\n        --name=DuoAuthProxy \\\n        --network auth \\\n        -v /path/to/authproxy.cfg:/app/conf/authproxy.cfg \\\n        --restart unless-stopped \\\n        minimages/duoauthproxy\n\n\nFeel free to use alternatives as well or even host it natively or generate your\nown image using the official instructions.\n\n\nPREPARING JELLYFIN\n\nI would assume you would already have Jellyfin set up, add Jellyfin to the auth\nnetwork by re-running the container while adding `--network auth` argument to\nthe docker run\n\n\nINSTALLING LDAP AUTHENTICATION JELLYFIN PLUGIN\n\nNote: I'm currently using the unstable version of Jellyfin (10.9.0) as of\nposting, I had to use the unstable plugins repository.\n\n[https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-20a91c182831f4b80999ec519c7eb3c2.png][https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-87410bcb3ef2dfbc2be257927231d614.png]\n\nPlease be reminded to restart Jellyfin after installing the plugin.\n\n\nSETTING UP LDAP AUTHENTICATION JELLYFIN PLUGIN\n\nSet the following configuration parameters for LDAP Server Settings section\n\n * LDAP Server: DuoAuthProxy\n * LDAP Port: 1812\n * Secure LDAP: unchecked\n * StartTLS: unchecked\n * Skip SSL/TLS Verification: checked\n * Allow Password Change: (optional, but ensure the service account have the\n   lldap_password_manager group if checked)\n * LDAP Bind User: uid=svc-duoauthproxy,ou=people,dc=example,dc=com\n * LDAP Bind User Password: password\n * LDAP Base DN for searches: ou=people,dc=example,dc=com\n\nAt this point click `Save and Test LDAP Server Settings` to test connectivity,\nit should pass if all your settings are good.\n\nSet the following configuration parameters for LDAP User Settings section\n\n * LDAP Search Filter: (uid=*)\n * LDAP Search Attributes: uid, mail\n * LDAP Uid Attribute: uid\n * LDAP Username Attribute: uid\n * LDAP Password Attribute: userPassword\n * LDAP Admin Filter: (memberof=cn=lldap_admin,ou=example,dc=com)\n\nNote you should change your LDAP Search Filter and LDAP Admin Filter according\nto your needs, reference:\nhttps://github.com/lldap/lldap/blob/main/example_configs/jellyfin.md\n\nAt this point click `Save and Test LDAP Filter Settings` and check if there are\nmore than 0 users and admins found, it should if all your settings are good.\n\nAfterwhich, enter `admin` in `Test Login Name` field and `Save Search Attribute\nSettings and Query User` button to do a final lookup check and save.\n\nFinally, configure Jellyfin User Settings sections to your needs and don't\nforget to hit the big blue `Save` button.\n\n\n(IF JELLYFIN HAS EXISTING USERS) SWITCH USERS' AUTHENTICATION PROVIDER TO\nLDAP-AUTHENTICATION\n\n[https://kb-static.jarylchng.com/kb-jarylchng-com/production/media/rich-editor/items/1MzzsuokbLV/image-fb7d2f5a7d9b7f612cdcc169dd69fce5.png]\n\n\nTEST THE NEW AUTHENTICATION FLOW\n\nThe new authentication flow should be immediately active, you can log out and\ntest it.\n\nIf not, you can try another restart of Jellyfin first.\n\n\nAFTERWORD\n\nOf course, this guide will not be a one-size fit all like the Authentik solution\nwas for me.\n\nA lot of settings can and should be customized to your needs.\n\nThis guide only follows through the most basic setup.","date_published":"2024-04-12T01:01:00.000Z","_microfeed":{"is_audio":false,"is_document":false,"is_external_url":false,"is_video":true,"is_image":false,"web_url":"https://kb.jarylchng.com/i/linux-docker-native-jellyfin-push-2fa-mfa-with-1MzzsuokbLV/","json_url":"https://kb.jarylchng.com/i/1MzzsuokbLV/json/","rss_url":"https://kb.jarylchng.com/i/1MzzsuokbLV/rss/","guid":"1MzzsuokbLV","status":"published","duration_hhmmss":"00:00:09","itunes:episodeType":"full","date_published_short":"Thu Apr 11 2024","date_published_ms":1712883660000}}],"_microfeed":{"microfeed_version":"0.1.2","base_url":"https://kb.jarylchng.com","categories":[{"name":"Technology"}],"subscribe_methods":[{"name":"RSS","type":"rss","url":"https://kb.jarylchng.com/rss/","image":"https://kb.jarylchng.com/assets/brands/subscribe/rss.png","enabled":true,"editable":false,"id":"sQbXXExV58H"},{"name":"JSON","type":"json","url":"https://kb.jarylchng.com/json/","image":"https://kb.jarylchng.com/assets/brands/subscribe/json.png","enabled":true,"editable":false,"id":"nC8cjLCnOOi"}],"description_text":"Welcome to the index page of my knowledge base, if you haven't done so, do visit\nmy website at https://jarylchng.com.\n\nI will mainly use this site to document stuff, most of which will likely be in\nthe public domain.","copyright":"©2024","itunes:type":"episodic","items_sort_order":"newest_first"}}