|
|
автор Chianglin Ng <chglin(at)singnet.com.sg> Об авторе: Я живу с Сингапуре - современном, многонациональном государстве, расположеннном в южной Азии. ОС Linux я использую на протяжении 2 лет. Первым моим дистрибутивом был redhat 6.2. В настоящее время дома я использую redhat 8.0. Перевод на Русский: Pukhlyakov Kirill <kirill(at)linuxfocus.org> Содержание: |
Резюме:
В заметке рассказывается о доступе к PostgreSQL через JDBC в redhat 8.0
используя расширения Sun's Java Secured Socket для обеспечения безопасного
доступа к удаленной базе данных.
Данные инструкции для redhat 8.0, но в общем применимы ко всем дистрибутивам. Прежде всего необходимо установить PostgreSQL и соответствующие JDBC драйверы если вы еще не сделали этого. В redhat 8.0, вы можете использовать команду rpm или графический инструмент для управления пакетами. Также установите Sun's JDK 1.4.1. Sun's JDK 1.4.1 распространяется с ограничениями возможностей шифрования в соответствии с экспортными положениями US. Чтобы использовать неограниченные возможности шифрования - скачайте JCE (Java Crytographic Extensions). Посетите Sun's Java website.
Я установил JDK1.4.1 в /opt и переменной JAVA_HOME дал значение местонахождения JDK. Также сразу обновил PATH. Следующие строки я добавил в файл .bash_profile.
JAVA_HOME = /opt/j2sdk1.4.1_01
PATH = /opt/j2sdk1.4.1_01/bin:$PATH
export JAVA_HOME PATH
Стандартные(ограниченые) файлы для шифрования, входящие в состав Sun JDK, были заменены файлами из JCE. Чтобы java нашла JDBC драйверы для postgres я скопировал их в /opt/j2sdk1.4.1_01/jre/lib/ext. В дистрибутиве redhat 8.0 postgres-jdbc драйверы находятся в /usr/share/pgsql.
Если вы первый раз установили postgresql - вам необходимо создать новую базу и завести пользователя. Во-первых переключитесь в su и запустите сервис postgres. Затем измените настройки администратора postgres.
su root
password:******
[root#localhost]#/etc/init.d/postgresql start
[root#localhost]# Starting postgresql service: [ OK ]
[root#localhost]# su postgres
[bash]$
Создаем нового пользователя postgres и базу данных.
[bash]$:createuser
Enter name of user to add: chianglin
Shall the new user be allowed to create databases? (y/n) y
Shall the new user be allowed to create more new users? (y/n)
y
CREATE USER
[bash]$createdb chianglin
CREATE DATABASE
Я создал нового администратора postgres в соответствии с моим пользовательским профилем в системе и новую базу данных с таким же именем. По умолчанию, когда вы запускаете psql, соединение происходит с базой соответствующей пользователю Linux. Подробнее читайте об этом в документации к postgres. Чтобы установить пароль новому пользователю - запустите psql и используйте команду ALTER USER. Наберите следующую команду
ALTER USER chianglin WITH PASSWORD 'test1234' ;
Чтобы разрешить tcp/ip соединения - отредактируйте postgresql.conf и установите опцию tcpip_socket в true. В redhat 8, этот файл расположен в /var/lib/pgsql/data. Переключитесь в root и установите опцию
tcpip_socket=true
И наконец отредактируйте файл pg_hba.conf. В нем определены компьютеры, имеющие доступ к базе данных postgres. Я добавил один компьютер - свой и установил использование пароля для него. И снова переключитесь в root, чтобы произвести необходимые изменения.
host sameuser 127.0.0.1 255.255.255.255 password
Перезапустите postgres, чтобы новые установки вступили в силу.
Теперь у вас есть функционирующая postgres, готовая обслуживать небезопасные локальные JDBC соединения. Чтобы использовать безопасные удаленные запросы необходимо провести еще некоторые работы.
Следующая диграмма показывает как это будет работать.
JDBC приложение устанавливает соединение с клиентским прокси, который будет направлять все данные через SSL соединение к удаленному серверному прокси. В свою очередь серверный прокси будет перенаправлять запросы к postgres и возвращать полученные данные JDBC приложению опять через SSL соединение. Весь этот процесс будет прозрачен для JDBC приложения.
Из диаграммы видно, что на стороне сервера необходимо сначала
получить входящие данные и переслать их локально в базу и затем
наоборот - сначала получить данные из базы и перенаправить их
клиенту. То же самое применимо к клиентскому компьютеру. Для
реализации этого будем использовать threads. Следующая диаграмма
демонстрирует это.
При SSL соединении необходима серверная аутентификация, клиентская -
по необходимости. Лично я предпочитаю использовать и клиентскую и
серверную аутентификации, это значит, что я буду создавать ключи и
сертификаты и для клиента и для сервера с помощью инструмента
предоставляемого Java JDK.. В результате получится по два хранилища
на каждой стороне - первое для хранения private ключа хоста и второе
для хранения сертификата.
Серверное хранилище создается следующим образом - private ключ и
public сертификат.
keytool -genkey -alias serverprivate -keystore
servestore -keyalg rsa -keysize 2048
Enter keystore password: storepass1
What is your first and last name?
[Unknown]: ServerMachine
What is the name of your organizational unit?
[Unknown]: ServerOrg
What is the name of your organization?
[Unknown]: ServerOrg
What is the name of your City or Locality?
[Unknown]: Singapore
What is the name of your State or Province?
[Unknown]: Singapore
What is the two-letter country code for this unit?
[Unknown]: SG
Is CN=ServerMachine, OU=ServerOrg, O=ServerOrg, L=Singapore,
ST=Singapore, C= [no]: yes
Enter key password for <serverprivate>
(RETURN if same as keystore password): prikeypass0
</serverprivate>
Обратите внимание, что пароли запрашиваются два раза - первый - для хранилища, а второй - для private ключа. После этого экспортируйте серверный сертификат в файл.
keytool -export -alias serverprivate -keystore -rfc servestore -file server.cer
Данной командой мы переместили серверный сертификат в файл server.cer. На клиентской стороне импортируйте этот файл в хранилище, где хранятся все сертификаты.
keytool -import -alias trustservercert -file server.cer -keystore clienttruststore
Предыдущая команда импортирует серверный сертификат в хранилище с названием clientruststore, если такого хранилища нет - оно будет создано и у вас попросят пароль для него.
Теперь ваша система способна установить SSL соединение с
серверной аутентификацией.
Но так как я решил использовать и клиентскую аутентификацию -
необходимо создать private/public ключи в новом клиентском хранилище,
экспортировать клиентский сертификат и импортировать его в серверное
хранилище.
После этого мы получим два хранилища на сервере - одно для хранения
private ключа и одно для сертификатов. То же самое и для клиента.
Чтобы использовать пример, рассматриваемый далее, необходимо установить одинаковые пароли для обоих хранилищ на компьютере - т.е. два хранилища на сервере должны иметь одинаковые пароли, то же самое и для клиентских хранилищ.
Подробнее об использовании keytool смотрите тут -> Sun's documentation.Мои классы будут использовать расширения Sun's Java Secured Socket. Документация по Sun JSSE доступна здесь http://java.sun.com/j2se/1.4.1/docs/guide/security/jsse/JSSERefGuide.html. Для использования ssl соединения необходимо создать экземпляр объекта SSLContext, предоставляемого JSSE. Инициализируйте объект SSLContext с необходимыми вам установками и создайте экземпляр класса Secured SocketFactory. socketfactory будет использоваться для создания ssl сокетов.
В моем случае будут созданы и клиентский и серверный классы для
построения SSL тоннеля. Так как они оба будут использовать SSL соединение -
они оба будут созданы на основе базового класса SSLConnection. Этот класс
ответственнен за SSLContext, который будет использоваться и клиентскими
и серверными прокси. И наконец, нам нужен еще класс для использования
threads. Всего получается четыре класса.
Фрагмент класса SSLConnection
/* initKeyStore method to load the keystores
which contain the private key and the trusted certificates
*/
public void initKeyStores(String key , String trust , char[]
storepass)
{
// mykey holding my own certificate and
private key, mytrust holding all the certificates that I
trust
try {
//get instances of the Sun JKS
keystore
mykey = KeyStore.getInstance("JKS" ,
"SUN");
mytrust = KeyStore.getInstance("JKS",
"SUN");
//load the keystores
mykey.load(new
FileInputStream(key) ,storepass);
mytrust.load(new FileInputStream(trust) ,storepass
);
}
catch(Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
/* initSSLContext method to obtain a SSLContext and
initialize it with the SSL protocol and data from the keystores
*/
public void initSSLContext(char[] storepass , char[] keypass)
{
try{
//get a SSLContext from Sun JSSE
ctx = SSLContext.getInstance("TLSv1" , "SunJSSE")
;
//initializes the keystores
initKeyStores(key , trust , storepass) ;
//Create the key and trust manager
factories for handing the cerficates
//in the key and trust stores
TrustManagerFactory tmf =
TrustManagerFactory.getInstance("SunX509" ,
"SunJSSE");
tmf.init(mytrust);
KeyManagerFactory kmf =
KeyManagerFactory.getInstance("SunX509" ,
"SunJSSE");
kmf.init(mykey , keypass);
//initialize the SSLContext with the data from
the keystores
ctx.init(kmf.getKeyManagers() ,
tmf.getTrustManagers() ,null) ;
}
catch(Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
Метод initSSLContext создает SSLContext из Sun JSSE.
Во время создания необходимо указать используемый протокол SSL.
Я выбрал TLS (Transport Layer Security) версии 1. После того как
SSLContext получен - инициализируем его данными из хранилищ.
Следующий фрагмент из кода класса SSLRelayServer, который будет
работать на том же компьютере, на котором работает postgres.
Его функцией будет передача входящих данных от SSL соединения и
передача их в postgres и наоборот.
Фрагмент класса SSLRelayServer
/* initSSLServerSocket method will get the
SSLContext via its super class SSLConnection. It will then
create a SSLServerSocketFactory object that will be used
to create a SSLServerSocket. */
public void initSSLServerSocket(int localport) {
try{
//get
the ssl socket factory
SSLServerSocketFactory
ssf = (getMySSLContext()).getServerSocketFactory();
//create the ssl socket
ss
= ssf.createServerSocket(localport);
((SSLServerSocket)ss).setNeedClientAuth(true);
}
catch(Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
// begin listening on SSLServerSocket and wait for incoming
client connections
public void startListen(int localport , int destport) {
System.out.println("SSLRelay server started
at " + (new Date()) + " " +
"listening
on port " + localport + " " + "relaying to
port " + destport );
while(true) {
try {
SSLSocket incoming
= (SSLSocket) ss.accept();
incoming.setSoTimeout(10*60*1000); // set 10 minutes time
out
System.out.println((new Date() ) + " connection from " +
incoming );
createHandlers(incoming,
destport); // create 2 new threads to handle the incoming
connection
}
catch(IOException e ) {
System.err.println(e);
}
}
}
Клиентский прокси RelayApp подобен SSLRelayServer.
Он наследуется из SSLConnection и использует 2 threads для
передачи данных. Разница в том, что он создает SSLSocket для
соединения с удаленным компьютером вместо SSLServerSocket для
прослушивания входящих соединений. И наконец, нам нужен класс
для обеспечения передачи данных. Он просто читает данные
из входящего потока и перенаправляет их в выходящий.
На клиенте вам необходимы следующие файлы : SSLConnection.java, RelayIntoOut.java и RelayApp.java. На сервере : SSLRelayServer.java, RelayIntoOut.java и SSLConnection.java. Поместите их в один каталог. Скомпилируйте клиентский прокси :
javac RelayApp.java
Скомпилируйте серверный прокси :
javac SSLRelayServer.java
На сервере с postgres, запустите SSLRelayServer с 6-ю аргументами :
java SSLRelayServer servestore trustclientcert storepass1 prikeypass0 2001 5432
Запустив серверный прокси, вы можете запустить и клиентский. Он требует 7 аргументов, дополнительный - это имя или IP адрес сервера, к которому обращаться :
java RelayApp clientstore trustservercert clistorepass1 cliprikeypass0 localhost 2001 5432
После того как SSL тоннель запущен - можете пользоваться JDBC приложением и работать с postgres как обычно. Весь процесс передачи данных будет прозрачным для вашего приложения. Заметка получилась достаточно большой и я не буду приводить здесь примеры JDBC приложений. В документации и postgres и sun достаточно таких примеров.
Если вы хотите все это протестировать на одном компьютере - вы это можете сделать либо поменяв порт для postgres, либо поменять порт RelayApp. Покажу вам как это сделать на втором варианте. Во-первых выйдите из RelayApp - пошлите ему сигнал [ctrl] c и таким же образом остановите SSLRelayServer прокси.
Запустите снова RelayApp следующей командой. Измените лишь номер последнего порта, теперь он 2002.
java RelayApp clientstore trustservercert clistorepass1 cliprikeypass0 localhost 2001 2002
Наилучшим приложением для тестирования будет psql. Мы будем перенаправлять весь psql трафик через наш тоннель. Запустите psql следующей командой :
psql -h localhost -p 2002
Команда перенаправляет psql соединение на порт 2002, который прослушивает RelayApp. После ввода вашего пароля postgres вы можете вводить SQL команды как обычно и тестировать SSL соединение, которое транслирует данные.
Не совсем правильно указывать пароли в командной строке, если вы не один используете компьютер. Любой пользователь вашего компьютера наберет команду ps -auxww и увидит все ваши процессы со всеми аргументами. Гораздо безопаснее хранить пароли в зашифрованном виде в другом файле и уже в java приложении брать их из этого файла. Также можно создать диалоговое окно, в котором требовать ввода паролей.
Достаточно просто использовать Sun JSSE для создания
SSL тоннеля, который будет использовать и postgres. На самом деле
любое приложение, требующее безопасного соединения, может использовать
подобный SSL тоннель и также можно сказать, что существует множество
путей добавить шифрование к вашему приложению - просто запустите ваш
любимый текстовый редактор в Linux и кодируйте. Удачи !
|
Webpages maintained by the LinuxFocus Editor team
© Chianglin Ng, FDL LinuxFocus.org |
Translation information:
|
2003-03-05, generated by lfparser version 2.31