четверг, 3 ноября 2022 г.

Финансовая инфраструктура для работы по контракту

Задача - твоя контора свалила из страны (сейчас не о моральном аспекте а техническиом). Офиса в России нет но работа по контракту. Оплата в евро. Требование - банк не под санкицями и не в России.

 Вариант, который сейчас кажется пока рабочим...

- Билеты в Бишкек (туда-обратно) + гостиница на несколько дней (ожидание изготовления карты) = тысяч 30-35

- SIM карта О! + верификация в офисе + подключение кошелька О!Деньги

- BakaiBank: 4-е счёта (евро, доллар, сом, рубль) + карта Виза Стандарт (только в $ доступна)

- Регистрация и заявление счетов в налоговой


Цепочка выглядит так:

Деньги приходят на счет в евро в БакайБанк. 

Конвертируем евро в рубли через приложение Бакай24.

Используя SWIFT перевод в приложении Бакай24 отправляем в Российский банк на свой счет.

Комиссия: на пробном переводе в 40 рублей комиссия была 500 рублей

Если надо в обратную сторону:

Есть несколько вариантов:

Вариант 1:

Берем приложение Тинькофф. В нем Платежи - Мобильная связь на свой О! номер (выбирается О! Mobile). Деньги приходят в сомах на кошелек О!Деньги. Но курс ужасен! В моем примере 500 рублей превратились в 588 сомов, то есть это 1.18....

И далее через приложение О! Кошелек пополняем без комиссии счет БакайБанка в сомах. далее конвертация.


Второй:

СбербанкОнлайн -> Платежи -> В другую страну -> Киргизия -> Бакай Банк по номеру телефона.

Рубли приедут на счет БакайБанка в сомах. Комиссия и перевод в сомы (курс так себе был - 1.23) - теряем часть денег.

Далее конвертация.


Третий.

SWIFT перевод из Тинькофф на Бакай




вторник, 23 ноября 2021 г.

AWS S3 bucket: Access Denied for Administrator

 Ситуация неприятная. Пользователь крутил политики на бакете и получил ситуацию, когда доступ к бакету закончился для него абсолютно. Он попросил меня удалить бакет но .... моих прав администратора оказалось недостаточно. Ни через Веб консоль ни через CLI - никак!

Нельзя было ни удалить бакет, ни снять политику, не прочитать бакет. В консоле все до единого пункты управления имели одно и то же - Access Denied.

Ситуацию разрешил доступ Root юзера в консоль. У него есть доступ к бакетам вне зависимости от Bucket Policy. Оказалось, что пользователь установил bucket policy c правилом доступа только из подсетей офиса, и это были не маршрутизируемые адреса :))


вторник, 1 декабря 2020 г.

Terraform: Re-add the provider configuration to destroy

 Вот такого рода ошибка на terraform destroy:

Error: Provider configuration not presentTo work with module.monitoring-ec2.aws_instance.this its original provider
configuration at
module.monitoring-ec2.provider["registry.terraform.io/hashicorp/aws"] is
required, but it has been removed. This occurs when a provider configuration
is removed while objects created by that provider still exist in the state.
Re-add the provider configuration to destroy
module.monitoring-ec2.aws_instance.this, after which you can remove the
provider configuration again

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

provider "aws" { region=var.region}

И потом, после деплоя и времени автор понимает, что надо убрать в дочернем модуле секцию, которая дублируется и в родительском модуле уже все равно есть. Убирает и .... получает вот эту ситуацию.

В файле состояния это выглядит вот так:

в блоке описания состояния дочернего модуля будет вот такая строка:

 Придется вытащить к себе этот файл состояния:

terraform state pull

и заменить такие строки на стандартные, рутовые провайдеры:

provider: "provider[\"registry.terraform.io/hashicorp/aws\"]"

далее можно отправить этот файл обратно в backend (terraform state push) или же используя локальный бэкенд удалить структуру руками:

terraform destroy -state=terraform.tfstate


вторник, 22 октября 2019 г.

Terraform: Обойти ограничение интерполяции в конфигурации Backend

Нельзя использовать переменные в описании Backend Terraform. В документации об этом написано явно. Тем не менее, можно указать через файл или переменную окружения практически все требуемые параметры кроме dynamodb_table. В документации нет указания на имя переменной, из которой можно вычитать это значение. Однако есть вот такая переменная окружения, в которой можно передать и это значение. Вот полный пример инициализации:

export TF_CLI_ARGS_init='-backend-config="key=main.tf" -backend-config="dynamodb_table=terraform-state-lock-staging-us-east-1" -backend-config="region=us-east-1" -backend-config="bucket=terraform-state-staging-us-east-1"'

далее запуск:
terraform init



пятница, 27 сентября 2019 г.

AWK power: Файл с параметрами: если есть параметр то меняем его значение на новое, если такого параметра еще нет - добавим

Прям порой надо такие штуки делать и время уходит не мало что бы отработать такой скрипт.
Есть файл с параметрами в виде пар NAME=VALUE.
Задача - если в этом файле уже есть определенные переменные с какими то значениями, то нужно им присвоить новые значения, а если таких переменных еще в файле нет - добавить их с уже новыми значениями.
Например, был такой файл с параметрами:
cat app.tfvars
appName = "app1"
appRepo = "ssh:git-repo@:name"
appBranch = "master"

Нужно при очередном прогоне заменять всегда ТРИ переменные
 - appRepo
 - appBranch
 - appUser
 (в примере только две нужные переменные уже есть , а третьей, appUser, еще нет).

Вот эти новые значения, которые мы как то получили в процессе (jenkins Job например их выставил)
--------------------------
APP_REPO="ssh:GIT"
APP_BRANCH="production"
APP_USER="kino505"
-------------------------
Хочу в один проход по файлу сделать две задачи сразу - заменить если есть такая переменная и добавить, если еще нет.

awk -v appRepo=${APP_REPO} -v appBranch=${APP_BRANCH} -v appUser=${APP_USER} '
    BEGIN {FS = OFS = "=";
    envVars["appRepo"]=appRepo;
    envVars["appBranch"]=appBranch;
    envVars["appUser"]=appUser;}
    {gsub(/ /,"",$1)1; gsub(/ |\"/,"",$2)1} $1 in envVars {$2 = envVars[$1];delete envVars[$1]}
    {print $1" "," \""$2"\""}
    END { for(i in envVars) print i" "," \""envVars[i]"\"" }
' app.tfvars > tmp.file && mv tmp.file app.tfvars

После отработки итоговый файл:

appName = "app1" <-- p="" unchanged="">appRepo = "ssh:GIT" <--- p="" replaced="">appBranch = "production" <--- p="" replaced="">appUser = "kino505"  <---- added="" because="" found="" not="" p="" string="">
PS: но было бы здорово если бы получилось бы передать ассоциативный массив в виде параметров в виде имя-значение (list of maps) в AWK. У меня не получилось. Может кто знает как?


пятница, 23 августа 2019 г.

Terraform. Creating dynamic VPC

Задача - создавать выделенные окружения ECS для каждого приложения. Выделенные имеется ввиду что каждое приложение это полностью свою структура на базе выделенной VPC и иже с ней. И что самое забавное, адреса VPC должны быть уникальны, то есть не допускается перекрытие VPC CIDR.

Не сложно, казалось бы сделать первую часть, просто используя terraform workspace. А вот со второй задачей было интересно.

Алгоритм я кодил такой - делаем запрос в регион по тэгам на наличие каких-то уже живых VPC и если они есть - получаю их cidr_block. Рядом делаю массив с помощью утилитки prips, который содержит все возможные VPC CIDR для моих приложений. далее удаляю из этого массива со всеми возможными CIDR те, что уже заняты (созданные ранее, живые) и первый минимальный CIDR - мой кандидат. Чуть более подробнее:
у меня выбрался один большой блок для DEV-env = 10.69.64.0/19
для экономии адресов было решено, что достаточно будет иметь для каждого приложения VPC CIDR класса /26, что дает возможность (AWS тут ограничивает минимальным /28) создать до 4-х подсетей в такой vpc класса /28), а это либо пара пар private-public, либо до 4-х private или public subnets. так же этот алгоритм позволяет переиспользовать освободившиеся VPC CIDR а не расти вверх до упора.

Идеи под реализации были такие:
1. использовать null_resource + local_file. То есть вызывается null_resource, который запускает шел-код, которому передаются переменные окружения из текущего состояния Terraform (CIDR, WORKSPACE и так далее). далее на выходе этого скрипта - первый свободный CIDR из нашего большого 10.69.64.0/19. И все было бы хорошо, но я не смог переубедить терраформ не убивать каждый раз то что он создал. Это потому что null_resource + local_file - это динамические объекты и они каждый раз при новом запуске не известны. Ни какие триггеры не помогли мне в этом.

вариант 2 оказался рабочий и даже мне понравился больше
2. Я нашел, что в 12-м терраформе есть data-source тип external (https://www.terraform.io/docs/providers/external/data_source.html). Вот на его базе это и получилось реализовать так как мне нужно было для решения задачи. Я покажу ключевые элементы что бы было понятно:
variable.tf:
-------------------------------------
data "external" "app-cidr" {
  program = ["${path.module}/getFirstFreeVpcCidr.sh"]
  query = {
    PRIPS         = "${path.module}/prips"
    VPC_CIDR_FULL = "${lookup(var.vpc-cidr, var.env)}"
    APP_CIDR_MASK = "${lookup(var.app-vpc-mask, var.env)}"
    REGION        = "${var.region}"
    STAGE_ENV     = "${var.env}"
    WORKSPACE_NAME = terraform.workspace
  }
}
--------------------------------------
здесь я указываю где лежит скрипт, который реализует логику. Тут особенность этого data-source в том, что ему на вход нужен JSON и выдавать он должен JSON. Переменные окружения передаем в блоке query {} а в скрипте эти переменные придется преобразовать
Вот весь скрипт:
-------------------------
#!/bin/bash
set -e
eval "$(jq -r '@sh "PRIPS=\(.PRIPS) VPC_CIDR_FULL=\(.VPC_CIDR_FULL) APP_CIDR_MASK=\(.APP_CIDR_MASK) REGION=\(.REGION) STAGE_ENV=\(.STAGE_ENV) WORKSPACE_NAME=\(.WORKSPACE_NAME)"')"

[[ -z "${PRIPS}" ]] && exit 1
[[ -z "${VPC_CIDR_FULL}" ]] && exit 1
[[ -z "${APP_CIDR_MASK}" ]] && exit 1
[[ -z "${REGION}" ]] && exit 1
[[ -z "${STAGE_ENV}" ]] && exit 1

declare -a VPC_CIDR_CURR
## check for exists vpc. If exists return current CIDR-block
VPC_CIDR_CURR=($(aws ec2 describe-vpcs --filters "Name=tag-key,Values=Environment" "Name=tag-value,Values=${WORKSPACE_NAME}-vpc" --region=${REGION} --query "Vpcs[*].CidrBlock" --output text))

if [ -z ${VPC_CIDR_CURR} ]; then

  VPC_CIDR_CURR=($(aws ec2 describe-vpcs --filters "Name=tag-key,Values=Environment" "Name=tag-value,Values=*-${STAGE_ENV}-vpc" --region=${REGION} --query "Vpcs[*].CidrBlock" --output text))

  #for test purpose: add element to array
  #VPC_CIDR_CURR=("${VPC_CIDR_CURR[@]}" "10.69.64.0/27")

  declare -a ALL_AVAIL_SUBNETS
  ### prips -i
  ### The offset is a value of adresses into app-vpc
  APP_CIDR="$( echo ${VPC_CIDR_FULL}|cut -d/ -f-1 )${APP_CIDR_MASK}"
  COUNT_IPS_INTO_APP_CIDR=$( ${PRIPS} ${APP_CIDR}|wc -l )
  #echo "Count - $COUNT_IPS_INTO_APP_CIDR"
  ALL_AVAIL_SUBNETS=($( ${PRIPS} -i ${COUNT_IPS_INTO_APP_CIDR} ${VPC_CIDR_FULL} ))

  for i in ${VPC_CIDR_CURR[@]}
  do
    l=$(echo $i|cut -d/ -f-1)
    ALL_AVAIL_SUBNETS=( ${ALL_AVAIL_SUBNETS[@]/$l/} )
  done

  FIRST_FREE_CIDR=${ALL_AVAIL_SUBNETS[@]:0:1}
  APP_CIDR="${FIRST_FREE_CIDR}${APP_CIDR_MASK}"
else
  APP_CIDR=${VPC_CIDR_CURR}
fi

echo "${APP_CIDR}" >> $LOG
jq -n --arg APP_CIDR "${APP_CIDR}" '{"app-cidr":$APP_CIDR}'
---------------------------------

Здесь я получаю переменные окружения из query{} блока. Преобразую их в башевские для работы. далее проверяю, есть ли уже vpc с таким именем как текущий workspace. Если есть, то выдаю его CIDR. Именно это не дает терраформу убить то что было создано уже.
Если такой vpc еще нет, то вычисляем первый свободный блок и отдаем его JSON-м.

теперь терраформ знает нужный ему CIDR. У меня модульный сценарий и есть другие, заранее оговоренные CIDR для предопределенных окружений (dev/stage/prod). Что бы оставить их рабочими я добавляю наш только что вычисленный CIDR в переменную типа MAP:

variable "vpc-cidr" {
  type    = map
  default = {
    dev              = "10.69.0.0/19"
    staging          = "10.69.64.0/19"
    prod             = "10.69.128.0/19"
 }
}


locals {
  vpc-cidr = merge(var.vpc-cidr,{"${terraform.workspace}" = "${data.external.app-cidr.result.app-cidr}"})
  vpcCidr = lookup(local.vpc-cidr, terraform.workspace)
}

## UPDATE after release 0.12.7 (fixed lookup error)
locals {
  vpcCidr = lookup(var.vpc-cidr, terraform.workspace, data.external.app-cidr.result.app-cidr)
}

и далее в модуле уже применяю как обычно:
resource "aws_vpc" "vpc" {
  cidr_block           = local.vpcCidr
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name        = "${terraform.workspace}-vpc"
    Environment = terraform.workspace
  }
}

четверг, 6 июня 2019 г.

Haproxy/Nginx. Dynamic DNS resolution of backends

Сделана связка Spotinst + Route53 Integration. Идея простая - изменился состав или количество Backend Spot EG, это отразилось в записи Route53, например prod-event.prod-us.local.
Теперь надо сделать для Haproxy DNS resolve для бэков:
Новая секция в haproxy.cfg:

resolvers aws
  parse-resolv-conf
  resolve_retries       3
  timeout resolve       1s
  timeout retry         1s
  hold timeout         10s
  hold valid           10s
  hold obsolete        10s


Очень важно добавить в секцию default:

 default-server          init-addr last,libc,none

Иначе при старте ничего не отрезолвится и будет всегда висеть 0 Backends

И секция для бэков такая например:

backend events
    server-template event 1-3 prod-event.prod-us.local:80 check resolvers aws

ну и hatop покажет список из 3-х серверов, те что живы будут UP.

Для Nginx - кусок из конфига с опциями по теме:
server {
    resolver 127.0.0.1 valid=10s;

    location ~* /(event|callback|rejection) {
      set $backend_servers prod-event.prod-us.local;
      proxy_pass http://$backend_servers:80;
    }