Dockerizando o CA Siteminder

Como aproveitar a infraestrutura como código para criar um ambiente portátil e leve do CA Siteminder usando o Docker.

8
leitura mínima

Em nosso post anterior discutimos várias vantagens da conteinerização quando se trata da migração de aplicativos de identidade legados para a nuvem. Ele permite uma migração em fases que mantém sua empresa funcionando sem problemas durante todo o processo (em oposição à abordagem “lift and shift”) e também permite o atraso de decisões arquitetônicas “imutáveis”, como qual ambiente de nuvem específico (e pilhas associadas) será o alvo. Neste post, estamos passando da teoria para a prática: passando de uma visão panorâmica até a implantação de um ambiente CA Siteminder totalmente conteinerizado, usando o Docker. ‍

Abordagem de dockerização

Separando a instalação da configuração

O Siteminder tem uma presença bastante significativa, assim como outros produtos corporativos, normalmente JavaEE. A instalação pressupõe a intervenção de um humano para alcançar a configuração de última milha. Para resolver essas restrições criando imagens de forma repetível, a configuração de última milha é automatizada acessando a API Siteminder para inserir objetos de configuração e modificar os existentes conforme necessário.

Configuração baseada em API

O CA Siteminder vem com um SDK Perl e 'C' que nos permite provisionar e gerenciar a maioria dos objetos de configuração de forma programática. Por uma questão de simplicidade, decidimos usar o Perl, porque ele não requer uma etapa adicional de compilação. É importante observar que o provisionamento baseado em Ansible e todos os artefatos associados, como scripts e configuração, também estão em contêineres e, portanto, isolados do ambiente host.

Aplicativo de referência leve

Para obter um tempo de resposta rápido com testes de ponta a ponta e resolver eventuais problemas, convém usar um aplicativo que fique o mais próximo possível da pilha do servidor http. Ter um aplicativo corporativo completo (por exemplo, JavaEE) pode atrasá-lo, dada a grande área ocupada pelo ambiente associado (por exemplo, SDK, JRE etc.), além de exigir uma compilação para cada pequena alteração. Nossa escolha é desenvolver o OpenResty (um fork do Nginx), que permite total visibilidade e controle da integração do Siteminder usando a linguagem de script Lua.

Nosso sandbox CA Siteminder

Blocos de construção

Vamos criar as seguintes imagens do docker:

  • CA Directory: Para manter os objetos de configuração do Siteminder, bem como as identidades e entidades associadas
  • Servidor de políticas: hospeda os serviços de autenticação e autorização que os agentes consomem. Além disso, ele hospeda a interface administrativa para configuração e gerenciamento
  • Armazenamento de políticas: armazena detalhes específicos da autorização (por exemplo, regras)
  • Gateway de acesso: também conhecido como Secure Proxy Server (SPS), intercepta solicitações e garante que somente solicitações autenticadas e autorizadas cheguem ao aplicativo OpenResty
  • Aplicativo OpenResty: atua como um aplicativo web seguro para fornecer login único (SSO) em cima do Siteminder

Melhores práticas

O processo de instalação e configuração do Siteminder é muito trabalhoso, com muitas peças móveis. Além disso, qualquer configuração incorreta ou estado intermediário inválido do sistema que ocorra durante esse processo pode se traduzir em um sistema que não funciona com poucas chances de recuperação. Normalmente, é menos insano começar do zero do que tentar consertar uma instalação quebrada.

Em um alto nível, a forma como escolhemos abordar a criação de uma imagem docker para o Siteminder é a seguinte:

Configure um ambiente de sandbox

Implemente um ambiente Siteminder em sandbox, idealmente como uma máquina virtual (VM). Você pode usar qualquer plataforma de virtualização (dica: experimente o Oracle VirtualBox — é gratuito). Ou você pode optar por uma VM em qualquer plataforma de infraestrutura como serviço (IaaS), mas uma desvantagem potencial dessa opção é que você terá que pagar pela VM mesmo que ela não esteja ativada.

O objetivo principal dessa sandbox é obter as propriedades do instalador do produto Siteminder que contêm as preferências de configuração a serem usadas durante uma instalação sem cabeçalho. Além disso, quaisquer artefatos relevantes podem ser obtidos desse ambiente — descritores de configuração, por exemplo — e incluídos na imagem do docker. Por fim, ele funciona como uma linha de base durante o processo de criação da imagem, o que pode ser útil para depuração quando as coisas não funcionam conforme o esperado.

Três fases da criação de imagens

O procedimento do CA Siteminder inclui não apenas a instalação passiva do produto, mas também a realização de procedimentos de configuração que dependem de um serviço instalado anteriormente estar em funcionamento e atender às solicitações de serviço. Isso implicaria na necessidade de implementar um fluxo de trabalho personalizado fora do Docker, para lançar contêineres que exponham os serviços de rede exigidos pela próxima etapa de instalação. Além disso, o processo de criação de imagens tem limitações significativas (por exemplo, no que diz respeito à rede) que não se aplicam aos contêineres.

Para resolver isso, dividimos o processo de criação da imagem em três fases:

Criação de imagem de osso nu

É criada uma imagem por produto Siteminder. Durante o processo de criação da imagem, não são necessários serviços de terceiros.

Vamos detalhar o descritor Dockerfile abaixo, que mostra a criação da imagem para o servidor de políticas:


FROM centos:8
#
#
# Environment variables required for this build (do NOT change)
# -------------------------------------------------------------
#
ENV PS_ZIP=ps-12.8-sp05-linux-x86-64.zip \
    ADMINUI_PRE_REQ_ZIP=adminui-pre-req-12.8-sp05-linux-x86-64.zip \
    ADMINUI_ZIP=adminui-12.8-sp05-linux-x86-64.zip \
    SDK_ZIP=smsdk-12.8-sp05-linux-x86-64.zip \
    BASE_DIR=/opt/CA/siteminder \
    INSTALL_TEMP=/tmp/sm_temp

ENV SCRIPT_DIR=${INSTALL_TEMP}/dockertools 

#
# Creation of User, Directories and Installation of OS packages
# ----------------------------------------------------------------
RUN yum install -y which unzip rng-tools java-1.8.0 ksh openldap-clients openssh-server xauth libnsl
RUN groupadd smuser && \
    useradd smuser -g smuser
RUN mkdir -p ${BASE_DIR} && \
    chmod a+xr ${BASE_DIR} && \ 
    chown smuser:smuser ${BASE_DIR} 
RUN mkdir -p ${INSTALL_TEMP} && \
    chmod a+xr ${INSTALL_TEMP} && chown smuser:smuser ${INSTALL_TEMP} 

# Setup SSH access
# ----------------
USER smuser 

RUN mkdir /home/smuser/.ssh && \
    chmod 700 /home/smuser/.ssh && \
    ssh-keygen -A && \
    echo "ssh-rsa " >> /home/smuser/.ssh/authorized_keys && \

USER root

RUN rm -f /run/nologin && \
    mkdir /var/run/sshd && \
    ssh-keygen -A && \
    sed -i "s/^.*PasswordAuthentication.*$/PasswordAuthentication no/" /etc/ssh/sshd_config && \
    sed -i "s/^.*X11Forwarding.*$/X11Forwarding yes/" /etc/ssh/sshd_config && \
    sed -i "s/^.*X11UseLocalhost.*$/X11UseLocalhost no/" /etc/ssh/sshd_config && \
    grep "^X11UseLocalhost" /etc/ssh/sshd_config || echo "X11UseLocalhost no" >> /etc/ssh/sshd_config

# Increase entropy
# ----------------
RUN mv /dev/random /dev/random.org && \
    ln -s /dev/urandom /dev/random

# Copy packages and scripts
# -------------------------
COPY --chown=smuser:smuser install/* ${INSTALL_TEMP}/
COPY --chown=smuser:smuser ca-ps-installer.properties ${INSTALL_TEMP}/
COPY --chown=smuser:smuser prerequisite-installer.properties ${INSTALL_TEMP}/
COPY --chown=smuser:smuser smwamui-installer.properties ${INSTALL_TEMP}/
COPY --chown=smuser:smuser sdk-installer.properties ${INSTALL_TEMP}/
COPY --chown=smuser:smuser sm.registry ${INSTALL_TEMP}/
COPY --chown=smuser:smuser container-scripts/* ${SCRIPT_DIR}/
RUN chmod +x ${SCRIPT_DIR}/*.sh

USER smuser

# Install Policy Server
# -------------------------
RUN unzip ${INSTALL_TEMP}/${PS_ZIP} -d ${INSTALL_TEMP} && \
    chmod +x ${INSTALL_TEMP}/ca-ps-12.8-sp05-linux-x86-64.bin && \
    ${INSTALL_TEMP}/ca-ps-12.8-sp05-linux-x86-64.bin -i silent -f ${INSTALL_TEMP}/ca-ps-installer.properties

RUN cp ${INSTALL_TEMP}/smreg /opt/CA/siteminder/bin && \
    cp ${INSTALL_TEMP}/sm.registry /opt/CA/siteminder/registry/sm.registry

RUN echo ". /opt/CA/siteminder/ca_ps_env.ksh" >> /home/smuser/.bash_profile

# Install Administrative Interface Prerequisites
# -----------------------------------------------
RUN unzip ${INSTALL_TEMP}/${ADMINUI_PRE_REQ_ZIP} -d ${INSTALL_TEMP} && \
    chmod +x ${INSTALL_TEMP}/adminui-pre-req-12.8-sp05-linux-x86-64.bin && \
    ${INSTALL_TEMP}/adminui-pre-req-12.8-sp05-linux-x86-64.bin -i silent -f ${INSTALL_TEMP}/prerequisite-installer.properties

# Install Administrative Interface
# -----------------------------------------------
RUN unzip ${INSTALL_TEMP}/${ADMINUI_ZIP} -d ${INSTALL_TEMP} && \
    chmod +x ${INSTALL_TEMP}/ca-adminui-12.8-sp05-linux-x86-64.bin && \
    ${INSTALL_TEMP}/ca-adminui-12.8-sp05-linux-x86-64.bin -i silent -f ${INSTALL_TEMP}/smwamui-installer.properties

# Install the SDK
# -----------------------------------------------
RUN unzip ${INSTALL_TEMP}/${SDK_ZIP} -d ${INSTALL_TEMP} && \
    chmod +x ${INSTALL_TEMP}/ca-sdk-12.8-sp05-linux-x86-64.bin && \
    ${INSTALL_TEMP}/ca-sdk-12.8-sp05-linux-x86-64.bin -i silent -f ${INSTALL_TEMP}/sdk-installer.properties

# Important note: Make sure to SSH and run the setup_ps.sh script for registering the adminitrative UI with the policy server. 
#                 *This has to be performed only once*

# Define default command to start bash.
USER root 
CMD ["/usr/sbin/sshd", "-D"]

Ele executa principalmente o seguinte:

  1. Seleciona o CentOS como sistema operacional
  2. Copia as imagens do instalador do host para a imagem
  3. Cria o usuário, os diretórios e os pacotes do sistema operacional instalados do Siteminder que são necessários
  4. Configura o acesso SSH, que é necessário para que o Ansible acesse e execute a operação de configuração assim que o contêiner é girado
  5. Copia os descritores de configuração e os arquivos de script que serão executados pelo Ansible durante a próxima fase
  6. Executa os instaladores do Siteminder para o servidor de políticas, a interface administrativa e o SDK
  7. Por fim, gera o servidor SSH para o usuário do Ansible acessar na próxima fase, para realizar as etapas de pós-instalação

Os Dockerfiles restantes seguem praticamente o mesmo padrão.

Configuração de imagem

Todos os contêineres das imagens geradas são girados e configurados adequadamente usando um script Ansible.


---
  - name: Setup CA Directory
    hosts: dx
    tasks:
      - name: Execute CA Directory post installation script
        shell: nohup /tmp/cadir_temp/dockertools/setup_psdsa.sh /dev/null 2>&1 &
  - name: Run Siteminder Policy Server
    hosts: ps
    tasks:
      - name: Execute the PS start command
        shell: nohup /opt/CA/siteminder/start-all /dev/null 2>&1 &
  - name: Configure Siteminder Policy Server
    hosts: ps
    tasks:
      - name: Execute PS post-install  command
        register: out
        command: "/tmp/sm_temp/dockertools/setup_ps.sh"
      - debug:
          var: out.stdout_lines
  - name: Setup Siteminder AG
    hosts: ag
    tasks:
      - name: Execute AG configuration command
        register: out
        command: "/tmp/sp_temp/dockertools/setup_ag.sh"
      - debug:
          var: out.stdout_lines
  - name: Post installation of AG
    hosts: ps
    tasks:
      - name: Execute AG post install command
        register: out
        shell: "source /opt/CA/siteminder/ca_ps_env.ksh && perl /tmp/sm_temp/dockertools/ProxyUIPostInstall.pl"
      - debug:
          var: out.stdout_lines
  - name: Run Siteminder AdminUI
    hosts: ps
    tasks:
      - name: Launch SM AdminUI
        shell: nohup /opt/CA/siteminder/adminui/bin/standalone.sh /dev/null 2>&1 &
  - name: Stop SPS UI
    hosts: ag
    tasks:
      - name: Kill SPS UI
        register: out
        shell: sleep 40 && /opt/CA/secure-proxy/default/proxy-engine/sps-ctl stop
        ignore_errors: True
      - debug:
          var: out.stdout_lines
  - name: Start SPS UI
    hosts: ag
    tasks:
      - name: Launch SPS UI
        register: out
        shell: /opt/CA/secure-proxy/default/proxy-engine/sps-ctl start
      - debug:
          var: out.stdout_lines

Consiste principalmente na configuração do CA Directory (atuando como fonte de política e identidade), o servidor de políticas, a interface de usuário do administrador, o gateway de acesso e a interface do proxy. Tanto os scripts específicos do SiteMinder quanto os ad-hoc são executados. Os primeiros se encarregam de gerenciar o ciclo de vida do sistema de destino; iniciá-lo e interrompê-lo, por exemplo — e os segundos realizam ações pós-instalação que teriam sido realizadas por meio de uma GUI se fosse uma instalação manual.

Abaixo está um dos scripts Perl necessários para configurar o servidor de políticas:


$adminName          = 'siteminder';
$adminPwd           = 'siteminder';

$userdir_namespace  = 'LDAP:';
$userdir_server     = 'dx:7777';  # IP Address if LDAP, Data Source Name if ODBC

#
# LDAP User Directory Settings
#
$ldap_srchroot      = 'ou=Contoso,o=psdsa,C=US';
$ldap_usrlkp_start  = 'cn=';
$ldap_usrlkp_end    = ',ou=Contoso,o=psdsa,c=US';
$ldap_usrname       = '';
$ldap_usrpwd        = '';
$ldap_require_creds = 0;

#
# Host Config settings
#
$host_conf_name     = "sps-hco";
$host_conf_desc     = "Secure Proxy Server Host";

#
# Policy server host
#
$policy_svr_host    = "ps";

#                                                                              #
# End site-specific configuration                                              #
#                                                                              #

$policymgtapi = Netegrity::PolicyMgtAPI->New();
$session = $policymgtapi->CreateSession($adminName, $adminPwd);
 
die "\nFATAL: Cannot create session. Please check admin credentials\n" 
    unless ($session != undef);

clean_ps_store();

setup_ps_store();

sub setup_ps_store {

    # Create a User Directory
    print "\tCreating User Directory \'contoso-userdir\'...";

    $userdir = $session->CreateUserDir( "contoso-userdir",
                                        $userdir_namespace,
                                        $userdir_server,
                                        $odbc_queryscheme,
                                        "Contoso User Directory",
                                        $ldap_srchroot,
                                        $ldap_usrlkp_start,
                                        $ldap_usrlkp_end,
                                        $ldap_usrname,
                                        $ldap_usrpwd,
                                        0,
                                        2,
                                        10,
                                        0,
                                        $ldap_require_creds
                                        );

    if(!defined $userdir) {
        die "\nFATAL: Unable to create User Directory \'contoso-userdir\'\n";
    }

    print "done\n";


    print "\tCreating Host Configuration for SPS \'sps-hco\'...";


    $hco = $session->CreateHostConfig( $host_conf_name,
                                    $host_conf_name,
                                    1,
                                    20,
                                    2,
                                    2,
                                    60
                                    );

    if(!defined $hco) {
        die "\nFATAL: Unable to create HCO \'sps-hco\'\n";
    }

    $hco->AddServer($policy_svr_host, 44441, 44442, 44443);

    print "done\n";

    print "\tCreating Agent \'spsapacheagent\'...";
    $agent = $session->CreateAgent( "spsapacheagent",
                                    $session->GetAgentType("Web Agent"),
                                    "Secure Proxy UI Agent"
                                );
    if(!defined $agent) {
        die "\nFATAL: Unable to create Agent \'spsapacheagent\'\n";
    }

    print "done\n";
}

sub clean_ps_store() {

    $session->DeleteDomain('DOMAIN-SPSADMINUI-spsapacheagent');
    if($status == -1) {
        print "Error deleting domain\n";
    }

    $session->DeleteAgentConfig( 'spsapacheagent-settings');
    if($status == -1) {
        print "Error deleting agent s\n";
    }

    $status = $session->DeleteHostConfig( $host_conf_name );
    if($status == -1) {
        print "Error deleting host config\n";
    }

    $status = $session->DeleteAgent( "spsapacheagent");
    if($status == -1) {
        print "Error deleting agent\n";
    }

    $status = $session->DeleteUserDir("contoso-userdir");
    if($status == -1) {
        print "Error deleting contoso-userdir\n";
    }

}

Mais especificamente, o objetivo principal desse script é criar um agente, um diretório de usuário e um objeto de configuração do host.

O processo de configuração de imagem do Ansible é executado a partir de um contêiner, portanto, não requer nenhum ambiente ou pacote específico na máquina host.

Provisionamento final da imagem

Depois que as imagens forem montadas com sucesso, elas podem ser marcadas e enviadas ao repositório para consumo. Eles podem ser usados no estado em que se encontram ou como imagens de base para oferecer implantações mais personalizadas do Siteminder.

Optando pela orquestração em vez de scripts de host

Os scripts de host são principalmente programas escritos em programação de alto nível ou linguagens shell que residem no host. Como regra geral, criar imagens usando scripts do shell do host é considerado uma prática ruim, pois é esperado um ambiente específico na máquina hospedeira, consistindo em um conjunto específico de ferramentas disponíveis no caminho atual — e isso pode se traduzir em “funciona em meu síndrome da “máquina”. É importante tornar o processo de automação o mais portátil possível, para que ele possa operar em um ambiente de servidor e estação de trabalho, independente do sistema operacional e dos pacotes instalados. Escolhemos o Docker Compose para nossa ferramenta de orquestração. É mais do que suficiente para criar nosso fluxo de trabalho e não requer a instalação de nenhum pacote adicional de DevOps.

Abaixo está o descritor de composição do docker para o CA Siteminder:


version: "3"
services:
  dx:
    build:
      context: ./dockerfiles/cadir/14.1.00
    image: atricore/dx
    hostname: dx
    networks:
      - sm
    ports:
      - "7777:7777"
      - "5022:22"
    stdin_open: true
    tty: true
  ps:
    build:
      context: ./dockerfiles/siteminder/12.8/ps
    image: atricore/ps
    hostname: ps
    networks:
      - sm
    ports:
      - "2022:22"
      - "8443:8443"
    depends_on:
      - dx
    stdin_open: true
    tty: true
  ag:
    build:
      context: ./dockerfiles/siteminder/12.8/ag
    image: atricore/ag
    hostname: ag
    ports:
      - "3022:22"
      - "9090:8080"
      - "9191:8181"
    depends_on:
        - ps
    stdin_open: true
    tty: true
    networks:
      sm:
        aliases:
          - extapp
  app:
    build:
      context: ./dockerfiles/apps/resty
    image: atricore/app
    hostname: app
    networks:
      - sm
    ports:
      - "7070:80"
    depends_on:
      - ag
    stdin_open: true
    tty: true
  browser:
    build:
      context: ./dockerfiles/apps/browser
    image: atricore/browser
    hostname: browser
    networks:
      - sm
    ports:
      - "4022:22"
    depends_on:
        - app
    stdin_open: true
    tty: true
  ansible:
    build:
      context: ./dockerfiles/ansible
    image: atricore/ansible
    hostname: ansible
    networks:
      - sm
    depends_on:
        - dx
        - ps
        - ag
    stdin_open: true
    tty: true
networks:
  sm:
    name: sm-net

Pronto para Dockerizar o Siteminder?

Se você quiser pegar o pacote inteiro, sinta-se à vontade para conferir o código neste Github repositório.

Assine nosso boletim informativo agora!

Obrigado por se juntar ao nosso boletim informativo.
Opa! Algo deu errado.