summaryrefslogtreecommitdiff
path: root/doc/markdown/automation-stack-architecture.md
blob: 2ae775c926518b6b10b2c11bf1a2d9006f4cf345 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
# Far-Edge Automation Stack Deployment Guide

The Far-Edge automation stack consists of nine Ubuntu 18.04 hosts:

- Two load balancers
- Two middleware application hosts
- Two ansible hosts
- Three database hosts

The provisioning of these hosts in openstack is outside the scope of
this document. There will be a separate document to address this.

There is an additional host in the production stack to handle LDAP
dual-stack proxying to reach USWIN. This host does not live in the
automation stack tenant space. It is a standalone VM managed by VCPe.

## OpenStack tenant space

In both production and MTCE the far-edge automation stack runs in an
OpenStack tenant space. (Production runs in VCPe.) Creating the
OpenStack objects (neutron networks, cinder volumes, instances) is
beyond the scope of this document and will be documented separately.

Secgroup rules must be created to allow network traffic. Use a script
like the following to create the secgroup rules:

    #!/bin/bash

    private_network_cidr="2001:4888:a21:3102:245:29::/112"
    public_network_cidrs="2001:4888::/32"

    for name in default-grp; do
        openstack security group show ${name} || openstack security group create --description "${name}" ${name}
        for protocol in tcp udp; do
            if [ "openstack security group show ${name} -f shell -c rules | grep -F 'ingress' | grep -F \"port_range_min='1'\" | grep -F \"port_range_max='65535'\" | grep -F \"${protocol}\" | grep -F 'IPv6'" == "" ]; then
                openstack security group rule create --egress --protocol ${protocol} --src-ip ::/0 --dst-port 1:65535 --ethertype IPv6 ${name}
            fi
        done
        for cidr in ${private_network_cidr} ${public_network_cidrs}; do
            if [ "$(openstack security group show ${name} -f shell -c rules | grep -F \"icmp\" | grep -F \"remote_ip_prefix='${cidr}'\")" == "" ]; then
                openstack security group rule create --ingress --protocol icmp --src-ip ${cidr} --ethertype IPv6 ${name}
            fi
        done
        for port in 22; do
            for cidr in ${private_network_cidr} ${public_network_cidrs}; do
                if [ "$(openstack security group show ${name} -f shell -c rules | grep -F \"port_range_min='${port}'\" | grep -F \"remote_ip_prefix='${cidr}'\")" == "" ]; then
                    openstack security group rule create --ingress --protocol tcp --dst-port ${port} --src-ip ${cidr} --ethertype IPv6 ${name}
                fi
            done
        done
    done

    for name in icinga; do
        openstack security group show ${name} || openstack security group create --description "${name}" ${name}
        for port in 5665; do
            for cidr in ${private_network_cidr}; do
                if [ "$(openstack security group show ${name} -f shell -c rules | grep -F \"port_range_min='${port}'\" | grep -F \"remote_ip_prefix='${cidr}'\")" == "" ]; then
                    openstack security group rule create --ingress --protocol tcp --dst-port ${port} --src-ip ${cidr} --ethertype IPv6 ${name}
                fi
            done
        done
    done

    for name in ldap; do
        openstack security group show ${name} || openstack security group create --description "${name}" ${name}
        for port in 636; do
            for cidr in ${private_network_cidr}; do
                if [ "$(openstack security group show ${name} -f shell -c rules | grep -F \"port_range_min='${port}'\" | grep -F \"remote_ip_prefix='${cidr}'\")" == "" ]; then
                    openstack security group rule create --ingress --protocol tcp --dst-port ${port} --src-ip ${cidr} --ethertype IPv6 ${name}
                fi
            done
        done
    done

    for name in web; do
        openstack security group show ${name} || openstack security group create --description "${name}" ${name}
        for port in 443 3000; do
            for cidr in ${private_network_cidr} ${public_network_cidrs}; do
                if [ "$(openstack security group show ${name} -f shell -c rules | grep -F \"port_range_min='${port}'\" | grep -F \"remote_ip_prefix='${cidr}'\")" == "" ]; then
                    openstack security group rule create --ingress --protocol tcp --dst-port ${port} --src-ip ${cidr} --ethertype IPv6 ${name}
                fi
            done
        done
        for port in 80 3000 3080 3128:3130 8000; do
            for cidr in ${private_network_cidr}; do
                if [ "$(openstack security group show ${name} -f shell -c rules | grep -F \"port_range_min='${port}'\" | grep -F \"remote_ip_prefix='${cidr}'\")" == "" ]; then
                    openstack security group rule create --ingress --protocol tcp --dst-port ${port} --src-ip ${cidr} --ethertype IPv6 ${name}
                fi
            done
        done
    done

    for name in galera; do
        openstack security group show ${name} || openstack security group create --description "${name}" ${name}
        for port in 3306 4444 4567 9200 13306; do
            for cidr in ${private_network_cidr}; do
                if [ "$(openstack security group show ${name} -f shell -c rules | grep -F \"port_range_min='${port}'\" | grep -F \"remote_ip_prefix='${cidr}'\")" == "" ]; then
                    openstack security group rule create --ingress --protocol tcp --dst-port ${port} --src-ip ${cidr} --ethertype IPv6 ${name}
                fi
            done
        done
    done

    for name in zmq; do
        openstack security group show ${name} || openstack security group create --description "${name}" ${name}
        for port in 5555:5559; do
            for cidr in ${private_network_cidr}; do
                if [ "$(openstack security group show ${name} -f shell -c rules | grep -F \"port_range_min='${port}'\" | grep -F \"remote_ip_prefix='${cidr}'\")" == "" ]; then
                    openstack security group rule create --ingress --protocol tcp --dst-port ${port} --src-ip ${cidr} --ethertype IPv6 ${name}
                fi
            done
        done
    done

    exit 0

## Database

```far_edge_ops_api``` and icinga2 require a database backend. We
currently use postgresql. The goal is to migrate to
galera/mariadb. For now we are using only one database host, but when
we switch to galera we will use all three. The galera solution is
dockerized. It is a mature and well-tested product of the VCP Metering
project.

### Installation

Install postgresql on the first database host per the typical
procedure that can be found on the internet. TODO: provide a link to
an internet HOWTO that best fits our needs.

#### postgresql.conf:

    data_directory = '/var/lib/postgresql/12/data'
    listen_addresses = '*'
    port = 5432
    max_connections = 2000
    password_encryption = md5
    ssl = off
    shared_buffers = 2000MB
    work_mem = 4MB
    maintenance_work_mem = 256MB
    dynamic_shared_memory_type = posix
    wal_buffers = 2MB
    checkpoint_timeout = 15min
    max_wal_size = 1GB
    min_wal_size = 80MB
    checkpoint_completion_target = 0.9
    logging_collector = on
    log_directory = '/var/lib/postgresql/12_log/'
    log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
    log_rotation_size = 50MB
    log_min_duration_statement = 5
    log_checkpoints = on
    log_connections = on
    log_disconnections = on
    log_duration = on
    log_error_verbosity = default
    log_line_prefix = '%m, %d, %a. %r, %s, %x, %p '
    log_timezone = 'GMT'
    autovacuum = on
    idle_in_transaction_session_timeout = 43200000
    datestyle = 'iso, dmy'
    timezone = 'GMT'
    lc_messages = 'en_US.utf8'
    lc_monetary = 'en_US.utf8'
    lc_numeric = 'en_US.utf8'
    lc_time = 'en_US.utf8'
    default_text_search_config = 'pg_catalog.simple'
    plperl.on_init = 'use utf8; use re; package utf8; require "utf8_heavy.pl";'

#### pg_hba.conf:

    local   icinga          icinga                                  md5
    local   all             all                                     password
    local   replication     postgres                                trust
    host    icinga          icinga          127.0.0.1/32            md5
    host    icinga          icinga          ::1/128                 md5
    host    all             all             127.0.0.1/32            password
    host    all             all             ::1/128                 password
    host    icinga          icinga          2001:4888:a21:3102::/32 md5
    host    all             all             2001:4888:a21:3102::/32 password

#### sysctl.conf:

Configure ```kernel.shmmax``` and ```kernel.shmall``` to be 25\% of
available RAM. For example on a 16GB host:

    kernel.shmmax=4199411712
    kernel.shmall=4199411712

### User and database creation

As the postgres user:

    # createuser -l -P -e faredge
    # createdb -e -O faredge faredge

We do not need to populate the database with a schema. Django will do
this automatically.

## Load Balancers

The services which run on the loadbalancer hosts are as follows:

- apache
- haproxy
- icinga2

### Apache

#### SSL Certificates:

A prerequisite for configuring apache is an SSL x509 certificate that
answers to the following names:

    middleware.<fqdn>
    icinga.<fqdn>
    dns-admin.<fqdn>

Place the certificate in ```/etc/ssl/certs/``` and the key in
```/etc/ssl/private```.

#### Modules:

Apache needs the following modules enabled:

    - access_compat.load
    - alias.conf
    - alias.load
    - auth_basic.load
    - authn_core.load
    - authn_file.load
    - authz_host.load
    - authz_user.load
    - authz_core.load
    - autoindex.conf
    - autoindex.load
    - deflate.conf
    - deflate.load
    - dir.conf
    - dir.load
    - env.load
    - filter.load
    - http2.load
    - mime.conf
    - mime.load
    - mpm_prefork.conf
    - mpm_prefork.load
    - negotiation.conf
    - negotiation.load
    - php7.2.conf
    - php7.2.load
    - proxy.conf
    - proxy.load
    - proxy_http.load
    - proxy_http2.load
    - proxy_wstunnel.load
    - reqtimeout.conf
    - reqtimeout.load
    - rewrite.load
    - setenvif.conf
    - setenvif.load
    - socache_shmcb.load
    - ssl.conf
    - ssl.load
    - status.conf
    - status.load

#### VirtualHosts:

Apache needs the following sites enabled:

    - dns-admin.conf
    - far_edge_ops_api.conf
    - icinga.conf
    - webdav.conf

```webdav.conf``` is only needed when an external HTTP share is not
available.

##### dns-admin.conf:

    <VirtualHost [2607:f160:b:10f1::e]:443>
        ServerName dns-admin.faredge.vzwops.com
        ServerAdmin carlos.konstanski@verizonwireless.com
        Protocols http/1.1
        SSLEngine on
        SSLProxyEngine on
        SSLCertificateFile /etc/ssl/certs/vcpfe-lb-vip.faredge.vzwops.com.pem
        SSLCertificateKeyFile /etc/ssl/private/vcpfe-lb-vip.faredge.vzwops.com.key
        SSLProtocol all -SSLv2 -SSLv3
        SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
        SSLHonorCipherOrder on
        SSLCompression off
        SSLOptions +StrictRequire
        ProxyPass "/" "http://[::1]:13000/"
        ProxyPassReverse "/" "http://[::1]:13000/"
        SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown
        ErrorLog "/var/log/apache2/dnsadmin_error_log"
        CustomLog "/var/log/apache2/dnsadmin_access_log" common
    </VirtualHost>

##### far\_edge\_ops\_api.conf:

    <VirtualHost [2607:f160:b:10f1::e]:443>
        ServerName middleware.faredge.vzwops.com
        ServerAdmin carlos.konstanski@verizonwireless.com
        Protocols http/1.1
        SSLEngine on
        SSLProxyEngine on
        SSLCertificateFile /etc/ssl/certs/vcpfe-lb-vip.faredge.vzwops.com.pem
        SSLCertificateKeyFile /etc/ssl/private/vcpfe-lb-vip.faredge.vzwops.com.key
        SSLProtocol all -SSLv2 -SSLv3
        SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
        SSLHonorCipherOrder on
        SSLCompression off
        SSLOptions +StrictRequire
        ProxyPass "/" "http://[::1]:13080/"
        ProxyPassReverse "/" "http://[::1]:13080/"
        ErrorLog "/var/log/apache2/middleware_error_log"
        CustomLog "/var/log/apache2/middleware_access_log" common
    </VirtualHost>

##### icinga.conf:

    <VirtualHost [2607:f160:b:10f1::e]:443>
        ServerName icinga.faredge.vzwops.com
        ServerAdmin carlos.konstanski@verizonwireless.com
        DocumentRoot "/usr/share/icingaweb2/public"
        SSLEngine on
        SSLProxyEngine on
        SSLCertificateFile /etc/ssl/certs/vcpfe-lb-vip.faredge.vzwops.com.pem
        SSLCertificateKeyFile /etc/ssl/private/vcpfe-lb-vip.faredge.vzwops.com.key
        SSLProtocol all -SSLv2 -SSLv3
        SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
        SSLHonorCipherOrder on
        SSLCompression off
        SSLOptions +StrictRequire

        <Directory "/usr/share/icingaweb2/public">
            Options SymLinksIfOwnerMatch
            AllowOverride None

            <IfModule mod_authz_core.c>
                <RequireAll>
                    Require all granted
                </RequireAll>
            </IfModule>

            SetEnv ICINGAWEB_CONFIGDIR "/etc/icingaweb2"
            EnableSendfile Off

            <IfModule mod_rewrite.c>
                RewriteEngine on
                RewriteBase /
                RewriteCond %{REQUEST_FILENAME} -s [OR]
                RewriteCond %{REQUEST_FILENAME} -l [OR]
                RewriteCond %{REQUEST_FILENAME} -d
                RewriteRule ^.*$ - [NC,L]
                RewriteRule ^.*$ index.php [NC,L]
            </IfModule>
        </Directory>
    </VirtualHost>

##### webdav.conf:

    <VirtualHost *:80>
        ServerName webdav.faredge.vzwops.com
        ServerAdmin carlos.konstanski@verizonwireless.com
        DocumentRoot "/var/www/html/webdav"
        SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown
        ErrorLog "/var/log/apache2/webdav_error_log"
        CustomLog "/var/log/apache2/webdav_access_log" common
        <Directory "/var/www/html/webdav">
            AllowOverride limit
            Options Indexes FollowSymLinks
        </Directory>
    </VirtualHost>

##### Webdav directory:

Create this directory only if you don't have access to an external HTTP share and you are using the webdav VirtualHost above:

    - /var/www/html/webdav

### HAProxy

Use the following haproxy config, adjusting IP addresses, hostnames and credentials as needed:

    global
        tune.ssl.default-dh-param 2048
        log /dev/log local0
        log /dev/log local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin
        stats timeout 30s
        maxconn 5000
        user haproxy
        group haproxy
        daemon
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private
        ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
        ssl-default-bind-options no-sslv3

    defaults
        log global
        option dontlognull
        timeout connect 10s
        timeout client 60s
        timeout server 180s
        errorfile 400 /etc/haproxy/errors/400.http
        errorfile 403 /etc/haproxy/errors/403.http
        errorfile 408 /etc/haproxy/errors/408.http
        errorfile 500 /etc/haproxy/errors/500.http
        errorfile 502 /etc/haproxy/errors/502.http
        errorfile 503 /etc/haproxy/errors/503.http
        errorfile 504 /etc/haproxy/errors/504.http

    listen stats
        bind 127.0.0.1:1993
        mode http
        option httplog
        option forwardfor
        stats enable
        stats uri /stats
        stats auth someuser:somepass
        stats refresh 30s
        stats show-node
        stats hide-version
        log global

    frontend dns-admin
        bind 0.0.0.0:13000
        bind :::13000
        mode http
        option httplog
        option forwardfor
        default_backend dns-admin-backend

    backend dns-admin-backend
        balance roundrobin
        mode http
        option log-health-checks
        option httpchk GET /dns HTTP/1.1\r\nHost:\ dns-admin.faredge.vzwops.com\r\nUser-Agent:\ curl/7.35.0\r\nAccept:\ */*\r\nAuthorization:\ Basic\ b3BlbmJvb2s6WnlsejNmZE1TZDhTVmlod0w5TnlaTHpU
        default-server inter 10s fall 1 rise 1
        server server-1 [2607:f160:b:10f1::7]:80 check
        #server server-2 [2607:f160:b:10f1::8]:80 check

    frontend far_edge_ops_api
        bind 0.0.0.0:13080
        bind :::13080
        mode http
        option httplog
        option forwardfor
        reqdel ^Host:
        reqadd Host:\ middleware.faredge.vzwops.com
        default_backend far_edge_ops_api-backend

    backend far_edge_ops_api-backend
        balance roundrobin
        mode http
        option log-health-checks
        option httpchk GET /caas/ HTTP/1.1\r\nHost:\ middleware.faredge.vzwops.com\r\nUser-Agent:\ curl/7.35.0\r\nAccept:\ */*\r\nAuthorization:\ Basic\ bWlkZGxld2FyZXVzZXI6bWlkZGxld2FyZXBhc3N3b3Jk
        default-server inter 10s fall 1 rise 1
        server server-1 [2607:f160:b:10f1::6]:3080 check
        #server server-2 [2607:f160:b:10f1::5]:3080 check

We currently only proxy to one backend for both ```dns-admin``` and
```far_edge_ops_api``` because there has been no opportunity to test
the behavior of the apps in a multiple-backend configuration. This is
why ```server-2``` is commented out. TODO: update this document when
this testing is complete.

### Icinga2

The icinga2 configuration is mostly standard. TODO: provide a link to
an internet HOWTO from which the rest of this documentation can
build. There will be only minor additions to the standard base
install.

## Jenkins

The applications are built and deployed from jenkins. The build jobs
already exist. But a new deploy job needs to be added for the new
environment which you are building.

### env.yaml

All of the far-edge builds and deploymenets are run on the jenkins
slave ```openbook-docker-slave-4.meter.vzwops.com```. Log onto this
host and edit three files in the jenkins home directory
```/var/lib/jenkins/```:

    - env.yaml.ansible-queue
    - env.yaml.dns-admin
    - env.yaml.far-edge-ops-api

A new deployment target section needs to be added to each of these
files. Give the environment a new unique name and use the same name in
all three files.

### jenkins-job-builder (jjb)

Clone the following git repo onto your laptop:

    git@gitlab.verizon.com:ONV6661_VCPSPUBLIC/jjb.git

Create an ini file called ```verizoncloudplatform.com.ini``` in the
top-level directory of your jjb git checkout with the following
contents:

#### verizoncloudplatform.com.ini:
    [job_builder]
    ignore_cache=True
    keep_descriptions=False
    include_path=.:scripts:~/git/
    recursive=False
    exclude=.*:manual:./development
    allow_duplicates=False

    [jenkins]
    user=<your_jenkins_username>
    password=<your_jenkins_api_token>
    url=https://openbook-jenkins-master.meter.vzwops.com:8080/
    timeout=120
    query_plugins_info=False

Follow these steps to create the new deploy jobs:

- Open the file ```yaml/verizoncloudplatform.com/deploy-vcpfe.yaml```
  in an editor.

- Add three new jobs to the list, one for each app
  (```ansible-queue```, ```dns-admin``` and
  ```far-edge-ops-api```). Set the target attribute to the new ansible
  target which you created in env.yaml in the previous section.

- ```# ./run.sh verizoncloudplatform.com```

Once this is done, log into the jenkins UI to verify that the new jobs
were created.

Push your jjb changes to git. (The ini file is gitignored; it is your
private file.)

## Application hosts

The services which run on the application hosts are as follows:

    - far_edge_ops_api

### far\_edge\_ops\_api

The middleware (as ```far_edge_ops_api``` is ubiquitously known) is a
docker containerized django web application.

#### Build/deploy

The application can be built and deployed from jenkins:

https://openbook-jenkins-master.meter.vzwops.com:8080

Run the job ```docker-container-far-edge-ops-api``` to build the
docker container, and the appropriate
```far-edge-ops-api-deploy-vcpfe-<env>``` job to deploy it to an
environment.

#### Configure

It might seem backwards to perform the build and deployment before
configuring. It's a chicken-and-egg problem: the deployment needs to
create the required files and directories before configuration can
commence. For first-time deployments the process will look like: build
-> deploy -> configure -> deploy. This is true of all three
applications.

The main configuration directory is ```/etc/far-edge-ops-api/```. It
looks like the following on a working system:

    $ tree /etc/far-edge-ops-api/
    /etc/far-edge-ops-api/
    ├── modules.d
    │   ├── wsgi.conf
    │   └── wsgi.load
    ├── settings.py
    └── vhosts.d
        └── far-edge-ops-api.conf

```settings.py``` is a rather long file, too long to include here. Get
a copy from a known good source (a production or lab server) and edit
to suit the new environment.

```far-edge-ops-api.conf``` is a rather typical-looking apache
VirtualHosts file, but with LDAP auth added. First the file:

##### far-edge-ops-api.conf:

    LDAPTrustedGlobalCert CA_BASE64 "/etc/apache2/auth/uswin_ca.pem"
    LDAPTrustedGlobalCert CA_BASE64 "/etc/ssl/certs/selfsigned_root_cert.crt"
    LDAPTrustedMode SSL
    LDAPLibraryDebug 7

    <VirtualHost *:3080>
        ServerName middleware.<fqdn>
        DocumentRoot "/opt/far-edge-ops-api/src/src"
        WSGIScriptAlias / /opt/far-edge-ops-api/src/src/far_edge_ops_api/wsgi.py
        WSGIDaemonProcess middleware.<fqdn> processes=20 threads=15 display-name=%{GROUP} python-home=/opt/far-edge-ops-api/src/venv
        WSGIProcessGroup middleware.<fqdn>
        Alias /static/ /opt/far-edge-ops-api/src/src/static/
        TimeOut 600
        SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown
        ErrorLog "/var/log/apache2/far-edge-ops-api_error_log"
        CustomLog "/var/log/apache2/far-edge-ops-api_access_log" common
    </VirtualHost>

    <Directory "/opt/far-edge-ops-api/src/src">
        AllowOverride all
        Options FollowSymlinks
        AuthName "USWIN"
        AuthType Basic
        AuthBasicProvider ldap
        AuthLDAPURL "ldaps://vcpfe-ldap-prod.vcpfe.vzwops.com:636/DC=uswin,DC=ad,DC=vzwcorp,DC=com?sAMAccountName?sub?(objectclass=*)"
        AuthLDAPBindDN "CN=SVC-VCP-LDAP,OU=SVC,OU=FNA,DC=uswin,DC=ad,DC=vzwcorp,DC=com"
        AuthLDAPBindPassword "<password_for_SVC-VCP-LDAP_account>"
        Require valid-user
    </Directory>

    <Directory "/opt/far-edge-ops-api/src/src/static">
        AllowOverride limit
        Require all granted
        Options Indexes FollowSymLinks
    </Directory>

There are two SSL certs because the far-edge stack is IPv6-only while
the production LDAP endpoint (USWIN) is IPv4-only. So we must use a
proxy. The first cert is the actual uswin cert, while the second cert
is the one for the proxy. Both of these certs must be placed on the
app hosts.

Replace ```<fqdn>``` with the DNS domain. Replace
```<password_for_SVC-VCP-LDAP_account>``` with the correct password.

OpenLDAP needs to be configured with these same certs. Since they are
self-signed, openldap will reject them unless they are listed as
exceptions.

#### /etc/ldap/ldap.conf:

    TLS_CACERT /etc/apache2/auth/uswin_ca.pem
    LDAPTLS_CACERT /etc/ssl/certs/self_ca_signed_cert_and_key_2.pem

Now that the configuration is complete, rerun the deployment from
jenkins. This time the application should start normally.

## Queue hosts

The services which run on the application hosts are as follows:

    - ansible-queue
    - dns-admin

### ansible-queue

```ansible-queue``` is a docker containerized common lisp
application.

#### Build/deploy

The application can be built and deployed from jenkins:

https://openbook-jenkins-master.meter.vzwops.com:8080

Run the job ```docker-container-ansible-queue``` to build the docker
container, and the appropriate ```ansible-queue-deploy-vcpfe-<env>```
job to deploy it to an environment.

#### Configure

Two files are required in the directory ```/etc/ansible-queue/```:

    - .ansible_pass.far_edge
    - options.lisp

```.ansible_pass.far_edge``` is the ansible vault password
file. Obtain a copy from an existing queue host.

```options.lisp``` is the config file for the ```ansible-queue```
application.

##### options.lisp:

    ((:queue (:num-icinga-process-threads 1
              :num-bmc-process-threads 30
              :num-nic-process-threads 20
              :num-wr-process-threads 25
              :num-dns-process-threads 1
              :num-patch-process-threads 1
              :num-hw-process-threads 20
              :num-fix-process-threads 20
              :icinga-wait-interval 0
              :bmc-wait-interval 1
              :nic-wait-interval 30
              :wr-wait-interval 0
              :dns-wait-interval 0
              :patch-wait-interval 30
              :hw-wait-interval 1
              :fix-wait-interval 1
              :sleep-interval 60
              :ipv6 t
              :vault-password-file "/etc/ansible-queue/.ansible_pass.far_edge"
              :icinga-queue-url "tcp://*:5555"
              :bmc-queue-url "tcp://*:5556"
              :nic-queue-url "tcp://*:5557"
              :wr-queue-url "tcp://*:5558"
              :dns-queue-url "tcp://*:5559"
              :patch-queue-url "tcp://*:5560"
              :hw-queue-url "tcp://*:5561"
              :fix-queue-url "tcp://*:5562"
              :middleware-url "https://middleware.vcpfe.vzwops.com"
              :middleware-username "SVC-Far-Edge"
              :middleware-password "<password_for_SVC-Far-Edge_account>")))

Replace ```<password_for_SVC-Far-Edge_account>``` with the correct
password.

Now that the configuration is complete, rerun the deployment from
jenkins. This time the application should start normally.

### dns-admin

```dns-admin``` is a docker containerized web application written in
common lisp and clojurescript.

#### Build/deploy

The application can be built and deployed from jenkins:

https://openbook-jenkins-master.meter.vzwops.com:8080

Run the job ```docker-container-dns-admin``` to build the docker
container, and the appropriate ```dns-admin-deploy-vcpfe-<env>``` job
to deploy it to an environment.

#### Configure

Only one new config file is required. Use the following content
verbatim:

##### /etc/dns-admin/conf.lisp:

    (:name "dns-admin"
     :document-root "dns-admin"
     :title "DNS Administration"
     :meta-description "A website for administering DNS. Supports nsupdate and infoblox."
     :dns (:label "CDS Infoblox"
           :backend-type "infoblox"
           :url "https://infoblox.alias/wapi/v2.6"))

Notice the odd-looking hostname ```infoblox.alias```. The following
needs to be added to ```/etc/hosts```:

    2607:f160:8a02:8016:a0:103:0:10 infoblox.alias

Now that the configuration is complete, rerun the deployment from
jenkins. This time the application should start normally.