Table of Contents

Для своего времени Semaphore был неплох.
Однако, Semaphore больше не поддерживается и сейчас стал бесплатным upstream Ansible Tower - AWX. Установить его можно вот так: Установка AWX на Ubuntu или в кластер Kubernetes
По сути Semaphore - бледное подобие Ansible Tower.

Установка Semaphore на Ubuntu/Debian

https://github.com/ansible-semaphore/semaphore/wiki/Installation

sudo apt-get update && sudo apt-get -y upgrade
sudo apt-get install -y software-properties-common
sudo apt-add-repository -y universe
sudo add-apt-repository -y ppa:ansible/ansible
sudo apt-get install -y git ansible mysql-server nginx
wget https://github.com/ansible-semaphore/semaphore/releases/download/v2.5.1/semaphore_2.5.1_linux_amd64.deb
sudo dpkg -i ./semaphore_2.5.1_linux_amd64.deb

Создаем базу данных:

 sudo mysql


mysql> CREATE DATABASE semaphore;
Query OK, 1 row affected (0.04 sec)

mysql> CREATE USER 'semaphore'@'localhost' IDENTIFIED BY 'superpassword';
Query OK, 0 rows affected (0.06 sec)

mysql> GRANT ALL PRIVILEGES ON semaphore.* TO 'semaphore'@'localhost';
Query OK, 0 rows affected (0.01 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.03 sec)

mysql> exit

Создаем файл конфигурации:

sudo semaphore -setup

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

Semaphore AD Authentication

Для аутентификации пользователей из AD нужно включить LDAP. Эта часть конфига должна выглядеть так:

        "ldap_binddn": "semaphore",
        "ldap_bindpassword": "semaphore_password",
        "ldap_server": "dc.local:389",
        "ldap_searchdn": "OU=Users,DC=domain,DC=local",
        "ldap_searchfilter": "(sAMAccountName=%s)",
        "ldap_mappings": {
                "dn": "dn",
                "mail": "mail",
                "uid": "sAMAccountName",
                "cn": "cn"
        },
        "ldap_enable": true,
        "ldap_needtls": false

С начала я пытался сконфигурировать параметр “ldap_binddn” в виде CN=semaphore,OU=Users,DC=domain,DC=local, однако это приводило к ошибкам вида:

time="2018-09-26T08:48:22Z" level=info msg="LDAP Result Code 49 "Invalid Credentials": 80090308: LdapErr: DSID-0C09042A, comment: AcceptSecurityContext error, data 52e, v3839"

в таком случае, стоит попробывать в качестве “ldap_binddn” использовать просто логин (sAMAccountname или UserPrincipalName а не distinguished name) пользователя, который будет обращаться к LDAP.
Также, вместо uid нужно использовать sAMAccountName. В противном случае можно увидеть такую ошибку:

time="2018-09-26T13:45:35Z" level=info msg="User does not exist or too many entries returned"

Запуск semaphore в виде сервиса

Копируем конфиг semaphore в /etc/ansible/:

cp ./config.json /etc/ansible/semaphore_config.json

И конфигурируем сервис: /etc/systemd/system/semaphore.service

[Unit]
Description=Ansible Semaphore
After=syslog.service
Before=nginx.service
Requires=network.target

[Service]
Type=forking
EnvironmentFile=-/etc/default/ansible-semaphore
ExecStart=/bin/sh -c "/usr/bin/semaphore -config ${SEMAPHORE_CONFIG} >> ${SEMAPHORE_LOGS} 2>&1 &"
Restart=always
RestartSec=10s

[Install]
WantedBy=multi-user.target


/etc/default/ansible-semaphore

#Ansible Semaphore Defaults

SEMAPHORE_CONFIG=/etc/ansible/semaphore_config.json
SEMAPHORE_LOGS=/var/log/semaphore.log

Включаем и запускаем сервис:

systemctl enable semaphore.service
service semaphore start

SSL посредством nginx

/etc/nginx/nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
  worker_connections 1024;
  use epoll;
  multi_accept on;
}

http {
        tcp_nodelay on;
        keepalive_timeout 600;
        types_hash_max_size 2048;

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        gzip on;

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}



/etc/nginx/sites-enabled/default

server {
        listen 80;
        root /var/www/;
        
        #Enable access to acme files
        location ~ /\.well-known/acme-challenge/ {
                        allow all;
                        access_log off;
                        log_not_found off;
       }

       return 301 https://$host$request_uri;
  }

  upstream semaphore {
    server 127.0.0.1:3000;
  }

  server {
    listen 443 ssl http2;
    server_name  server.domain.com;
    
    # add Strict-Transport-Security to prevent man in the middle attacks
    add_header Strict-Transport-Security "max-age=31536000" always;

    # SSL
    ssl_certificate /etc/letsencrypt/live/server.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/server.domain.com/privkey.pem;

    # Recommendations from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
    ssl_protocols TLSv1.1 TLSv1.2;
    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    # disable any limits to avoid HTTP 413 for large image uploads
    client_max_body_size 0;

    # required to avoid HTTP 411: see Issue #1486 (https://github.com/docker/docker/issues/1486)
    chunked_transfer_encoding on;

    location / {
      proxy_pass http://semaphore/;
      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

      proxy_set_header X-Forwarded-Proto $scheme;

      proxy_buffering off;
      proxy_request_buffering off;
    }

    location /api/ws {
      proxy_pass http://semaphore/api/ws;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_set_header Origin "";
    }
  }