Connect from your Mac to an Oracle Database container through TCPS (TCP/IP with SSL and TLS / Certificates) connection instead of TCP.
QuickStart: The fastest and easiest way to run an Instance of Oracle Database on Mac OS
In
my previous post, I got some questions about how to connect to Oracle Database container through TCPS instead of TCP? So please follow the below steps for the questions:
In the previous post, we already have a docker container of Oracle Database Enterprise Edition Image running on our Mac, if you are not yet, please read the previous
post first.
1) Download Oracle Instance Client for Mac includes Basic Package and SQL*Plus Package.
You should need a free Oracle account to download these package:
Extract to one folder in your Mac.
After download and extract to a folder it should look like below:
Dos-Mac:instantclient_18_1 koacervate$ ll
total 480744
-rw-r--r--@ 1 koacervate staff 1354 Feb 3 00:42 BASIC_README
-rw-rw-rw-@ 1 koacervate staff 1358 Feb 3 00:42 SQLPLUS_README
-rwxr-xr-x@ 1 koacervate staff 9888 Feb 3 00:42 adrci
drwxr-xr-x 7 koacervate staff 224 Jul 30 23:28 client_wallet
drwxr-xr-x 7 koacervate staff 224 Jul 27 23:23 client_wallet_backup
drwxr-xr-x 6 koacervate staff 192 Jul 27 21:17 client_wallet_bk
-rwxr-xr-x@ 1 koacervate staff 40448 Feb 3 00:42 genezi
-r-xr-xr-x@ 1 koacervate staff 342 Apr 24 2015 glogin.sql
lrwxr-xr-x@ 1 koacervate staff 20 Jul 24 21:36 libclntsh.dylib -> libclntsh.dylib.18.1
-r-xr-xr-x@ 1 koacervate staff 94966232 Oct 30 2018 libclntsh.dylib.18.1
-r-xr-xr-x@ 1 koacervate staff 4786360 Oct 30 2018 libclntshcore.dylib.18.1
-r-xr-xr-x@ 1 koacervate staff 8554544 Oct 14 2018 libnnz18.dylib
lrwxr-xr-x@ 1 koacervate staff 18 Jul 24 21:36 libocci.dylib -> libocci.dylib.18.1
-r-xr-xr-x@ 1 koacervate staff 1593632 Oct 30 2018 libocci.dylib.18.1
-rwxr-xr-x@ 1 koacervate staff 126944172 Feb 3 00:41 libociei.dylib
-r-xr-xr-x@ 1 koacervate staff 151980 Oct 29 2018 libocijdbc18.dylib
-r-xr-xr-x@ 1 koacervate staff 247244 Oct 8 2018 libons.dylib
-r-xr-xr-x@ 1 koacervate staff 100492 Oct 30 2018 liboramysql18.dylib
-rwxrwxrwx@ 1 koacervate staff 1277332 Feb 3 00:40 libsqlplus.dylib
-r-xr-xr-x@ 1 koacervate staff 1655772 Oct 30 2018 libsqlplusic.dylib
drwxr-xr-x 3 koacervate staff 96 Jul 27 22:02 log
drwxr-xr-x@ 5 koacervate staff 160 Jul 30 23:34 network
-r-xr-xr-x@ 1 koacervate staff 4161484 Dec 6 2017 ojdbc8.jar
drwxr-xr-x 7 koacervate staff 224 Jul 27 23:22 server_wallet
-rwxr-xr-x@ 1 koacervate staff 8528 Feb 3 00:42 sqlplus
-r-xr-xr-x@ 1 koacervate staff 1366460 Dec 6 2017 ucp.jar
-rwxr-xr-x@ 1 koacervate staff 150080 Feb 3 00:42 uidrvci
-rw-r--r--@ 1 koacervate staff 74263 Feb 7 2018 xstreams.jar
2) Generate wallets (certificates) for both client-side (your Mac host) and server-side (your Oracle Database container).
You need to access to your Oracle Database container and use "orapki" tool generate wallets:
Dos-Mac:~ koacervate$ docker
exec -it oracle-db /bin/bash
[oracle@0ef8c79a8c4b /]$
Create a folder to store server and create a new auto-login wallet for server-side:
[oracle@0ef8c79a8c4b /]$
mkdir -p /u01/app/oracle/product/12.2.0/dbhome_1/server_wallet
[oracle@0ef8c79a8c4b /]$
orapki wallet create -wallet
/u01/app/oracle/product/12.2.0/dbhome_1/server_wallet -auto_login -pwd
WalletPassword1
Oracle PKI Tool : Version
12.2.0.1.0
Copyright (c) 2004, 2016,
Oracle and/or its affiliates. All rights reserved.
Operation is successfully
completed.
[oracle@0ef8c79a8c4b /]$ orapki wallet add
-wallet /u01/app/oracle/product/12.2.0/dbhome_1/server_wallet -dn
"CN=`hostname`" -keysize 1024 -self_signed -validity 3650 -sign_alg
sha256 -pwd WalletPassword1
Oracle PKI Tool : Version 12.2.0.1.0
Copyright (c) 2004, 2016, Oracle and/or its
affiliates. All rights reserved.
Operation is successfully completed.
[oracle@0ef8c79a8c4b /]$ orapki wallet display
-wallet /u01/app/oracle/product/12.2.0/dbhome_1/server_wallet
Oracle PKI Tool : Version 12.2.0.1.0
Copyright (c) 2004, 2016, Oracle and/or its
affiliates. All rights reserved.
Requested Certificates:
User Certificates:
Subject:
CN=0ef8c79a8c4b
Trusted Certificates:
Subject:
CN=0ef8c79a8c4b
[oracle@0ef8c79a8c4b /]$ orapki wallet export
-wallet /u01/app/oracle/product/12.2.0/dbhome_1/server_wallet -dn
"CN=`hostname`" -cert
/u01/app/oracle/product/12.2.0/dbhome_1/server_wallet/`hostname`_server.cert
Oracle PKI Tool : Version 12.2.0.1.0
Copyright (c) 2004, 2016, Oracle and/or its
affiliates. All rights reserved.
Operation is successfully completed.
[oracle@0ef8c79a8c4b /]$ ll
/u01/app/oracle/product/12.2.0/dbhome_1/server_wallet
total 12
-rw------- 1 oracle oinstall 643 Jul
30 15:39 0ef8c79a8c4b_server.cert
-rw------- 1 oracle oinstall 2477 Jul 30 15:36
cwallet.sso
-rw------- 1 oracle oinstall 0
Jul 30 15:34 cwallet.sso.lck
-rw------- 1 oracle oinstall 2432 Jul 30 15:36
ewallet.p12
-rw------- 1 oracle oinstall 0
Jul 30 15:34 ewallet.p12.lck
Create a folder to store and create a new auto-login wallet for client-side:
[oracle@0ef8c79a8c4b /]$ mkdir
/u01/app/oracle/product/12.2.0/dbhome_1/client_wallet
[oracle@0ef8c79a8c4b /]$ orapki wallet create
-wallet /u01/app/oracle/product/12.2.0/dbhome_1/client_wallet -auto_login -pwd
WalletPassword1
Oracle PKI Tool : Version 12.2.0.1.0
Copyright (c) 2004, 2016, Oracle and/or its
affiliates. All rights reserved.
Operation is successfully completed.
[oracle@0ef8c79a8c4b /]$ orapki wallet add
-wallet /u01/app/oracle/product/12.2.0/dbhome_1/client_wallet -dn
"CN=koacervate" -keysize 1024 -self_signed -validity 3650 -sign_alg
sha256 -pwd WalletPassword1
Oracle PKI Tool : Version 12.2.0.1.0
Copyright (c) 2004, 2016, Oracle and/or its
affiliates. All rights reserved.
Operation is successfully completed.
[oracle@0ef8c79a8c4b /]$ orapki wallet display
-wallet /u01/app/oracle/product/12.2.0/dbhome_1/client_wallet
Oracle PKI Tool : Version 12.2.0.1.0
Copyright (c) 2004, 2016, Oracle and/or its
affiliates. All rights reserved.
Requested Certificates:
User Certificates:
Subject:
CN=koacervate
Trusted Certificates:
Subject:
CN=koacervate
[oracle@0ef8c79a8c4b /]$ orapki wallet export
-wallet /u01/app/oracle/product/12.2.0/dbhome_1/client_wallet -dn
"CN=koacervate" -cert /u01/app/oracle/product/12.2.0/dbhome_1/client_wallet/koacervate_client.cert
Oracle PKI Tool : Version 12.2.0.1.0
Copyright (c) 2004, 2016, Oracle and/or its
affiliates. All rights reserved.
Operation is successfully completed.
[oracle@0ef8c79a8c4b /]$ ll
/u01/app/oracle/product/12.2.0/dbhome_1/client_wallet
total 12
-rw------- 1 oracle oinstall 2461 Jul 30 16:13
cwallet.sso
-rw------- 1 oracle oinstall 0
Jul 30 16:12 cwallet.sso.lck
-rw------- 1 oracle oinstall 2416 Jul 30 16:13
ewallet.p12
-rw------- 1 oracle oinstall 0
Jul 30 16:12 ewallet.p12.lck
-rw------- 1
oracle oinstall 634 Jul 30 16:15 koacervate_client.cert
Exchange the certificates between client and server-side:
orapki wallet add -wallet
/u01/app/oracle/product/12.2.0/dbhome_1/client_wallet -trusted_cert -cert
/u01/app/oracle/product/12.2.0/dbhome_1/server_wallet/0ef8c79a8c4b_server.cert
-sign_alg sha256 -pwd WalletPassword1
Oracle PKI Tool : Version 12.2.0.1.0
Copyright (c) 2004, 2016, Oracle and/or its
affiliates. All rights reserved.
Operation is
successfully completed.
[oracle@0ef8c79a8c4b /]$ orapki wallet add
-wallet /u01/app/oracle/product/12.2.0/dbhome_1/server_wallet -trusted_cert
-cert
/u01/app/oracle/product/12.2.0/dbhome_1/client_wallet/koacervate_client.cert
-sign_alg sha256 -pwd WalletPassword1
Oracle PKI Tool : Version 12.2.0.1.0
Copyright (c) 2004, 2016, Oracle and/or its
affiliates. All rights reserved.
Operation is successfully completed.
Show the certificates after exchange to verify:
[oracle@0ef8c79a8c4b /]$ orapki wallet display
-wallet /u01/app/oracle/product/12.2.0/dbhome_1/client_wallet
Oracle PKI Tool : Version 12.2.0.1.0
Copyright (c) 2004, 2016, Oracle and/or its
affiliates. All rights reserved.
Requested Certificates:
User Certificates:
Subject:
CN=koacervate
Trusted Certificates:
Subject:
CN=0ef8c79a8c4b
Subject:
CN=koacervate
[oracle@0ef8c79a8c4b /]$ orapki wallet display
-wallet /u01/app/oracle/product/12.2.0/dbhome_1/server_wallet
Oracle PKI Tool : Version 12.2.0.1.0
Copyright (c) 2004, 2016, Oracle and/or its
affiliates. All rights reserved.
Requested Certificates:
User Certificates:
Subject:
CN=0ef8c79a8c4b
Trusted Certificates:
Subject:
CN=0ef8c79a8c4b
Subject:
CN=koacervate
3) Configure listener.ora, sqlnet.ora, tnsnames.ora in Oracle Database container (server-side).
Note: In
the previous post, we mapped only port 1521/1521 from Oracle Database container to Mac host. But in this post, I want to support both TCP/1521 and TCPS/1525 so I added port 1525/1525 to "
ExposedPorts" and "
NetworkSettings", I will cover this in
another post. And if you want to support both TCP/1521 and TCPS/1525 you can try with a new Oracle Database container with this command:
Dos-Mac:instantclient_18_1 koacervate$ docker
run -d -it --name oracle-db -p 1521:1521/tcp -p 1525:1525/tcp -p
5500:5500/tcp store/oracle/database-enterprise:12.2.0.1
0ef8c79a8c4b30fca3dda74cec5bd91cf03e01518be8d43a4bc241cdd1fbbc41
The
original of those files in the container look like as below:
[oracle@0ef8c79a8c4b /]$ cat
/u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB_backup/listener.ora
LISTENER = (DESCRIPTION_LIST
= (DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 0.0.0.0)(PORT =
1521)) (ADDRESS = (PROTOCOL = IPC)(KEY =
EXTPROC1521)) ) )
DIAG_ADR_ENABLED = off
SSL_VERSION = 1.0
[oracle@0ef8c79a8c4b /]$ cat
/u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB_backup/sqlnet.ora
NAME.DIRECTORY_PATH= {TNSNAMES, EZCONNECT,
HOSTNAME}
SQLNET.EXPIRE_TIME = 10
SSL_VERSION = 1.0
[oracle@0ef8c79a8c4b /]$ cat
/u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB_backup/tnsnames.ora
ORCLCDB = (DESCRIPTION
= (ADDRESS = (PROTOCOL = TCP)(HOST = 0.0.0.0)(PORT =
1521)) (CONNECT_DATA =
(SERVER = DEDICATED) (SERVICE_NAME =
ORCLCDB.localdomain) ) )
ORCLPDB1 = (DESCRIPTION
= (ADDRESS = (PROTOCOL = TCP)(HOST = 0.0.0.0)(PORT =
1521)) (CONNECT_DATA =
(SERVER = DEDICATED) (SERVICE_NAME =
ORCLPDB1.localdomain) ) )
[oracle@0ef8c79a8c4b
/]$
and will be modified as below:
[oracle@0ef8c79a8c4b /]$ cat
/u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB/listener.ora
WALLET_LOCATION=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=/u01/app/oracle/product/12.2.0/dbhome_1/server_wallet)))
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST =
0.0.0.0)(PORT = 1521))
)
(DESCRIPTION
=
(ADDRESS = (PROTOCOL =
TCPS)(HOST = 0.0.0.0)(PORT = 1525))
)
(DESCRIPTION =
(ADDRESS = (PROTOCOL = IPC)(KEY =
EXTPROC1521))
)
)
ADR_BASE_LISTENER =
/u01/app/oracle/product/12.2.0/dbhome_1/log
DIAG_ADR_ENABLED = off
SSL_VERSION = 1.0
[oracle@0ef8c79a8c4b /]$ cat
/u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB/sqlnet.ora
SQLNET.AUTHENTICATION_SERVICES = (NTS)
WALLET_LOCATION=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=/u01/app/oracle/product/12.2.0/dbhome_1/server_wallet)))
SSL_CLIENT_AUTHENTICATION=TRUE
SSL_CIPHER_SUITES =
(SSL_RSA_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA)
SECURE_REGISTER_LISTENER = (IPC)
NAME.DIRECTORY_PATH= {TNSNAMES, EZCONNECT, HOSTNAME}
SQLNET.EXPIRE_TIME = 10
SSL_VERSION = 1.0
[oracle@0ef8c79a8c4b /]$ cat
/u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB/tnsnames.ora
ORCLCDB = (DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 0.0.0.0)(PORT =
1521)) (CONNECT_DATA =
(SERVER = DEDICATED) (SERVICE_NAME =
ORCLCDB.localdomain) ) )
ORCLCDB_SSL =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL =
TCPS)(HOST = 0.0.0.0)(PORT = 1525))
)
(CONNECT_DATA =
(SERVICE_NAME =
ORCLCDB.localdomain)))
ORCLPDB1 = (DESCRIPTION
= (ADDRESS = (PROTOCOL = TCP)(HOST = 0.0.0.0)(PORT =
1521)) (CONNECT_DATA =
(SERVER = DEDICATED) (SERVICE_NAME =
ORCLPDB1.localdomain) ) )
Now we need to restart lsnrctl service to load the changed above.
[oracle@0ef8c79a8c4b /]$ lsnrctl stop
LSNRCTL for Linux: Version 12.2.0.1.0 -
Production on 30-JUL-2019 17:02:56
Copyright (c) 1991, 2016, Oracle. All
rights reserved.
Connecting to
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=0.0.0.0)(PORT=1521)))
The command completed successfully
[oracle@0ef8c79a8c4b /]$ lsnrctl start
LSNRCTL for Linux: Version 12.2.0.1.0 - Production
on 30-JUL-2019 17:03:02
Copyright (c) 1991, 2016, Oracle. All
rights reserved.
Starting
/u01/app/oracle/product/12.2.0/dbhome_1/bin/tnslsnr: please wait...
TNSLSNR for Linux: Version 12.2.0.1.0 -
Production
System parameter file is /u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB/listener.ora
Log messages written to
/u01/app/oracle/product/12.2.0/dbhome_1/log/diag/tnslsnr/0ef8c79a8c4b/listener/alert/log.xml
Listening on:
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=0.0.0.0)(PORT=1521)))
Listening on:
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=0.0.0.0)(PORT=1525)))
Listening on:
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
Connecting to
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=0.0.0.0)(PORT=1521)))
STATUS of the LISTENER
------------------------
Alias
LISTENER
Version
TNSLSNR for Linux: Version 12.2.0.1.0 -
Production
Start Date
30-JUL-2019 17:03:02
Uptime
0 days 0 hr. 0 min. 0 sec
Trace Level
off
Security
ON: Local OS Authentication
SNMP
OFF
Listener Parameter
File /u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB/listener.ora
Listener Log File
/u01/app/oracle/product/12.2.0/dbhome_1/log/diag/tnslsnr/0ef8c79a8c4b/listener/alert/log.xml
Listening Endpoints Summary...
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=0.0.0.0)(PORT=1521)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=0.0.0.0)(PORT=1525)))
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
The listener supports no services
The command
completed successfully
It's time to verify it works or not from server-side before we continue with client-side.
[oracle@0ef8c79a8c4b /]$ tnsping ORCLCDB_SSL
TNS Ping Utility for Linux: Version 12.2.0.1.0 -
Production on 30-JUL-2019 15:57:35
Copyright (c) 1997, 2016, Oracle. All
rights reserved.
Used parameter files:
/u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB/sqlnet.ora
Used TNSNAMES adapter to resolve the alias
Attempting to contact (DESCRIPTION =
(ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCPS)(HOST = 0.0.0.0)(PORT = 1525)))
(CONNECT_DATA = (SERVICE_NAME = ORCLCDB.localdomain)))
OK (70
msec)
Nice, we have done in server-side, now we exit the container and move client_wallet (certificates) and
sqlnet.ora, tnsnames.ora to your Mac host.
[oracle@0ef8c79a8c4b /]$ exit
exit
Dos-Mac:instantclient_18_1
koacervate$
docker cp
Oracle-DB:/u01/app/oracle/product/12.2.0/dbhome_1/client_wallet
/Users/koacervate/TOOL/instantclient_18_1/
Dos-Mac:instantclient_18_1 koacervate$ docker cp
oracle-db:/u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB/sqlnet.ora
/Users/koacervate/TOOL/instantclient_18_1/network/admin/
Dos-Mac:instantclient_18_1 koacervate$ docker cp
oracle-db:/u01/app/oracle/product/12.2.0/dbhome_1/admin/ORCLCDB/tnsnames.ora
/Users/koacervate/TOOL/instantclient_18_1/network/admin/
And we will modify sqlnet.ora, tnsnames.ora for client-side as below:
Dos-Mac:instantclient_18_1 koacervate$ cat
/Users/koacervate/TOOL/instantclient_18_1/network/admin/sqlnet.ora
SQLNET.AUTHENTICATION_SERVICES = (NTS)
WALLET_LOCATION=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=/Users/koacervate/TOOL/instantclient_18_1/client_wallet)))
SSL_CLIENT_AUTHENTICATION=TRUE
SSL_CIPHER_SUITES =
(SSL_RSA_WITH_AES_256_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA)
SECURE_REGISTER_LISTENER = (IPC)
NAME.DIRECTORY_PATH= {TNSNAMES, EZCONNECT,
HOSTNAME}
SQLNET.EXPIRE_TIME = 10
SSL_VERSION =
1.0
Dos-Mac:instantclient_18_1 koacervate$ cat
/Users/koacervate/TOOL/instantclient_18_1/network/admin/tnsnames.ora
ORCLCDB = (DESCRIPTION
= (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))
(CONNECT_DATA = (SERVER =
DEDICATED) (SERVICE_NAME = ORCLCDB.localdomain
) )
ORCLCDB_SSL =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL =
TCPS)(HOST = localhost)(PORT
= 1525))
)
(CONNECT_DATA =
(SERVICE_NAME =
ORCLCDB.localdomain)))
ORCLPDB1 = (DESCRIPTION
= (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))
(CONNECT_DATA = (SERVER =
DEDICATED) (SERVICE_NAME =
ORCLPDB1.localdomain) ) )
Dos-Mac:instantclient_18_1 koacervate$
Ok, its time to verify overall of the processes above.
with SQL*Plus:
Dos-Mac:instantclient_18_1 koacervate$ ./sqlplus
system/Oradoc_db1@ORCLCDB_SSL
SQL*Plus: Release 18.0.0.0.0 Production on Wed
Jul 31 01:53:19 2019
Version 18.1.0.0.0
Copyright (c) 1982, 2018, Oracle. All
rights reserved.
Last Successful login time: Wed Jul 31 2019
01:49:47 +07:00
Connected to:
Oracle Database 12c Enterprise
Edition Release 12.2.0.1.0 - 64bit Production
SQL>
with SQL Developer ...
Error Notes:
- ERROR: ORA-12154: TNS:could not
resolve the connect identifier specified -> if you are incorrect HOST or SERVICE_NAME in
tnsnames.ora.
- ERROR: ORA-28860: Fatal SSL error -> if you
missing: SSL_CIPHER_SUITES = (SSL_RSA_WITH_AES_256_CBC_SHA,
SSL_RSA_WITH_3DES_EDE_CBC_SHA).
- ERROR: ORA-28864: SSL connection
closed gracefully ->
if you missing: "-sign_alg sha256".
References:
- https://database.edorex.ch/blog/database-connection-with-a-certificate/
- https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/how-to-setting-up-encrypted-communications-channels-in-oracle-database/
- https://oracle-base.com/articles/misc/configure-tcpip-with-ssl-and-tls-for-database-connections
- https://rbikblog.wordpress.com/2018/02/21/tcps-listener-configuration-for-oracle-database-12c/
QuickStart: The fastest and easiest way to run an Instance of Oracle Database on Mac OS