Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Self signed certificate chain is not showing up in the certificate path #2204

Open
1 task done
alternativ opened this issue Aug 26, 2024 · 9 comments
Open
1 task done
Labels
Status: Needs Triage New Issue needing triage Type: Bug Issue is a bug

Comments

@alternativ
Copy link

Is there an existing issue for this?

  • I have searched the existing open and closed issues

Current Behavior

Prowlarr is running in a docker container, my containers are hosted using a macvlan setup. Essentially every container gets it's own ip on the network. That also means that traffic is moving unsecured over my local lan. Time to fix it, I setup ssl in the prowlar config under settings/general/host. Configured a pcks12 file in the pfx export format requested by prowlarr, but the certificate chain is not popping up. The same certificate is working in a plex container. Below the debug info:

user@server:/etc/config/certificates$ openssl pkcs12 -in docker.pfx -clcerts -passout pass:"" | openssl x509 -serial -noout
serial=46F75F5374DC084069C96D54CB2DA762638C9DE2

user@server:/etc/config/certificates$ sha256sum docker.p12 docker.pfx
f67a2f12e33b71b74b86f185589d30643f0eafbb6aea1f4422929ce7935087e9  docker.p12
f67a2f12e33b71b74b86f185589d30643f0eafbb6aea1f4422929ce7935087e9  docker.pfx
Screenshot 2024-08-26 at 22 01 04 Screenshot 2024-08-26 at 22 01 30

Expected Behavior

If I use a certificate that hosts a certificate chain, in a browser the full certificate chain should be visible not only the server certificate.

Steps To Reproduce

See above. Logs don't display much regarding prowlarr's webserver.

Environment

- OS: docker
- Prowlarr: 1.21.2.4649
- Docker Install: 1.21.2.4649-ls83 by linuxserver.io
- Using Reverse Proxy: no, local lan
- Browser: safari/chrome/firefox all experience the same

What branch are you running?

Master

Trace Logs?

2024-08-26 22:10:56.5|Info|Microsoft.Hosting.Lifetime|Now listening on: https://[::]:443
2024-08-26 22:10:56.5|Trace|EventAggregator|Publishing ApplicationStartedEvent
2024-08-26 22:10:56.5|Trace|EventAggregator|ApplicationStartedEvent -> UpdaterConfigProvider
2024-08-26 22:10:56.5|Trace|EventAggregator|ApplicationStartedEvent <- UpdaterConfigProvider
2024-08-26 22:10:56.5|Trace|EventAggregator|ApplicationStartedEvent -> UpdateHistoryService
2024-08-26 22:10:56.7|Trace|EventAggregator|ApplicationStartedEvent <- UpdateHistoryService
2024-08-26 22:10:56.7|Trace|EventAggregator|ApplicationStartedEvent -> ProgressMessageTarget
2024-08-26 22:10:56.7|Trace|EventAggregator|ApplicationStartedEvent <- ProgressMessageTarget
2024-08-26 22:10:56.7|Trace|EventAggregator|ApplicationStartedEvent -> AppSyncProfileService
2024-08-26 22:10:56.7|Trace|EventAggregator|ApplicationStartedEvent <- AppSyncProfileService
2024-08-26 22:10:56.7|Trace|EventAggregator|ApplicationStartedEvent -> NotificationFactory
2024-08-26 22:10:56.7|Debug|NotificationFactory|Initializing Providers. Count 20
2024-08-26 22:10:56.7|Trace|Http|Req: 1 [GET] /ping (from ::1 curl/8.9.0)
2024-08-26 22:10:56.7|Trace|EventAggregator|ApplicationStartedEvent <- NotificationFactory
2024-08-26 22:10:56.7|Trace|EventAggregator|ApplicationStartedEvent -> CommandExecutor
2024-08-26 22:10:56.7|Info|CommandExecutor|Starting 3 threads for tasks.
2024-08-26 22:10:56.7|Trace|EventAggregator|ApplicationStartedEvent <- CommandExecutor
2024-08-26 22:10:56.7|Trace|EventAggregator|ApplicationStartedEvent -> CommandQueueManager
2024-08-26 22:10:56.7|Trace|CommandQueueManager|Orphaning incomplete commands
2024-08-26 22:10:56.8|Trace|EventAggregator|ApplicationStartedEvent <- CommandQueueManager
2024-08-26 22:10:56.8|Trace|EventAggregator|ApplicationStartedEvent -> Scheduler
2024-08-26 22:10:56.8|Trace|EventAggregator|ApplicationStartedEvent <- Scheduler
2024-08-26 22:10:56.8|Trace|EventAggregator|ApplicationStartedEvent -> TaskManager
2024-08-26 22:10:56.8|Trace|ConfigService|Using default config value for 'backupinterval' defaultValue:'7'
2024-08-26 22:10:56.8|Trace|TaskManager|Initializing jobs. Available: 8 Existing: 8
2024-08-26 22:10:56.8|Debug|Prowlarr.Http.Authentication.ApiKeyAuthenticationHandler|AuthenticationScheme: API was not authenticated.
2024-08-26 22:10:56.8|Trace|Http|Res: 1 [GET] /ping: 200.OK (149 ms)
2024-08-26 22:10:56.9|Trace|EventAggregator|ApplicationStartedEvent <- TaskManager
2024-08-26 22:10:56.9|Trace|EventAggregator|ApplicationStartedEvent -> IndexerDefinitionUpdateService
2024-08-26 22:10:56.9|Debug|HttpClient|Downloading [https://indexers.prowlarr.com/master/11/package.zip] to [/config/Definitions/indexers.zip]
2024-08-26 22:10:57.0|Trace|HttpClient|Req: [GET] https://indexers.prowlarr.com/master/11/package.zip
2024-08-26 22:10:57.0|Trace|ConfigService|Using default config value for 'proxyenabled' defaultValue:'False'
2024-08-26 22:10:57.1|Info|ManagedHttpDispatcher|IPv4 is available: True, IPv6 will be disabled
2024-08-26 22:10:57.2|Trace|HttpClient|Res: HTTP/2.0 [GET] https://indexers.prowlarr.com/master/11/package.zip: 200.OK (0 bytes) (214 ms)
2024-08-26 22:10:57.2|Debug|HttpClient|Downloading Completed. took 0s
2024-08-26 22:10:57.4|Trace|DiskProviderBase|Deleting file: /config/Definitions/indexers.zip
2024-08-26 22:10:57.4|Debug|IndexerDefinitionUpdateService|Updated indexer definitions
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent <- IndexerDefinitionUpdateService
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent -> IndexerFactory
2024-08-26 22:10:57.4|Debug|IndexerFactory|Initializing Providers. Count 67
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent <- IndexerFactory
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent -> IndexerProxyFactory
2024-08-26 22:10:57.4|Debug|IndexerProxyFactory|Initializing Providers. Count 4
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent <- IndexerProxyFactory
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent -> DownloadClientFactory
2024-08-26 22:10:57.4|Debug|DownloadClientFactory|Initializing Providers. Count 18
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent <- DownloadClientFactory
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent -> ApplicationFactory
2024-08-26 22:10:57.4|Debug|ApplicationFactory|Initializing Providers. Count 7
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent <- ApplicationFactory
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent ~> UpdateHistoryService
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent ~> ReconfigureSentry
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent <~ UpdateHistoryService
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent ~> HealthCheckService
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent ~> ConfigFileProvider
2024-08-26 22:10:57.4|Info|Microsoft.Hosting.Lifetime|Application started. Press Ctrl C to shut down.
2024-08-26 22:10:57.4|Trace|Http|Req: 2 [GET] /logfile/prowlarr.trace.txt (from 172.16.3.87 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36)
2024-08-26 22:10:57.4|Info|Microsoft.Hosting.Lifetime|Hosting environment: Production
2024-08-26 22:10:57.4|Info|Microsoft.Hosting.Lifetime|Content root path: /app/prowlarr/bin
2024-08-26 22:10:57.4|Trace|EventAggregator|ApplicationStartedEvent <~ ReconfigureSentry
2024-08-26 22:10:57.4|Trace|HealthCheckService|Check health -> ServerSideNotificationService
2024-08-26 22:10:57.5|Trace|ServerSideNotificationService|Getting notifications
2024-08-26 22:10:57.5|Trace|HttpClient|Req: [GET] https://prowlarr.servarr.com/v1/notification?version=1.21.2.4649&os=linuxmusl&arch=X64&runtime=netcore&branch=master
2024-08-26 22:10:57.5|Trace|ConfigService|Using default config value for 'proxyenabled' defaultValue:'False'
2024-08-26 22:10:57.4|Error|TaskExtensions|Task Error

[v1.21.2.4649] System.NullReferenceException: Object reference not set to an instance of an object.
   at NzbDrone.Core.Configuration.ConfigFileProvider.SetValue(String key, Object value) in ./Prowlarr.Core/Configuration/ConfigFileProvider.cs:line 335
   at NzbDrone.Core.Configuration.ConfigFileProvider.<>c__DisplayClass89_0.<GetValue>b__0() in ./Prowlarr.Core/Configuration/ConfigFileProvider.cs:line 325
   at NzbDrone.Common.Cache.Cached`1.Get(String key, Func`1 function, Nullable`1 lifeTime) in ./Prowlarr.Common/Cache/Cached.cs:line 98
   at NzbDrone.Core.Configuration.ConfigFileProvider.GetValue(String key, Object defaultValue, Boolean persist) in ./Prowlarr.Core/Configuration/ConfigFileProvider.cs:line 308
   at NzbDrone.Core.Configuration.ConfigFileProvider.MigrateConfigFile() in ./Prowlarr.Core/Configuration/ConfigFileProvider.cs:line 378
   at NzbDrone.Core.Configuration.ConfigFileProvider.HandleAsync(ApplicationStartedEvent message) in ./Prowlarr.Core/Configuration/ConfigFileProvider.cs:line 463
   at NzbDrone.Core.Messaging.Events.EventAggregator.<>c__DisplayClass6_2`1.<PublishEvent>b__2() in ./Prowlarr.Core/Messaging/Events/EventAggregator.cs:line 120
   at System.Threading.Tasks.Task.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__272_0(Object obj)
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)


2024-08-26 22:10:57.6|Trace|Http|Res: 2 [GET] /logfile/prowlarr.trace.txt: 200.OK (183 ms)
2024-08-26 22:10:57.6|Trace|HttpClient|Res: HTTP/2.0 [GET] https://prowlarr.servarr.com/v1/notification?version=1.21.2.4649&os=linuxmusl&arch=X64&runtime=netcore&branch=master: 200.OK (2 bytes) (155 ms)
2024-08-26 22:10:57.7|Trace|Http|Req: 3 [GET] /favicon.ico (from 172.16.3.87 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36)
2024-08-26 22:10:57.7|Trace|Http|Res: 3 [GET] /favicon.ico: 200.OK (18 ms)
2024-08-26 22:10:57.8|Trace|HealthCheckService|Check health <- ServerSideNotificationService
2024-08-26 22:10:57.8|Trace|HealthCheckService|Check health -> ApiKeyValidationCheck
2024-08-26 22:10:57.8|Trace|HealthCheckService|Check health <- ApiKeyValidationCheck
2024-08-26 22:10:57.8|Trace|HealthCheckService|Check health -> AppDataLocationCheck
2024-08-26 22:10:57.8|Trace|HealthCheckService|Check health <- AppDataLocationCheck
2024-08-26 22:10:57.8|Trace|HealthCheckService|Check health -> ApplicationLongTermStatusCheck
2024-08-26 22:10:57.8|Trace|HealthCheckService|Check health <- ApplicationLongTermStatusCheck
2024-08-26 22:10:57.8|Trace|HealthCheckService|Check health -> ApplicationStatusCheck
2024-08-26 22:10:57.8|Trace|HealthCheckService|Check health <- ApplicationStatusCheck
2024-08-26 22:10:57.8|Trace|HealthCheckService|Check health -> DownloadClientStatusCheck
2024-08-26 22:10:57.8|Trace|HealthCheckService|Check health <- DownloadClientStatusCheck```

### Trace Logs have been provided as applicable. Reports may be closed if the required logs are not provided.

- [X] I have read and followed the steps in the wiki link above and provided the required trace logs - the logs contain `trace` - that are relevant and show this issue.
@alternativ alternativ added Status: Needs Triage New Issue needing triage Type: Bug Issue is a bug labels Aug 26, 2024
@mynameisbogdan
Copy link
Contributor

Post the contents of your config.xml file.

Also please use openssl s_client -connect google:443 instead.

@alternativ
Copy link
Author

alternativ commented Aug 26, 2024

<Config>
  <BindAddress>*</BindAddress>
  <Port>9696</Port>
  <SslPort>443</SslPort>
  <EnableSsl>True</EnableSsl>
  <LaunchBrowser>True</LaunchBrowser>
  <ApiKey>ommited</ApiKey>
  <AuthenticationMethod>Forms</AuthenticationMethod>
  <AuthenticationRequired>Enabled</AuthenticationRequired>
  <Branch>master</Branch>
  <LogLevel>trace</LogLevel>
  <SslCertPath>/certificates/docker.pfx</SslCertPath>
  <SslCertPassword>ommited</SslCertPassword>
  <UrlBase>ommited</UrlBase>
  <InstanceName>Prowlarr</InstanceName>
  <UpdateMechanism>Docker</UpdateMechanism>
  <Theme>dark</Theme>
  <AnalyticsEnabled>False</AnalyticsEnabled>
</Config>

openssl s_client -connect domain:443
CONNECTED(00000003)
depth=0 C = NL, ST = Some-State, O = doma.in, CN = docker
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = NL, ST = Some-State, O = doma.in, CN = docker
verify error:num=21:unable to verify the first certificate
verify return:1
depth=0 C = NL, ST = Some-State, O = doma.in, CN = docker
verify return:1
---

@mynameisbogdan
Copy link
Contributor

Can you post step by step commands how to create the SSL certificates so I can reproduce yours?

@alternativ
Copy link
Author

alternativ commented Aug 26, 2024

https://deliciousbrains.com/ssl-certificate-authority-for-local-https-development/

  1. openssl genrsa -out rootCA.key 4096
  2. openssl req -x509 -new -nodes -key rootCA.key -sha512 -days 3650 -out rootCA.pem
  3. openssl genrsa -out domain.key 4096
  4. openssl req -new -nodes -key domain.key -out domain.csr
  5. openssl x509 -req -in domain.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out domain.crt -days 825 -sha256 -extfile config
  6. cat domain.crt rootCA.pem > bundle.crt
  7. openssl pkcs12 -export -out bundle.pfx -inkey domain.key -in bundle.crt

config:

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = domain

@mynameisbogdan
Copy link
Contributor

Except for the fact you truncated the important part from the openssl command, it root CA shows up in my chain.

openssl s_client -connect prowlarr:9697
Connecting to 127.0.0.1
CONNECTED(00000003)
Can't use SSL_get_servername
depth=0 C=US, ST=Texas, O=USER CERT LTD, CN=prowlarr
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C=US, ST=Texas, O=USER CERT LTD, CN=prowlarr
verify error:num=21:unable to verify the first certificate
verify return:1
depth=0 C=US, ST=Texas, O=USER CERT LTD, CN=prowlarr
verify return:1
---
Certificate chain
 0 s:C=US, ST=Texas, O=USER CERT LTD, CN=prowlarr
   i:C=UK, ST=London, O=ROOT CA LTD, CN=root-prowlarr.com
   a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256
   v:NotBefore: Aug 26 21:17:23 2024 GMT; NotAfter: Nov 29 21:17:23 2026 GMT

Same output when using a .NET 8 build. I'm still not certain yet if a dotnet issue or a misconfiguration in certificate generation.

@alternativ
Copy link
Author

alternativ commented Aug 26, 2024

Good that you can reproduce.

Perhaps an issue with the library that exports the cert to the browser. The certificate chain should be good as I mentioned above in the plex instance the chain pops up correctly. Files are bit for bit comparable.

I tried multiple ways of generating the chain with the same results (stacked certsbundle.pem or generating a pfx with a separate carootbundle).

Furthermore I saw a tip that dotnet might require the rootCA to be present in the certificate store. I exported the rootCA.crt using a docker volume and ran the update ca certificates command, also to no avail.

openssl s_client -connect plex.docker:32400
CONNECTED([00000003](tel:00000003))
depth=1 C = NL, ST = Some-State, O = doma.in
verify error:num=19:self-signed certificate in certificate chain
verify return:1
depth=1 C = NL, ST = Some-State, O = doma.in
verify return:1
depth=0 C = NL, ST = Some-State, O = doma.in, CN = docker
verify return:1
---
Certificate chain
 0 s:C = NL, ST = Some-State, O = doma.in, CN = docker
   i:C = NL, ST = Some-State, O = doma.in
   a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256
   v:NotBefore: Aug 26 13:59:19 2024 GMT; NotAfter: Aug 24 13:59:19 2034 GMT
 1 s:C = NL, ST = Some-State, O = doma.in
   i:C = NL, ST = Some-State, O = doma.in
   a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256
   v:NotBefore: Aug 26 13:50:48 2024 GMT; NotAfter: Aug 24 13:50:48 2034 GMT
---

openssl s_client -connect prowlarr.docker:443
CONNECTED([00000003](tel:00000003))
depth=0 C = NL, ST = Some-State, O = doma.in, CN = docker
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 C = NL, ST = Some-State, O = doma.in, CN = docker
verify error:num=21:unable to verify the first certificate
verify return:1
depth=0 C = NL, ST = Some-State, O = doma.in, CN = docker
verify return:1
---
Certificate chain
 0 s:C = NL, ST = Some-State, O = doma.in, CN = docker
   i:C = NL, ST = Some-State, O = doma.in
   a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256
   v:NotBefore: Aug 26 13:59:19 2024 GMT; NotAfter: Aug 24 13:59:19 2034 GMT
---

@alternativ
Copy link
Author

alternativ commented Aug 27, 2024

I am not familiair with .net but it seems to be here:

src/Prowlarr.Api.V1/Config/HostConfigController.cs

  private bool IsValidSslCertificate(HostConfigResource resource)
        {
            X509Certificate2 cert;
            try
            {
                cert = new X509Certificate2(resource.SslCertPath, resource.SslCertPassword, X509KeyStorageFlags.DefaultKeySet);
            }
            catch
            {
                return false;
            }

            return cert != null;
        }

src/NzbDrone.Host/Bootstrap.cs

private static X509Certificate2 ValidateSslCertificate(string cert, string password)
        {
            X509Certificate2 certificate;

            try
            {
                certificate = new X509Certificate2(cert, password, X509KeyStorageFlags.DefaultKeySet);
            }
            catch (CryptographicException ex)
            {
                if (ex.HResult == 0x2 || ex.HResult == 0x2006D080)
                {
                    throw new ProwlarrStartupException(ex,
                        $"The SSL certificate file {cert} does not exist");
                }

                throw new ProwlarrStartupException(ex);
            }

            return certificate;
        }
    }

Shamelessly asking a llm for help:

private static X509Certificate2 ValidateSslCertificate(string cert, string password)
{
    X509Certificate2 certificate;

    try
    {
        certificate = new X509Certificate2(cert, password, X509KeyStorageFlags.DefaultKeySet);
    }
    catch (CryptographicException ex)
    {
        if (ex.HResult == 0x2 || ex.HResult == 0x2006D080)
        {
            throw new ProwlarrStartupException(ex, $"The SSL certificate file {cert} does not exist");
        }

        throw new ProwlarrStartupException(ex);
    }

    // Load and validate the certificate chain
    var chain = new X509Chain();
    chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; // Adjust based on your revocation check needs
    chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;

    bool isChainValid = chain.Build(certificate);

    if (!isChainValid)
    {
        throw new ProwlarrStartupException("SSL certificate chain validation failed.");
    }

    return certificate;
}

@mynameisbogdan
Copy link
Contributor

Doesn't work.

But searching related stuff, looks like a limitation of X509Certificate2 since it can only hold one leaf. Didn't find any proper solutions to work with Kestrel.

@alternativ
Copy link
Author

alternativ commented Aug 28, 2024

https://www.straypaper.com/kestrel-and-chain-of-trust/, seems possible but not out of the box.

And if you’re testing if the chain works make sure that the root ca is in the trusted certificate store (cp rootCA.crt /usr/share/ca-certificates/(mozilla/)rootCA.crt && sudo update-ca-certificates), without it Kestrel will not display the rootCA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Needs Triage New Issue needing triage Type: Bug Issue is a bug
Projects
None yet
Development

No branches or pull requests

10 participants
@mynameisbogdan @alternativ and others