среда, 30 сентября 2015 г.

Oracle Database: Disaster Recovery

Введение

Прошедшие 20 лет показали, что базы данных Oracle - вещь весьма надежная и, с использованием адекватных систем хранения (правильно построенных и администрируемых) крайне редко выходят из строя.

Однако такие факторы, как системные администраторы/администраторы баз данных - то есть человеческий фактор - никуда не делись и по-прежнему представляют собой угрозу.

Кроме того, один из самых разрушительных факторов - пожар - все еще является серьезной угрозой.

По моей личной статистике, наиболее частыми катастрофическими факторами для БД являются:

  1. Пожары, связанные с электросистемами
  2. Человеческий фактор
  3. Отказы дисковых систем, в том числе катастрофические - связанные как с предсказуемыми или непредсказуемыми отказами оборудования (отказ кондиционера в серверной - наиболее частая причина массового отказа дисков в массивах), ошибки системных администраторов (ошибочное удаление всей или части БД) с привилегированным доступом, множественный отказ дисков, сопряженный с недостаточно тщательным конфигурированием дисковой системы.
Все три случая довольно часто приводят к тому, что принято называть disaster.

Как защитить БД от катастрофических последствий и как восстановить ее в случае фатального повреждения или уничтожения?

Backup 

Для успешного восстановления в подобных случаях backup settings должны быть правильно заданы и база должна регулярно резервироваться.

Итак, определим задачу.

Допустим, что у нас есть круглосуточно работающая БД в режиме non-stop, которая в идеале работает в режиме 24x7 с минимальным допустимым временем простоя, достаточно большая, чтобы создать проблему простоя при холодном резервировании.

То есть мы не можем делать полные холодные бэкапы. 

Допустим также, что мы получили эту базу в наследство, в работающем состоянии. Архивация логов (ARCHIVELOG) включена, логи собираются, но бэкапы не делались или делались нерегулярно. 

Допустим, что нам надо предпринять все меры по защите от дизастера безотносительно версии БД.

Наша задача - определить подходящий график резервирования, задать его, обеспечить минимальное время восстановления и максимальную его полноту даже в случае внезапного и полного уничтожения основного сервера БД.

Следует выполнить некоторые подготовительные шаги:

  1. Необходимо определить точки монтирования и пути всех основных компонентов БД: файлов данных, redo logs, control files, и архивных логов. Эту информацию необходимо сохранить вне сервера БД и, желательно, вообще вне компьютера. Необходимо обеспечить сохранность этой информации и ее своевременную актуализацию.
  2. Необходимо сохранить конфигурационные файлы сетевой подсистемы (tnsnames.ora и listener.ora) вне сервера БД и также обеспечить их сохранность.
  3. Необходимо сохранить текущий файл параметров (pfile) в текстовом виде вне сервера БД.
  4. Необходимо определить и сохранить вне сервера БД идентификатор базы данных - DBID.
После этого можно приступать к определению стратегии резервирования, к ее детализации и к реализации выбранной стратегии. Стратегия выбирается по многим критериям, детали мы опустим, отмечу лишь, что факторами выбора является критичность БД для бизнеса, допустимая величина потерь, время простоя при восстановлении в случае отказа, и некоторые другие факторы.

В большинстве случаев необходимо определить точку монтирования, в которую будет происходить резервное копирование - обычно это место находится на сервере БД и оттуда происходит перенос резервных копий на ленту (как минимум ежедневный), туда же включается одна из точек назначения архивных логов, и автоматические бэкапы control files, включающие в себя файл параметров. Автобэкапы выполняются каждый раз при существенном изменении структуры БД и/или модификации файла параметров (spfile). 

Все эти данные необходимо переносить на ленту и обеспечить хранение этих данных вне серверной и (второй экземпляр) вообще вне офиса.

Вводная: в нашем гипотетическом случае БД работает круглосуточно, причем днем выполняется текущая активность - в том числе транзакционная - а в нерабочее время идут пакетные DML-задания и бОльшая часть отчетности. График основной активности (интерактивной) - 8x5. Остальная часть задач выполняется в течение оставшейся части суток. Раз в месяц идут крупные группы тяжелых отчетов.

Так как в данном случае остановка базы для выполнения холодных бэкапов не допускается,  и, допустим, что объем базы составляет 1 Тб, определим следующий график резервного копирования:

  1. Раз в неделю, в субботу в полночь, выполняется полный онлайновый бэкап нулевого уровня, являющийся основой для последующих инкрементальных бэкапов.
  2. В остальные дни недели, в полночь, выполняется создание инкрементальных бэкапов.
  3. Бэкапы выполняются с компрессией как backupsets, с включением в бэкап сеты архивных логов и с удалением архивных логов из точек назначения после архивирования для housekeeping.
  4. С целью уменьшения объема и ускорения создания инкрементальных бэкапов, на исходной базе активирован block change tracking file. Также в бэкап включаются и автобэкапы control-файлов.
Резервное копирование будем выполнять средствами RMAN, дабы не зависеть от работы OEM (он бывает достаточно ненадежен), заскриптуем задания и будем выполнять посредством cron по необходимому нам расписанию.

Напишем простой скрипт для резервного копирования:
 #!/usr/bin/sh  
 #  
 # Oracle RMAN backup script  
 # By Y.Voinov (c) 2014,2015  
 #  
   
 # Edit this variables according your installation  
 #ORACLE_BASE=/opt/oracle  
 ORACLE_BASE=/oracle  
 #ORACLE_HOME=$ORACLE_BASE/product/11.2.0/dbhome_1  
 ORACLE_HOME=$ORACLE_BASE/ora_home10g  
   
 PATH=$PATH:$ORACLE_HOME/bin  
 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib  
   
 export ORACLE_BASE ORACLE_HOME LD_LIBRARY_PATH PATH  
   
 # arg1 - backup type. 0 - full, 1 - incremental  
 backup_type=$1  
 # arg2 - Oracle SID  
 ORACLE_SID=$2  
 # arg3 - backup path. Must starts with / and without trailing /  
 backup_path=$3  
 # arg4 - SYS password  
 sys_pwd=$4  
   
 if [ -z "$backup_path" ]; then  
  echo "ERROR: Backup path not specified. Exiting..."  
  exit 2  
 elif [ -z "$sys_pwd" ]; then  
  echo "ERROR: SYS password is not specified. Exiting..."  
  exit 3  
 elif [ -z "$ORACLE_SID" ]; then  
  echo "ERROR: ORACLE_SID is not specified. Exiting..."  
  exit 4  
 fi  
   
 export ORACLE_SID  
   
 # Re-configure RMAN options  
 $ORACLE_HOME/bin/rman TARGET sys/$sys_pwd@$ORACLE_SID <<EOT  
 configure default device type to disk;  
 configure device type disk parallelism 1 backup type to compressed backupset;  
 configure channel device type disk format '$backup_path/db_%U_%t_%s.bk' maxpiecesize 8 G;  
 configure controlfile autobackup format for device type disk to '$backup_path/%F';  
 configure controlfile autobackup on;  
 EOT  
   
 # Do backup  
 if [ "$backup_type" = "0" ]; then  
 echo "Full DB backup level 0"  
 $ORACLE_HOME/bin/rman TARGET sys/$sys_pwd@$ORACLE_SID <<EOT  
 run{  
  backup as compressed backupset incremental level 0 cumulative tag 'Weekly_FULL_0_level_backup' database;  
  backup as compressed backupset tag 'Weekly_FULL_0_level_backup' archivelog all not backed up delete all input;  
  crosscheck backup;  
  delete noprompt expired backup;  
  }  
 EOT  
 elif [ "$backup_type" = "1" ]; then  
 echo "Incremental DB backup level 1"  
 $ORACLE_HOME/bin/rman TARGET sys/$sys_pwd@$ORACLE_SID <<EOT  
 run{  
  backup as compressed backupset incremental level 1 cumulative tag 'Daily_INC_1_level_backup' database;  
  backup as compressed backupset tag 'Daily_INC_1_level_backup' archivelog all not backed up delete all input;  
  crosscheck backup;  
  delete noprompt expired backup;  
  }  
 EOT  
 else  
  echo "ERROR: First argument must be 0 (full backup) or 1 (incremental). Exiting..."  
  exit 1  
 fi  
   
 unset ORACLE_BASE ORACLE_HOME ORACLE_SID LD_LIBRARY_PATH  
   
 exit 0  
 ########  

Скрипт написан с учетом необходимости housekeeping. Единственным его недостатком является тот факт, что пароль SYS задается из командной строки и на некоторых ОС может быть виден в выводе команды ps. В таком случае можно его модифицировать и задать пароль в теле скрипта, обеспечив необходимые права доступа с целью избежать утечки.

Напоминаю: в целях обеспечения сохранности бэкапов в случае дизастера, необходимо обеспечить как минимум ежедневный перенос бэкапов и их частей на ленту и надежное хранение этих лент вне серверной и вообще вне офиса.

Для гарантированного восстановления нам необходимо хранить все данные резервирования за 14 суток (не менее 2х бэкапов нулевого уровня плюс все инкрементальные между ними) + 1 день.

Таким образом, в данных бэкапов будут: файловые бэкапы, архивные логи, автобэкапы control-файлов и файла параметров. Также в бэкап стоит включить и хотя бы одну копию текущих архивных логов - в нашем случае они будут в интервале 1 суток.

Безусловно, необходимо обеспечить защиту redo - файлов (зеркалирование) на исходной БД. Однако, поскольку мы рассматриваем случай disaster recovery, считайте, что их у вас уже нет и не будет. То есть мы в подобной ситуации заранее миримся с потерей текущей транзакционной активности (в нашем случае максимум за сутки).

Restore and recovery

Для восстановления в случае фатальной катастрофы понадобится определенная последовательность действий:

  1. Необходимо иметь целевой сервер с точками монтирования либо путями, совпадающими с исходным сервером БД. (Технически это не обязательно, однако усложнит восстановление и потребуется больше времени на переименование, зачастую вручную. Обычно при фатальных отказах фактор времени критичен и, чем меньше будет ненужных движений - тем раньше вы вернете БД в строй. Поэтому следует руководствоваться двумя правилами - восстановление нужно делать как можно более простым технически с минимальным количеством шагов и второе - все операции желательно заскриптовать. Это важно в критических ситуациях - меньше вероятность непоправимых ошибок. Разумеется, предполагается, что вы выполнили хотя бы раз тестирование ваших процедур восстановления, в идеале - выполняете регулярно)
  2. Необходимо установить на него ПО, восстановить сетевую конфигурацию базы и запустить листенер.
  3. Необходимо создать файл паролей (orapwd), так как маловероятно, что вы его сохраняли регулярно с исходного сервера БД.
Как я и говорил, желательно заранее написать и подготовить скрипт для восстановления. Его можно написать в общем виде, предоставляю это в качестве самостоятельного упражнения, в качестве основы покажу специфичный для БД скрипт:

 #!/bin/sh  
   
 # Database disaster recovery script  
 # By Y.Voinov (c) 2015  
   
 # Critical parameters  
 dbid="1234567890"  
 dbbackup="/db_backup"  
 autoback="autobackups"  
 auto_maxdays="360"  
 syspwd="syspwd123456"  
   
 # Target host fs parameters  
 db_name="onyma"  
 fs_mountpoint1="/oradata/$db_name"  
 fs_mountpoint2="/oradata2/$db_name"  
 fs_mountpoint3="/redolog1/$db_name"  
 fs_mountpoint4="/redolog2/$db_name"  
 fs_mountpoint5="/oradata2/redologs3"  
   
 ### BEGIN  
   
 # Re-create dirs/mountpoints  
 mkdir -p $fs_mountpoint1  
 mkdir -p $fs_mountpoint2  
 mkdir -p $fs_mountpoint3  
 mkdir -p $fs_mountpoint4  
 mkdir -p $fs_mountpoint5  
   
 # Startup instance in nomount mode  
 sqlplus sys/$syspwd as sysdba <<EOT  
 startup nomount  
 exit  
 EOT  
   
 # Restore & recover database  
 # Scenario: Disaster recovery  
 # DBID: 1234567890  
 # Listener: Configured local (127.0.0.1)  
 # SYS's pwd: syspwd123456  
 # Password file manually created first  
 # No redo.  
 # Initial startup mode: nomount  
 rman target sys/$syspwd <<EOT  
 SET DBID=$dbid  
 SET CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '$dbbackup/$autoback/%F';  
 run{  
 allocate channel d1 type disk format '$dbbackup/db_%t_%s.bk';  
 restore controlfile from autobackup maxdays $auto_maxdays;  
 sql 'alter database mount';  
 restore database;  
 recover database;  
 release channel d1;  
 }  
 exit  
 EOT  
   
 sqlplus sys/$syspwd as sysdba <<EOT  
 alter database open resetlogs;  
 exit  
 EOT  
   
 ### END  

Все описанные в скрипте шаги можно выполнять и вручную, раздельно.

Итак, для восстановления вы выполняете следующие шаги (считаем, что предыдущие подготовительные операции вы уже выполнили):
  1. Берете файл параметров из ваших бэкапов, кладете его в $ORACLE_HOME/dbs, создаете SPFILE.
  2. Создаете файл паролей в той же директории.
  3. Подготавливаете все ваши бэкапы за 8 суток (7 дней +1), включая как минимум один нулевой в точке $dbbackup, заданной в вашем скрипте.
  4. Выполняете восстановление.
 -bash-2.05b$ sqlplus sys/XXXXXXXXXXXX as sysdba <<EOT
 > startup nomount  
 > exit  
 > EOT  
   
 SQL*Plus: Release 10.2.0.4.0 - Production on Mon Sep 21 00:37:26 2015  
   
 Copyright (c) 1982, 2007, Oracle. All Rights Reserved.  
   
 Connected to an idle instance.  
   
 SQL> ORA-32004: obsolete and/or deprecated parameter(s) specified  
 ORACLE instance started.  
   
 Total System Global Area 1.6777E+10 bytes  
 Fixed Size         2113472 bytes  
 Variable Size      1.3220E+10 bytes  
 Database Buffers     3472883712 bytes  
 Redo Buffers        81772544 bytes  
 SQL> Disconnected from Oracle Database 10g Enterprise Edition Release - 64bit Production  
 With the Partitioning, OLAP, Data Mining and Real Application Testing  
   
 -bash-2.05b$ rman target sys/XXXXXXXXXXXX <<EOT  
 > SET DBID=1234567890  
 > SET CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '/db_backup/autobackups/%F';  
 > run{  
 > allocate channel d1 type disk format '/db_backup/db_%t_%s.bk';  
 > restore controlfile from autobackup maxdays 360;  
 > sql 'alter database mount';  
 > restore database;  
 > recover database;  
 > sql 'alter database open resetlogs';  
 > release channel d1;  
 > }  
 > exit  
 > EOT  
   
 Recovery Manager: Release 10.2.0.4.0 - Production on Mon Sep 21 00:37:44 2015  
   
 Copyright (c) 1982, 2007, Oracle. All rights reserved.  
   
 connected to target database: onyma (not mounted)  
   
 RMAN>   
 executing command: SET DBID  
   
 RMAN>   
 executing command: SET CONTROLFILE AUTOBACKUP FORMAT  
 using target database control file instead of recovery catalog  
   
 RMAN> 2> 3> 4> 5> 6> 7> 8> 9>   
 allocated channel: d1  
 channel d1: sid=320 devtype=DISK  
   
 Starting restore at 21-SEP-15  
   
 channel d1: looking for autobackup on day: 20150921  
 channel d1: looking for autobackup on day: 20150920  
 channel d1: looking for autobackup on day: 20150919  
 channel d1: looking for autobackup on day: 20150918  
 channel d1: looking for autobackup on day: 20150917  
 channel d1: looking for autobackup on day: 20150916  
 channel d1: looking for autobackup on day: 20150915  
 channel d1: looking for autobackup on day: 20150914  
 channel d1: autobackup found: /db_backup/autobackups/c-2978122531-20150914-01  
 channel d1: control file restore from autobackup complete  
 output filename=/db_backup/control.bkp  
 Finished restore at 21-SEP-15  
   
 sql statement: alter database mount  
   
 Starting restore at 21-SEP-15  
   
 skipping datafile 2; already restored to file /oradata/onyma/undotbs01.dbf  
 skipping datafile 4; already restored to file /oradata/onyma/ONYMA_0.dbf  
 skipping datafile 5; already restored to file /oradata/onyma/ONYMA_1.dbf  
 skipping datafile 8; already restored to file /oradata2/onyma/ONYMA_STAT_0.dbf  
 skipping datafile 9; already restored to file /oradata2/onyma/ONYMA_STAT_1.dbf  
 skipping datafile 13; already restored to file /oradata/onyma/undotbs02.dbf  
 skipping datafile 14; already restored to file /oradata2/onyma/ONYMA_STAT_2.dbf  
 skipping datafile 15; already restored to file /oradata/onyma/ONYMA_2.dbf  
 skipping datafile 16; already restored to file /oradata2/onyma/ONYMA_STAT_3.dbf  
 skipping datafile 18; already restored to file /oradata2/onyma/ONYMA_STAT_5.dbf  
 skipping datafile 19; already restored to file /oradata/onyma/undotbs03.dbf  
 skipping datafile 28; already restored to file /oradata/onyma/ONYMA_6.dbf  
 channel d1: starting datafile backupset restore  
 channel d1: specifying datafile(s) to restore from backup set  
 restoring datafile 00001 to /oradata/onyma/system01.dbf  
 restoring datafile 00003 to /oradata/onyma/sysaux01.dbf  
 restoring datafile 00006 to /oradata/onyma/ONYMA_3.dbf  
 restoring datafile 00007 to /oradata/onyma/ONYMA_4.dbf  
 restoring datafile 00010 to /oradata/onyma/users01.dbf  
 restoring datafile 00011 to /oradata/onyma/xdb01.dbf  
 restoring datafile 00012 to /oradata/onyma/AP.dbf  
 restoring datafile 00017 to /oradata2/onyma/ONYMA_STAT_4.dbf  
 restoring datafile 00020 to /oradata2/onyma/ONYMA_ANALYS_2.dbf  
 restoring datafile 00021 to /oradata2/onyma/ONYMA_STAT_6.dbf  
 restoring datafile 00022 to /oradata/onyma/ONYMA_5.dbf  
 restoring datafile 00023 to /oradata2/onyma/ONYMA_STAT_7.dbf  
 restoring datafile 00024 to /oradata2/onyma/ONYMA_STAT_8.dbf  
 restoring datafile 00025 to /oradata2/onyma/ONYMA_STAT_9.dbf  
 restoring datafile 00026 to /oradata2/onyma/ONYMA_ANALYS_1.dbf  
 restoring datafile 00027 to /oradata2/onyma/ONYMA_ANALYS_0.dbf  
 restoring datafile 00029 to /oradata/onyma/drsys01.dbf  
 restoring datafile 00030 to /oradata2/onyma/ONYMA_STAT_10.dbf  
 restoring datafile 00031 to /oradata2/onyma/ONYMA_STAT_11.dbf  
 channel d1: reading from backup piece /db_backup/onyma/ukqh0gvg_1_1  
 channel d1: restored backup piece 1  
 piece handle=/db_backup/onyma/ukqh0gvg_1_1 tag=BACKUP_ONYMA_FULL__091215100002  
 channel d1: restore complete, elapsed time: 10:45:20  
 Finished restore at 21-SEP-15  
   
 Starting recover at 21-SEP-15  
 channel d1: starting incremental datafile backupset restore  
 channel d1: specifying datafile(s) to restore from backup set  
 destination for restore of datafile 00001: /oradata/onyma/system01.dbf  
 destination for restore of datafile 00002: /oradata/onyma/undotbs01.dbf  
 destination for restore of datafile 00003: /oradata/onyma/sysaux01.dbf  
 destination for restore of datafile 00004: /oradata/onyma/ONYMA_0.dbf  
 destination for restore of datafile 00005: /oradata/onyma/ONYMA_1.dbf  
 destination for restore of datafile 00006: /oradata/onyma/ONYMA_3.dbf  
 destination for restore of datafile 00007: /oradata/onyma/ONYMA_4.dbf  
 destination for restore of datafile 00008: /oradata2/onyma/ONYMA_STAT_0.dbf  
 destination for restore of datafile 00009: /oradata2/onyma/ONYMA_STAT_1.dbf  
 destination for restore of datafile 00010: /oradata/onyma/users01.dbf  
 destination for restore of datafile 00011: /oradata/onyma/xdb01.dbf  
 destination for restore of datafile 00012: /oradata/onyma/AP.dbf  
 destination for restore of datafile 00013: /oradata/onyma/undotbs02.dbf  
 destination for restore of datafile 00014: /oradata2/onyma/ONYMA_STAT_2.dbf  
 destination for restore of datafile 00015: /oradata/onyma/ONYMA_2.dbf  
 destination for restore of datafile 00016: /oradata2/onyma/ONYMA_STAT_3.dbf  
 destination for restore of datafile 00017: /oradata2/onyma/ONYMA_STAT_4.dbf  
 destination for restore of datafile 00018: /oradata2/onyma/ONYMA_STAT_5.dbf  
 destination for restore of datafile 00019: /oradata/onyma/undotbs03.dbf  
 destination for restore of datafile 00020: /oradata2/onyma/ONYMA_ANALYS_2.dbf  
 destination for restore of datafile 00021: /oradata2/onyma/ONYMA_STAT_6.dbf  
 destination for restore of datafile 00022: /oradata/onyma/ONYMA_5.dbf  
 destination for restore of datafile 00023: /oradata2/onyma/ONYMA_STAT_7.dbf  
 destination for restore of datafile 00024: /oradata2/onyma/ONYMA_STAT_8.dbf  
 destination for restore of datafile 00025: /oradata2/onyma/ONYMA_STAT_9.dbf  
 destination for restore of datafile 00026: /oradata2/onyma/ONYMA_ANALYS_1.dbf  
 destination for restore of datafile 00027: /oradata2/onyma/ONYMA_ANALYS_0.dbf  
 destination for restore of datafile 00028: /oradata/onyma/ONYMA_6.dbf  
 destination for restore of datafile 00029: /oradata/onyma/drsys01.dbf  
 destination for restore of datafile 00030: /oradata2/onyma/ONYMA_STAT_10.dbf  
 destination for restore of datafile 00031: /oradata2/onyma/ONYMA_STAT_11.dbf  
 channel d1: reading from backup piece /db_backup/onyma/usqh5imh_1_1  
 channel d1: restored backup piece 1  
 piece handle=/db_backup/onyma/usqh5imh_1_1 tag=BACKUP_ONYMA_INC_1_091415080002  
 channel d1: restore complete, elapsed time: 00:34:27  
   
 starting media recovery  
   
 channel d1: starting archive log restore to default destination  
 channel d1: restoring archive log  
 archive log thread=1 sequence=40431  
 channel d1: reading from backup piece /db_backup/onyma/uuqh5jm0_1_1  
 channel d1: restored backup piece 1  
 piece handle=/db_backup/onyma/uuqh5jm0_1_1 tag=BACKUP_ONYMA_INC_1_091415080002  
 channel d1: restore complete, elapsed time: 00:41:27  
 archive log filename=/db_backup/arch/1_40431_689284067.dbf thread=1 sequence=40431  
 unable to find archive log  
 archive log thread=1 sequence=40432  
 released channel: d1  
 RMAN-00571: ===========================================================  
 RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============  
 RMAN-00571: ===========================================================  
 RMAN-03002: failure of recover command at 09/21/2015 12:39:25  
 RMAN-06054: media recovery requesting unknown log: thread 1 seq 40432 lowscn 11839023228093  
   
 RMAN>   
   
 Recovery Manager complete.  
 -bash-2.05b$ pwd  
 /opt/oracle  
 -bash-2.05b$ cd /redolog1  
 -bash-2.05b$ ls -al  
 total 8  
 drwxr-xr-x  4 oracle  oinstall    256 Sep 19 14:57 .  
 drwxr-xr-x 38 oracle  oinstall    4096 Sep 19 01:51 ..  
 drwxr-xr-x  2 root   system     256 Sep 10 14:53 lost+found  
 drwxr-xr-x  2 oracle  oinstall    256 Sep 19 15:53 onyma  
 -bash-2.05b$ cd onyma  
 -bash-2.05b$ ls -al  
 total 0  
 drwxr-xr-x  2 oracle  oinstall    256 Sep 19 15:53 .  
 drwxr-xr-x  4 oracle  oinstall    256 Sep 19 14:57 ..  
 -bash-2.05b$ rman target sys/XXXXXXXXXXXX  
   
 Recovery Manager: Release 10.2.0.4.0 - Production on Mon Sep 21 12:43:47 2015  
   
 Copyright (c) 1982, 2007, Oracle. All rights reserved.  
   
 connected to target database: ONYMA (DBID=1234567890, not open)  
   
 RMAN>   
   
 Recovery Manager complete.  
 -bash-2.05b$ sqlplus sys/XXXXXXXXXXXX as sysdba  
   
 SQL*Plus: Release 10.2.0.4.0 - Production on Mon Sep 21 12:45:28 2015  
   
 Copyright (c) 1982, 2007, Oracle. All Rights Reserved.  
   
   
 Connected to:  
 Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Prod  
 With the Partitioning, OLAP, Data Mining and Real Application Testing   
   
 SQL> alter database open resetlogs;  
   
 Database altered.  
   
 SQL>   
   

Восстановление завершено. Убедитесь, что архивные логи пишутся и можно приступать к проверке полноты восстановления БД.

Обратите внимание на следующее: онлайновые логи будут созданы автоматически на последнем шаге, точки их расположения должны существовать. Текущая транзакционная активность, разумеется, не будет восстановлена. Глубина потерь зависит от степени активности базы в последние сутки. Контрол-файлы будут восстановлены в местах и с именами, заданными в файле параметров, который использовался при восстановлении. Кроме того, заметьте - две самые длительные процедуры - это восстановление данных с лент и restore. Собственно recovery выполняется весьма быстро. Последняя операция - открытие базы - также требует времени ввиду создания redo logs.

четверг, 3 сентября 2015 г.

Рукосуи Казахтелекома и YouTube

Честно говоря, я даже в затруднении, как именно квалифицировать то, что недавно обнаружилось.

То, что YouTube регулярно спотыкается у конечных пользователей на отдельных группах клипов или вообще блокируется по всяким ерундовым поводам - в общем, ни для кого не новость. Обвиняют чаще всего кого угодно, но никто толком не понимает, что происходит.

Я думаю, у меня есть что сказать по этому поводу. Несколько проясняющее ситуацию.

Для начала, небольшое пояснение. Ютубовский CDN устроен достаточно нетривиально. По уникальному ID видео (11-значному) - например https://www.youtube.com/watch?v=BObFtnEYcaI -  вас выводит на временные back-end сервера *.googlevideo.com, с динамически сформированным именем, территориально ближайшие к пользователю. Эти ссылки действительны несколько часов. Потом устаревают.

Сервера *.googlevideo.com получают IP-адреса из динамических пулов Google, и, вообще говоря, должны принадлежать Google.

Одновременно существует такая вещь, как GGC - Google Global Cache - у которого есть младший брат, который крупный провайдер может поставить у себя. Главным образом с целью экономии трафика.

Однако, некоторые товарищи, которые нам совсем не товарищи, пытаются контролировать, что смотреть можно, а что нельзя. И ладно бы делали это корректно. Блокируя входную точку, однозначно идентифицирующую видео - https://www.youtube.com/watch?v=BObFtnEYcaI . 

Так как эти рукосуи не в состоянии технически блокировать URL под HTTPS (заметьте, я, в отличие от РоскомПозора, не заявляю, что это невозможно. То, что вы знаете - не равнозначно тому, что существует.), они пытаются это делать средствами блокирования IP или DNS-спуфинга.

Причем делают это обычно настолько топорно, что....

Впрочем, смотрите сами.


Часть серверов YouTube в Казахстане получает IP Казахтелекома, причем 95 и 178я сеть - DHCP-пул, они используются для раздачи IP части физических лиц и никаких серверов YT, разумеется, не содержат.



Причем Казахтелеком настроил свой DPI настолько коряво, что по всему миру эти сервера YT ресолвятся в телекомовские IP.






То, что эти IP принадлежат Казахтелекому, знает весь мир.





Иногда эти адреса отвечают на попытки коннекта, однако:


Посмотреть по-прежнему ничего нельзя.


Прокси-сервер при попытке просмотра видео фиксирует TCP_ABORT.



Господа казахтелекомовцы. В руки вам насрать.

Если вы хотите блокировать ролики - делайте это по URL. Не надо всему миру пол-Ютуба банить.

Подобный полупрофессионализм выглядит позорно и подтверждает факт, что интернет в стране вы цензурируете. Но хотя бы либо не врали - потому что IP-адреса вас с головой выдают - либо направляли на чужие адреса. Не принадлежащие крупнейшему провайдеру страны. А вообще надо блокировки осуществлять точечно и грамотно. А не так, как вы это делаете. Если уж вообще это делать.

И, самое главное, не врать. От должностных лиц вранье выглядит особенно отвратительно. Не Ютуб эти проблемы создает.

PS. И, кстати говоря, господа рукосуи - блокирование - особенно выборочное - ICMPv4/ICMPv6 противоречит RFC и ломает интернет. В руки вам насрать. Вы поняли?

Squid и кэширование Yandex Maps

Это почти так же просто, как и кэширование 2gis. Дело, собственно говоря, в следующем. Яндекс Карты и 2gis устроены настолько сходно, что невольно возникает вопрос, кто у кого что спёр.

Итак.

 acl store_rewrite_list_web url_regex "/usr/local/squid/etc/url.rewrite_web"   
     
 cat /usr/local/squid/etc/url.rewrite_web:   
   
  # Yandex maps  
  vec[\d][\d]\.maps\.yandex\.net  
  lrs\.maps\.yandex\.net  
  stv\.maps\.yandex\.net   
     
 cat /usr/local/squid/etc/squid.conf:   
   
  # Storeurl rewriter   
  store_id_program /usr/local/squid/libexec/storeid_file_rewrite /usr/local/squid/etc/storeid.conf   
  store_id_children 32 startup=0 idle=1 concurrency=0   
  # Store ID access   
  store_id_access deny !GET   
  store_id_access allow store_rewrite_list   
  store_id_access allow store_rewrite_list_web   
  store_id_access allow store_rewrite_list_web_cdn   
  store_id_access deny all   
  store_id_bypass off   
     
 cat /usr/local/squid/etc/storeid.conf:   
    
  vec[\d][\d]\.maps\.yandex\.net\/tiles\?.*x=([^&]+).*&y=([^&]+).*&z=([^&]+).*&lang=([^&]+)   http://ymaps-vec.squidinternal/$1/$2/$3/$4  
  lrs\.maps\.yandex\.net\/tiles\?.*x=([^&]+).*&y=([^&]+).*&z=([^&]+).*&lang=([^&]+)  http://ymaps-lrs.squidinternal/$1/$2/$3/$4  
  stv\.maps\.yandex\.net\/images\/.*oid=([^&]+).*x=([^&]+).*&y=([^&]+).*&z=([^&]+)   http://ymaps-stv.squidinternal/$1/$2/$3/$4  
    

That's all, folks!

среда, 2 сентября 2015 г.

Squid 3: Харденинг при использовании SSL Bump

Конечно же вы, дорогие мальчики и девочки, не догадывались взглянуть, как у вас шифруется исходящее от прокси до веб-сайта, соединение, верно? Вы его просто не видите. Клиенты видят соединение от себя до прокси, видят зеленый замочек, и успокаиваются.

Однако, должен вам заметить, что, если вы специально не проверяли соединение от прокси до веб-сайта - вы можете здорово удивиться.

Не поленитесь зайти из-за прокси с включенным бампом вот сюда https://www.ssllabs.com/ssltest/viewMyClient.html . Узнаете массу нового о ваших внешних соединениях.

Прежде всего, вы можете увидеть экспортное шифрование - да-да, то самое, 56 бит. Как вы считаете, в 2015 году это нормально? Ах, вы не знали....


И даже 40-битное:

Ну а про SSLv3 я просто умолчу.

Итак.

Поднимая бампинг SSL, стоит сразу же - сразу же! - позаботиться о харденинге.

Думаю, вы уже знаете, что опции сервера в Squid задаются в sslproxy_* параметрах, а опции соединений от прокси до клиента - в http(s)_port.

Первое, что посоветовал бы вам лично я - отключить SSLv3 на обеих сторонах, совсем, и задать набор сайферов покруче умолчания. Выбрав, хотя бы, вот такие (от Мозиллы):

sslproxy_options NO_SSLv3,SINGLE_DH_USE
sslproxy_cipher EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH+aRSA+RC4:EECDH:EDH+aRSA:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS

(строка sslproxy_cipher пишется в одну строчку, вы помните, да?)

Обратите внимание - sslproxy_options разделяет опции запятыми ',' или ':'. Это не документировано (на данный момент) и совершенно не очевидно. В случае, если вы напишете опции через пробел, действовать будет только первая и сквид при запуске или проверке конфигурации даже не гавкнет на эту строку.

После перезапуска прокси и проверки клиента вы должны увидеть примерно следующее:


То есть никаких экспортных шифров, никаких SSLv3 и так далее. В идеале соединение от клиента до веб-сервера идет с использованием TLSv1.2.

Все?

Как бы не так. Если вы с клиента посмотрите на замочек, то увидите надпись "Соединение зашифровано с использованием устаревших наборов шифров". Да-да, несмотря на зеленый замочек - а иногда он будет вполне себе перечеркнутый красным.

В чем дело?

Дело в том, что по умолчанию, если вы явно не задали в строке http(s)_port параметров DH-обмена, используются действительно устаревшие пары, не EDH, а DH. Вы, конечно, понимаете, насколько эфемеральные DH-пары на данный момент безопаснее.

Итак, вы открываете squid.cache.documented, внимательно его читаете и видите следующее:


# dhparams= File containing DH parameters for temporary/ephemeral
# DH key exchanges. See OpenSSL documentation for details
# on how to create this file.
# WARNING: EDH ciphers will be silently disabled if this
# option is not set.



Конечно, это зависит от версии OpenSSL, но, в вашем случае, вы наверняка не утруждали себя сборкой самой последней версии из исходников и тупо установили ее из репов, не так ли? Даже не задумавшись хоть на мгновение, какую версию вы используете.

Ладно, это ваши проблемы.

В любом случае нас не устраивает тот факт, что мы по-прежнему используем устаревшую аутентификацию.

Как это исправить?

Во-первых, надо создать этот самый файл DH-параметров. Он небольшой (хотя создаваться может довольно долго):


openssl dhparam -outform PEM -out dhparam.pem 2048


После его создания его надо положить в место, доступное прокси и задать в http(s)_port строчках (тех из них, для которых включен SSL-бамп):



https_port 3129 intercept ssl-bump generate-host-certificates=on dynamic_cert_mem_cache_size=4MB cert=/usr/local/squid/etc/rootCA.crt key=/usr/local/squid/etc/rootCA.key options=NO_SSLv3 dhparams=/usr/local/squid/etc/dhparam.pem

Теперь надо реконфигурировать прокси и - вуаля! Готово.


Обратите внимание - SSLv3 отключен на обеих сторонах соединения. Старые версии клиентов и серверов будут отказывать в соединении - будьте готовы к жалобам пользователей. Пусть обновляют софт и свои предпочтения. Потому что SSLv3 в 2015м году никто не использует - как небезопасный.

К вашему большому сожалению, это не все.

Совместимость

Во-первых, осталось достаточно большое количество сайтов, в цепочке сертификатов которых исторически остались подписанные SHA-1. Вы будете их видеть. Как правило. И пользователи будут видеть. Особенно пользователи Хрома. Он гавкает на сколько-нибудь небезопасное в TLS.

Во-вторых, ваш прокси будет посылать пользователей подальше на весьма многих сайтах. Или не отображать часть контента. Начиная с mail.ru и java.com. Ввиду несовместимости набора сайферов. Да, там тоже сидят рукосуи. И там посейчас используется старье.

Вам придется добавить набор сайферов HIGH в вашу конфигурацию. Вот так:



sslproxy_cipher EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH+aRSA+RC4:EECDH:EDH+aRSA:HIGH:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS

Все остальное оставляете без изменений.

Не забудьте, что именно добавили. Со временем это придется убирать.

Вот теперь действительно все.

PS. Я остаюсь при своем мнении, что HTTPS-зло. И все вышеописанное не более, чем театр безопасности.