changeset 658:31a7e52b3da6

split DICOM TLS in 2: check-client and no-check-client
author Alain Mazy <am@orthanc.team>
date Mon, 17 Jun 2024 18:25:18 +0200
parents 9582c37652f4
children 3ac37a99a093
files .hgignore README Tests/CheckDicomTls.py
diffstat 3 files changed, 73 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Wed Jun 05 17:53:36 2024 +0200
+++ b/.hgignore	Mon Jun 17 18:25:18 2024 +0200
@@ -10,4 +10,7 @@
 *~
 NewTests/storages/**
 NewTests/configurations/*.json
-.env/
\ No newline at end of file
+.env/
+Tests/*.crt
+Tests/*.key
+Tests/dicom-tls.json
\ No newline at end of file
--- a/README	Wed Jun 05 17:53:36 2024 +0200
+++ b/README	Mon Jun 17 18:25:18 2024 +0200
@@ -139,6 +139,23 @@
 To run the IngestTranscoding tests:
 # rm -rf /tmp/OrthancTest && python ./Tests/CheckIngestTranscoding.py /home/alain/o/build/orthanc/orthanc
 
+To run the DICOM TLS tests without Client certificate checks:
+# cd ./Tests
+# python CheckDicomTls.py --config-no-check-client
+# /home/alain/o/build/orthanc/Orthanc --verbose dicom-tls.json
+#### or
+# docker run -p 8042:8042 -p 4242:4242 -e ORTHANC__DICOM_TLS_ENABLED=true -e ORTHANC__DICOM_TLS_CERTIFICATE=/certs/dicom-tls-a.crt -e ORTHANC__DICOM_TLS_PRIVATE_KEY=/certs/dicom-tls-a.key -e ORTHANC__DICOM_TLS_REMOTE_CERTIFICATE_REQUIRED=false -e ORTHANC__DICOM_TLS_TRUSTED_CERTIFICATES=/certs/dicom-tls-trusted.crt -e ORTHANC__EXECUTE_LUA_ENABLED=true -v .:/certs/ -e ORTHANC__AUTHENTICATION_ENABLED=false -e VERBOSE_ENABLED=true orthancteam/orthanc:24.6.1
+# python CheckDicomTls.py --force OrthancNoCheckClient
+
+To run the DICOM TLS tests without Client certificate checks:
+# cd ./Tests
+# python CheckDicomTls.py --config-check-client
+# /home/alain/o/build/orthanc/Orthanc --verbose dicom-tls.json
+#### or
+# docker run -p 8042:8042 -p 4242:4242 -e ORTHANC__DICOM_TLS_ENABLED=true -e ORTHANC__DICOM_TLS_CERTIFICATE=/certs/dicom-tls-a.crt -e ORTHANC__DICOM_TLS_PRIVATE_KEY=/certs/dicom-tls-a.key -e ORTHANC__DICOM_TLS_REMOTE_CERTIFICATE_REQUIRED=true -e ORTHANC__DICOM_TLS_TRUSTED_CERTIFICATES=/certs/dicom-tls-trusted.crt -e ORTHANC__EXECUTE_LUA_ENABLED=true -v .:/certs/ -e ORTHANC__AUTHENTICATION_ENABLED=false -e VERBOSE_ENABLED=true orthancteam/orthanc:24.6.1
+# python CheckDicomTls.py --force OrthancCheckClient
+
+
 
 (Option 2b) With Docker:
 
--- a/Tests/CheckDicomTls.py	Wed Jun 05 17:53:36 2024 +0200
+++ b/Tests/CheckDicomTls.py	Mon Jun 17 18:25:18 2024 +0200
@@ -61,7 +61,9 @@
                     help = 'Password to the REST API')
 parser.add_argument('--force', help = 'Do not warn the user',
                     action = 'store_true')
-parser.add_argument('--config', help = 'Create the configuration files for this test in the current folder',
+parser.add_argument('--config-no-check-client', help = 'Create the configuration files for the "no-check-client" tests in the current folder',
+                    action = 'store_true')
+parser.add_argument('--config-check-client', help = 'Create the configuration files for the "check-client" tests test in the current folder',
                     action = 'store_true')
 parser.add_argument('options', metavar = 'N', nargs = '*',
                     help='Arguments to Python unittest')
@@ -74,14 +76,14 @@
 ##
 
 
-if args.config:
+if args.config_no_check_client or args.config_check_client:
     def CreateCertificate(name):
         subprocess.check_call([ 'openssl', 'req', '-x509', '-nodes', '-days', '365', '-newkey', 'rsa:2048',
                                 '-keyout', '%s.key' % name,
                                 '-out', '%s.crt' % name,
                                 '-subj', '/C=BE/CN=localhost' ])
 
-    print('Writing configuration to folder: %s' % args.config)
+    print('Writing configuration for the %s tests to current folder' % ('no-check-client' if args.config_no_check_client else 'check-client'))
     CreateCertificate('dicom-tls-a')
     CreateCertificate('dicom-tls-b')
     CreateCertificate('dicom-tls-c')  # Not trusted by Orthanc
@@ -102,7 +104,7 @@
             'RegisteredUsers' : {
                 'alice' : 'orthanctest'
             },
-            'DicomTlsRemoteCertificateRequired' : False,  # New in Orthanc 1.9.3
+            'DicomTlsRemoteCertificateRequired' : args.config_check_client,  # New in Orthanc 1.9.3
         }))
 
     exit(0)
@@ -136,7 +138,8 @@
 FNULL = open(os.devnull, 'w')  # Emulates "subprocess.DEVNULL" on Python 2.7
 
     
-class Orthanc(unittest.TestCase):
+# in these tests, Orthanc does not check client certificates
+class OrthancNoCheckClient(unittest.TestCase):
     def setUp(self):
         if (sys.version_info >= (3, 0)):
             # Remove annoying warnings about unclosed socket in Python 3
@@ -147,7 +150,7 @@
 
         
     def test_incoming(self):
-        # No certificate     
+        # No client certificate provided and client does not check server cert -> raise
         self.assertRaises(Exception, lambda: subprocess.check_call([
             FindExecutable('echoscu'),
             ORTHANC['Server'], 
@@ -155,6 +158,16 @@
             '-aec', 'ORTHANC',
         ], stderr = FNULL))
 
+        # No client certificate provided and client does check server cert -> no raise
+        self.assertRaises(Exception, lambda: subprocess.check_call([
+            FindExecutable('echoscu'),
+            ORTHANC['Server'], 
+            str(ORTHANC['DicomPort']),
+            '-aec', 'ORTHANC',
+            '+cf', 'dicom-tls-a.crt'
+        ], stderr = FNULL))
+
+        # random client certificate provided and client does check server cert -> no raise since Orthanc does not check the client cert
         subprocess.check_call([
             FindExecutable('echoscu'),
             ORTHANC['Server'], 
@@ -164,23 +177,6 @@
             '+cf', 'dicom-tls-a.crt',
         ], stderr = FNULL)
 
-        self.assertRaises(Exception, lambda: subprocess.check_call([
-            FindExecutable('echoscu'),
-            ORTHANC['Server'], 
-            str(ORTHANC['DicomPort']),
-            '-aec', 'ORTHANC',
-            '+tls', 'dicom-tls-c.key', 'dicom-tls-c.crt',  # Not trusted by Orthanc
-            '+cf', 'dicom-tls-a.crt',
-        ], stderr = FNULL))
-
-        self.assertRaises(Exception, lambda: subprocess.check_call([
-            FindExecutable('echoscu'),
-            ORTHANC['Server'], 
-            str(ORTHANC['DicomPort']),
-            '-aec', 'ORTHANC',
-            '+tls', 'dicom-tls-b.key', 'dicom-tls-b.crt',
-            '+cf', 'dicom-tls-b.crt',  # Not the certificate of Orthanc
-        ], stderr = FNULL))
 
         
     def test_outgoing_to_self(self):
@@ -218,7 +214,40 @@
             '+cf', 'dicom-tls-a.crt',
         ], stderr = FNULL)
         
+
+# in these tests, Orthanc do checks client certificates
+class OrthancCheckClient(unittest.TestCase):
+    def setUp(self):
+        if (sys.version_info >= (3, 0)):
+            # Remove annoying warnings about unclosed socket in Python 3
+            import warnings
+            warnings.simplefilter('ignore', ResourceWarning)
+
+        DropOrthanc(ORTHANC)
+
         
+    def test_check_client_incoming(self):
+        # client provides an untrusted certificate -> Orthanc will complain -> raise
+        self.assertRaises(Exception, lambda: subprocess.check_call([
+            FindExecutable('echoscu'),
+            ORTHANC['Server'], 
+            str(ORTHANC['DicomPort']),
+            '-aec', 'ORTHANC',
+            '+tls', 'dicom-tls-c.key', 'dicom-tls-c.crt',  # Not trusted by Orthanc
+            '+cf', 'dicom-tls-a.crt',
+        ], stderr = FNULL))
+
+        # client provides a trusted certificate but expects another cert from Orthanc -> raise
+        self.assertRaises(Exception, lambda: subprocess.check_call([
+            FindExecutable('echoscu'),
+            ORTHANC['Server'], 
+            str(ORTHANC['DicomPort']),
+            '-aec', 'ORTHANC',
+            '+tls', 'dicom-tls-b.key', 'dicom-tls-b.crt',
+            '+cf', 'dicom-tls-b.crt',  # Not the certificate of Orthanc
+        ], stderr = FNULL))
+
+
 try:
     print('\nStarting the tests...')
     unittest.main(argv = [ sys.argv[0] ] + args.options)