Categories
linux Windows Server

Reverse proxy for Microsoft Exchange or RDS Gateway

When you have a bunch of applications all needing port 443 and a single public IP, you must use a reverse proxy.

For example, you have an Terminal services broker and an Exchange server which both need to use port 443.

You can set up an Haproxy instance that will proxy the request to the appropriate server. This is the configuration we use.
It uses SNI to find the requested hostname and direct you to the appropriate server.

######## Default values for all entries till next defaults section
defaults
option dontlognull # Do not log connections with no requests
option redispatch # Try another server in case of connection failure
option contstats # Enable continuous traffic statistics updates
retries 3 # Try to connect up to 3 times in case of failure
timeout connect 5s # 5 seconds max to connect or to stay in queue
timeout http-keep-alive 1s # 1 second max for the client to post next request
timeout http-request 15s # 15 seconds max for the client to send a request
timeout queue 30s # 30 seconds max queued on load balancer
timeout tarpit 1m # tarpit hold tim
backlog 10000 # Size of SYN backlog queue

balance roundrobin #alctl: load balancing algorithm
mode tcp #alctl: protocol analyser
option tcplog #alctl: log format
log global #alctl: log activation
timeout client 10800s #alctl: client inactivity timeout
timeout server 10800s #alctl: server inactivity timeout
default-server inter 3s rise 2 fall 3 #alctl: default check parameters

global
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-server-options no-sslv3
ssl-default-server-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
tune.ssl.default-dh-param 2048
log         stdout format raw  local0  info
# turn on stats unix socket
stats socket /var/run/haproxy.stat

listen stats
mode http
log global
bind :9000

maxconn 10

timeout queue 100s

stats enable
stats hide-version
stats refresh 30s
stats show-node
stats auth admin:password
stats uri /haproxy?stats

frontend https-in
bind :::443 v4v6 alpn h2,http/1.1 ssl crt /usr/local/etc/haproxy/certs/
log global
option httplog
mode http
http-response set-header Strict-Transport-Security max-age=31540000

use_backend mail.domain.com if { ssl_fc_sni -i mail.domain.com }
use_backend mail.domain.com if { ssl_fc_sni -i autodiscover.domain.com }
use_backend rds.domain.com if { ssl_fc_sni -i rds.domain.com }
use_backend website.domain.com if { ssl_fc_sni -i website.domain.com }

default_backend mail.domain.com

backend mail.domain.com
mode http
server exchange exchange_server.local:443 ssl verify none maxconn 10000 check #alctl: server exchange configuration.

backend rds.domain.com
mode http
server rds rds_server.local:443 ssl verify none maxconn 10000 check #alctl: server rds configuration.

backend website.domain.com
mode http
server website website_server.local:443 ssl verify none maxconn 10000 check #alctl: server rds configuration.

Categories
Docker linux Security

Traefik 2 and Nextcloud

I use Traefik and Nextcloud on docker and it took me a few tries to get it to the point where nextcloud would not complain about configuration issues.

Here is the configuration I ended up with:

- "traefik.enable=true"
- "traefik.docker.network=webgateway"
- "traefik.http.routers.nextcloud.middlewares=nextcloud,nextcloud_redirect"
- "traefik.http.routers.nextcloud.rule=Host(`nextcloud.fqdn.com`)"
- "traefik.http.services.nextcloud.loadbalancer.server.port=80"
- "traefik.http.routers.nextcloud.entrypoints=websecure"
- "traefik.http.routers.nextcloud.tls.certresolver=mydnschallenge"
- "traefik.http.middlewares.nextcloud.headers.customFrameOptionsValue=ALLOW-FROM https://fqdn.com"
- "traefik.http.middlewares.nextcloud.headers.contentSecurityPolicy=frame-ancestors 'self' fqdn.com *.fqdn.com"
- "traefik.http.middlewares.nextcloud.headers.stsSeconds=155520011"
- "traefik.http.middlewares.nextcloud.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.nextcloud.headers.stsPreload=true"
- "traefik.http.middlewares.nextcloud.headers.referrerPolicy=same-origin"
- "traefik.http.middlewares.nextcloud.headers.customFrameOptionsValue=SAMEORIGIN"
- "traefik.http.middlewares.nextcloud_redirect.redirectregex.regex=/.well-known/(card|cal)dav"
- "traefik.http.middlewares.nextcloud_redirect.redirectregex.replacement=/remote.php/dav/"

This answers a question that’s been asked a few times on this post, which is “how to configure HSTS on Traefik 2”

Categories
Docker

Rexray S3fs docker plugin and Scaleway

I have been looking far and wide for a solution for docker swarm persistent volume shared among multiple hosts and nothing seemed to be easily deployable, reasonnably fast and reliable.

Glusterfs is slow as hell on every hardware I’ve tried to run it, Ceph seems very complicated to set up for a simple swarm setup.

Now I’ve begun experimenting with Rexray S3FS docker plugin and it seems quite interesting.

The only issue I’ve had is that I’m using Scaleway for hosting and I could not find a way to make it work with their S3 implementation.

I was missing a crucial piece of the puzzle: you need to specify the S3FS_REGION option, which is not mentionned in the scaleway docs, so the command line looks like this:

docker plugin install --grant-all-permissions rexray/s3fs:latest \
S3FS_ACCESSKEY=XXXXXXXXXX\
S3FS_SECRETKEY=YYYYYYYYYYYYY \
S3FS_ENDPOINT=https://s3.fr-par.scw.cloud \
S3FS_REGION=fr-par \
S3FS_OPTIONS="allow_other,nonempty,use_path_request_style,url=https://s3.fr-par.scw.cloud"

Also, it seems that the plugin tries to delete existing buckets, so I’ve created a separate account for docker S3 volumes, just to be safe.

Categories
Docker home assistant home automation

Home assistant and docker swarm

I’ve been trying to get Homeassistant working on swarm for a few months now, but the thing that was preventing me from moving to swarm was the Homeassistant requirement to use host networking on the container.
I had tried many things but I finally got everything to work with the macvlan driver. I configured the homeassistant service with two networks:

  • One macvlan network, giving it an IP address in my iot network
  • One overlay network, giving traefik access to homeassistant

The macvlan network is configured as follows. I had to create a local macvlan network on each member of the swarm, with a subnet swarm is free to choose an IP from:

1st node:
docker network create –config-only –subnet=192.168.2.0/24 –gateway=192.168.2.1 -o parent=eth0 –ip-range 192.168.2.232/29 macvlan_local
2nd node:
docker network create –config-only –subnet=192.168.2.0/24 –gateway=192.168.2.1 -o parent=eth0 –ip-range 192.168.2.240/29 macvlan_local
3rd node:
docker network create –config-only –subnet=192.168.2.0/24 –gateway=192.168.2.1 -o parent=eth0 –ip-range 192.168.2.248/29 macvlan_local

Then I create a swarm scoped network:

docker network create -d macvlan –scope swarm –config-from macvlan_local macvlan_swarm

Then I can deploy the whole stack, here is the docker-compose file:
(the wait-for-it config is a script that prevents homeassistant from starting before mqtt, it’s not necessary but quite useful)

version: '3.7'

configs:
  wait-for-it:
    file: /srv/docker/homeassistant/wait-for-it/wait-for-it.sh

services:
  homeassistant:
    image: homeassistant/armhf-homeassistant:0.86.4
    configs:
      - source: wait-for-it
        target: /wait-for-it.sh
        mode: 755
    networks:
      - webgateway
      - macvlan_swarm
    command: ["/wait-for-it.sh", "rasper:1883", "--", "python", "-m", "homeassistant", "--config", "/config"]
    volumes:
      - /srv/docker/homeassistant/config:/config
      - /etc/localtime:/etc/localtime:ro
    deploy:
      labels:
        - "traefik.backend=hassio"
        - "traefik.frontend.rule=Host:myhomeassistanthostname"
        - "traefik.port=8123"
        - "traefik.enable=true"
        - "traefik.docker.network=webgateway"
        - "traefik.default.protocol=http"

  mosquitto:
    image: eclipse-mosquitto
    volumes:
      - /srv/docker/mosquitto/config:/mosquitto/config
      - /etc/localtime:/etc/localtime:ro
      - /srv/docker/mosquitto/data/mosquitto-data:/mosquitto/data
      - /srv/docker/mosquitto/data/mosquitto-log:/mosquitto/log
    ports:
      - "1883:1883"
      - "9001:9001"

  nodered:
    image: nodered/node-red-docker:rpi-v8
    deploy:
      labels:
        - "traefik.backend=nodered"
        - "traefik.frontend.rule=Host:mynoderedhostname"
        - "traefik.port=1880"
        - "traefik.enable=true"
        - "traefik.docker.network=webgateway"
        - "traefik.default.protocol=http"
    networks:
      - webgateway
    volumes:
      - /srv/docker/nodered:/data
    environment:
      - TZ:Europe/Brussels


volumes:
  mosquitto-data:
  mosquitto-log:

networks:
  webgateway:
    external: true
  macvlan_swarm:
    external: true
  hostnet:
    external: true
    name: host
Categories
home assistant home automation

Home assistant and Unifi Mpower pro

I’ve had a Unifi Mpower pro for a while and I thought it was destined to die forgotten in a closet since UBNT has abandonned it… They are not improving the software anymore, and it’s not really installable at this, plus they have massive security issues with the software controller…

But then I found out that someone had made an MQTT client for this power strip!

I’ve never really worked with MQTT before, but it’s actually quite easy.

First, I installed the software on the power strip, after connecting it to my iot-only wireless network (which doesn’t have internet access. After all, this hardware has no software update, I don’t want to get it into some botnet)

Then I configured it like this:

mqtthost=<ip of my mqtt broker>
topic=homeassistant/mpower1

The power strip publishes to MQTT much information about the state of the plugs.
What I was interested in was the ability to turn them on and off, and to use port1 to monitor our washing machine and alert me when it was finished (when the power usage goes below some value)

To allow me to do that, I defined some sensors to get the power usage of each port, a binary sensor that turns on if the power usage on port1 goes above a fixed value, and some switches to turn everything on or off

So in my configuration.yaml, I have:

sensor:
  - platform: mqtt
    name: "mpower port1 power"
    unit_of_measurement: 'watts'
    state_topic: "homeassistant/mpower1/port1/power"
  - platform: mqtt
    name: "mpower port2 power"
    unit_of_measurement: 'watts'
    state_topic: "homeassistant/mpower1/port2/power"
  - platform: mqtt
    name: "mpower port3 power"
    unit_of_measurement: 'watts'
    state_topic: "homeassistant/mpower1/port3/power"
  - platform: mqtt
    name: "mpower port4 power"
    unit_of_measurement: 'watts'
    state_topic: "homeassistant/mpower1/port4/power"
  - platform: mqtt
    name: "mpower port5 power"
    unit_of_measurement: 'watts'
    state_topic: "homeassistant/mpower1/port5/power"
  - platform: mqtt
    name: "mpower port6 power"
    unit_of_measurement: 'watts'
    state_topic: "homeassistant/mpower1/port6/power"

binary_sensor:
  - platform: template
    sensors:
      washer_state:
        friendly_name: "washer state"
        value_template: >-
          {{ states('sensor.mpower_port1_power')|float > 2 }}

switch:
  - platform: mqtt
    state_topic: "homeassistant/mpower1/port1/relay"
    name: "mpower port1 switch"
    command_topic: "homeassistant/mpower1/port1/relay/set"
    payload_on: 1
    payload_off: 0
    availability_topic: 'homeassistant/mpower1/port1/lock/$settable'
    payload_available: 'true'
    payload_not_available: 'false'
  - platform: mqtt
    state_topic: "homeassistant/mpower1/port2/relay"
    name: "mpower port2 switch"
    command_topic: "homeassistant/mpower1/port2/relay/set"
    payload_on: 1
    payload_off: 0
    availability_topic: 'homeassistant/mpower1/port2/lock/$settable'
    payload_available: 'true'
    payload_not_available: 'false'
  - platform: mqtt
    state_topic: "homeassistant/mpower1/port3/relay"
    name: "mpower port3 switch"
    command_topic: "homeassistant/mpower1/port3/relay/set"
    payload_on: 1
    payload_off: 0
    availability_topic: 'homeassistant/mpower1/port3/lock/$settable'
    payload_available: 'true'
    payload_not_available: 'false'
  - platform: mqtt
    state_topic: "homeassistant/mpower1/port4/relay"
    name: "mpower port4 switch"
    command_topic: "homeassistant/mpower1/port4/relay/set"
    payload_on: 1
    payload_off: 0
    availability_topic: 'homeassistant/mpower1/port4/lock/$settable'
    payload_available: 'true'
    payload_not_available: 'false'
  - platform: mqtt
    state_topic: "homeassistant/mpower1/port5/relay"
    name: "mpower port5 switch"
    command_topic: "homeassistant/mpower1/port5/relay/set"
    payload_on: 1
    payload_off: 0
    availability_topic: 'homeassistant/mpower1/port5/lock/$settable'
    payload_available: 'true'
    payload_not_available: 'false'
  - platform: mqtt
    state_topic: "homeassistant/mpower1/port6/relay"
    name: "mpower port6 switch"
    command_topic: "homeassistant/mpower1/port6/relay/set"
    payload_on: 1
    payload_off: 0
    availability_topic: 'homeassistant/mpower1/port6/lock/$settable'
    payload_available: 'true'
    payload_not_available: 'false'

And in my automations.yaml I have

- id: '1525140123407'
  alias: alert if washer stops
  trigger:
    - platform: state
      entity_id:
        - binary_sensor.washer_state
      from: 'on'
      to: 'off'
  action:
    service: telegram_bot.send_message
    data:
      message: 'washing over'
      target: 123456789

- id: '1525140123408'
  alias: alert if washing starts
  trigger:
    - platform: state
      entity_id:
        - binary_sensor.washer_state
      from: 'off'
      to: 'on'
  action:
    service: telegram_bot.send_message
    data:
      message: 'washing starts'
      target: 123456789
Categories
Windows Server

Exchange 2010 management console after removing a DC

Recently we removed the Domain Controller in an Exchange 2010 setup.
All the accounts that had launched the management console at some points in the past were stuck with an error about not finding the deleted DC.
The fix was easy: there is a cache file that needs to be deleted in:
%appdata%\Microsoft\MMC\

Categories
Docker

HSTS with Traefik 1 and Docker

I’ve recently started to move the stuff I host to Docker, using the Traefik reverse proxy as the SSL termination.

Traefik is a really nice piece of software, but unfortunately while the documentation is great, it’s somewhat missing in tutorials and examples.

Among other things, I host a Nextcloud instance, and among the security suggestions, it tells me to add a Strict-Transport-Security header with a value of at least 15552000.

In my case, it was not strictly necessary as edzilla.info is already using HSTS preloading, but I wanted to follow the security suggestions to the letter.

To add the header to any host reverse proxied service, you simply have to add a label such as this:

traefik.frontend.headers.customResponseHeaders=Strict-Transport-Security:15552000

Categories
Rudder

Rudder and in-file text replacement

At work, we use Rudder to take care of configuration automation.

Today, I was trying to update a small script that we use on all our SMTP gateways, which does an LDAP request.
That LDAP request needed to be updated and instead of connecting to 40+ servers, I made a new Directive in Rudder. Using a regexp, I was trying to replace:

(objectCategory=msExchDynamicDistributionList)(objectCategory=group)(objectCategory=publicFolder)))",

with

(objectCategory=msExchDynamicDistributionList)(objectCategory=group)(objectCategory=publicFolder)))",

With the help of https://regex101.com, I used this regexp at first:

\(objectCategory=group\)

But for some reason it wasn’t working, and I would get:
Error: Because the regular expression '\(objectCategory=group\)' still matches the replacement string

The explanation, of course, is that the regexp I used would still match the replacement line, which means that every time rudder ran, it would be applied.

Thank you rudder for saving my script 🙂

Categories
linux

Proftpd on CIFS share

For several of our clients, we provide an FTP server on a linux server, with the files hosted on a windows 2008r2 server and authentication being handled by active directory through proftpd-ldpap.

The windows share are automatically mounted on access with autofs in a /srv/ftp subfolder, and users are jailed in yet another level of subdirectory using proftpd.

I hit a bit of a wall recently with this setup, as everything seemed to be in order, autofs mounted the directory, proftpd allowed login with ldap auth, but for some reason I couldn’t write anything.

If I pointed proftpd at a local directory however, write was fine.

 

I finally found out that proftpd was trying to use “chmod” on every write, and with SMB < 3, it failed, resulting in a “permission denied” error and nothing whatsoever in the logs.

 

I fixed it by mounting the CIFS share with the “noperm” option:

homes -fstype=cifs,rw,credentials=/etc/cifscredentials,gid=nogroup,uid=proftpd,vers=2.1,noperm ://SERVER/SHARE\$/SFTP

Categories
Zabbix

A few Zabbix templates

We are in the process of moving our monitoring platform from a non working whatsup’gold to Zabbix.

Among other things, we wanted to monitor our domains at OVH (expiration date, essentially) and Eset Nod32 remote management server.

Here are the templates I designed, along with the necessary scripts:

https://github.com/Edzilla2000/ZABBIX-Eset-Nod32

https://github.com/Edzilla2000/zabbix-ovh