27 ตุลาคม 2566

build postfix docker image for mikrotik chr

- smtp authen for send mail
- no mailbox only forward to another email

---
Dockerfile
FROM alpine:latest

RUN apk update  
RUN apk add bash ca-certificates cyrus-sasl cyrus-sasl-login cyrus-sasl-crammd5 iproute2 mailx postfix postfix-pcre rsyslog supervisor tzdata
RUN rm -rf /tmp/*
RUN rm -rf /var/cache/apk/*

EXPOSE 25/tcp
EXPOSE 587/tcp

COPY ./supervisord.conf /etc/supervisord.conf
COPY ./smtpd.pem /etc/postfix/certs/smtpd.pem
COPY ./docker-entrypoint.sh /docker-entrypoint.sh

RUN chmod +x /docker-entrypoint.sh

ENTRYPOINT ["/docker-entrypoint.sh"]

CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]


---
docker-entrypoint.sh
#!/bin/bash

cp -f /usr/share/zoneinfo/Asia/Bangkok /etc/localtime
echo Asia/Bangkok > /etc/timezone

mkdir -p /var/spool/rsyslog/

cat <<EOF > /etc/rsyslog.conf
module(load="imuxsock")

\$WorkDirectory  /var/spool/rsyslog

*.*             -/dev/stdout
EOF

mkdir -p /etc/sasl2/

cat <<EOF > /etc/sasl2/smtp.conf
pwcheck_method: auxprop
auxprop_plugin: sasldb
mech_list: PLAIN LOGIN CRAM-MD5 DIGEST-MD5
log_level: 7
EOF

echo "user@domain.tld forward@gmail.com" >> /etc/postfix/virtual
echo "admin@domain.tld root" >> /etc/postfix/virtual

postmap /etc/postfix/virtual

postconf -e "virtual_alias_maps = lmdb:/etc/postfix/virtual"

postconf -e "mydomain = domain.tld"
postconf -e "myhostname = mail.domain.tld"
postconf -e "mydestination = localhost, \$myhostname, \$mydomain"
postconf -e "inet_interfaces = all"
postconf -e "broken_sasl_auth_clients = yes"

postconf -e "smtp_tls_security_level=may"
postconf -e "smtp_tls_loglevel=1"

postconf -e "smtpd_helo_required = yes"
postconf -e "smtpd_sasl_auth_enable = yes"
postconf -e "smtpd_sender_restrictions = reject_unknown_sender_domain, reject_unauthenticated_sender_login_mismatch, reject_known_sender_login_mismatch, permit_sasl_authenticated, permit"
postconf -e "smtpd_recipient_restrictions = permit_sasl_authenticated, reject_non_fqdn_helo_hostname, reject_non_fqdn_sender, reject_unknown_sender_domain, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination"
postconf -e "smtpd_relay_restrictions = permit_sasl_authenticated, reject_unauth_destination"
postconf -e "smtpd_sasl_authenticated_header = yes"

postconf -e "smtpd_use_tls = yes"
postconf -e "smtpd_tls_auth_only = yes"
postconf -e "smtpd_tls_loglevel = 1"
postconf -e "smtpd_tls_cert_file=/etc/postfix/certs/smtpd.pem"
postconf -e "smtpd_tls_key_file=/etc/postfix/certs/smtpd.pem"
postconf -e "smtpd_tls_CAfile=/etc/postfix/certs/smtpd.pem"

postconf -e "smtputf8_enable = yes"

postconf -M submission/inet="submission   inet   n   -   n   -   -   smtpd"
postconf -P "submission/inet/syslog_name=postfix/submission"
postconf -P "submission/inet/smtpd_tls_security_level=encrypt"
postconf -P "submission/inet/smtpd_sasl_auth_enable=yes"
postconf -P "submission/inet/smtpd_client_restrictions=permit_sasl_authenticated,reject_unauth_destination"
postconf -P "submission/inet/smtpd_recipient_restrictions=permit_sasl_authenticated,reject_unauth_destination"

echo password | saslpasswd2 -p -c -u domain.tld username

chown postfix /etc/sasl2/sasldb2

newaliases

exec "$@"


---
smtpd.pem
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----

---
supervisord.conf
[supervisord]
nodaemon = true
user = root

[program:rsyslog]
autorestart = true
command = /usr/sbin/rsyslogd -n
priority = 100
process_name = rsyslog
redirect_stderr = true
stdout_logfile = /dev/stdout
stdout_logfile_maxbytes = 0

[program:postfix]
autorestart = true
command = /usr/libexec/postfix/master -c /etc/postfix -d
process_name = postfix
redirect_stderr = true
stdout_logfile = /dev/stdout
stdout_logfile_maxbytes = 0


---
build.sh
!/bin/bash

docker buildx build -t postfix-alpine:latest .

docker save postfix-alpine:latest > postfix-alpine.tar


---
upload postfix-alpine.tar to chr

/container
add file=postfix-alpine.tar interface=veth-postfix hostname=mail.domain.tld dns=10.10.0.1 logging=yes

21 ตุลาคม 2566

secure docker with ufw

Docker and ufw
Uncomplicated Firewall (ufw) is a frontend that ships with Debian and Ubuntu, and it lets you manage firewall rules. Docker and ufw use iptables in ways that make them incompatible with each other.

When you publish a container's ports using Docker, traffic to and from that container gets diverted before it goes through the ufw firewall settings. Docker routes container traffic in the nat table, which means that packets are diverted before it reaches the INPUT and OUTPUT chains that ufw uses. Packets are routed before the firewall rules can be applied, effectively ignoring your firewall configuration.

Docker installs two custom iptables chains named DOCKER-USER and DOCKER, and it ensures that incoming packets are always checked by these two chains first. These chains are part of the FORWARD chain.


Solving UFW and Docker issues
This solution needs to modify only one UFW configuration file, all Docker configurations and options remain the default.

Modify the UFW configuration file /etc/ufw/after.rules and add the following rules at the end of the file:

# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN

-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN

-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP

COMMIT
# END UFW AND DOCKER



ref:
https://docs.docker.com/network/packet-filtering-firewalls/
https://github.com/chaifeng/ufw-docker

docker build bind9 (authoritative) alpine for mikrotik container

docker build bind9 (authoritative) alpine for mikrotik container


# create Dockerfile
FROM alpine:latest

RUN apk add --no-cache bind

RUN cp /etc/bind/named.conf.authoritative /etc/bind/named.conf

RUN sed -i "s/127.0.0.1/any/g" /etc/bind/named.conf

RUN mkdir -p /etc/bind/zone/ && chown named: /etc/bind/zone/

EXPOSE 53/tcp
EXPOSE 53/udp

CMD ["named", "-c", "/etc/bind/named.conf", "-g", "-u", "named"]

# build image
docker buildx build -t bind9-alpine:latest .

# save image
docker save bind9-alpine:latest > bind9-alpine.tar

# upload image to mikrotik
echo 'put bind9-alpine.tar' | sftp user@mikrotik


---
mikrotik

/container config
set registry-url=https://registry-1.docker.io

/interface bridge
add name=Docker

/ip address
add address=10.0.0.1/24 interface=Docker

/ip firewall nat
add chain=srcnat src-address=10.0.0.0/24 action=masquerade

/interface veth
add address=10.0.0.10/24 gateway=10.0.0.1 name=veth-bind9

/interface bridge port
add bridge=Docker interface=veth-bind9

/container mounts
add name=bind9 src=/bind9 dst=/etc/bind/

/container
add interface=veth-bind9 file=bind9-alpine.tar mounts=bind9 logging=yes

#start container (check container number with command print)
start 0

# shell to container number 0
shell 0

# append zone config to /etc/bind/named.conf
cat >> /etc/bind/named.conf << 'EOF'
zone "domain.tld" IN {
    type master;
    file "/etc/bind/zone/db.domain.tld";
};
EOF

# create zone file db.domain.tld
cat > /etc/bind/zone/db.domain.tld << 'EOF'
$TTL 3600
$ORIGIN domain.tld.
@       SOA     ns1.domain.tld. dns.domain.tld. (
                2023102100    ; Serial
                28800              ; Refresh
                7200                ; Retry
                604800            ; Expire
                7200 )              ; Minimum

                NS      ns1.domain.tld.

                MX    10 mail.thnic.co.th.

                A    10.0.0.10
www        A    10.0.0.10
ns1          A    10.0.0.10
EOF

# reconfig bind
rndc reconfig

15 ตุลาคม 2566

proxmox install openwrt 23.05

---
proxmox shell

curl -fsSL https://downloads.openwrt.org/releases/23.05.0/targets/x86/64/openwrt-23.05.0-x86-64-generic-ext4-combined.img.gz | gunzip

wget -O - https://downloads.openwrt.org/releases/23.05.0/targets/x86/64/openwrt-23.05.0-x86-64-generic-ext4-combined.img.gz | gunzip -c > openwrt.raw

qemu-img resize -f raw openwrt.raw 512M

mkdir /var/lib/vz/images/770

qemu-img convert -f raw -O qcow2 openwrt.raw /var/lib/vz/images/770/vm-770-disk-0.qcow2

chmod 540 /var/lib/vz/images/770/vm-770-disk-0.qcow2

qm create 770 --name OpenWrt --ostype l26 --cpu host --sockets 1 -cores 1 --memory 1024 --net0 virtio,bridge=vmbr1 --net1 virtio,bridge=vmbr0 --onboot yes  --virtio0 local:770/vm-770-disk-0.qcow2

qm start 770

---
OpenWrt console

passwd

ip a

uci set firewall.@zone[1].input='ACCEPT'

uci commit

service firewall reload

02 ตุลาคม 2566

Mikrotik set NTP Client

/system ntp client servers add address=0.pool.ntp.org
/system ntp client servers add address=clock.nectec.or.th
/system ntp client servers add address=203.159.70.33

/system ntp client set enabled=yes

nginx docker with certbot docker (Let's Encrypt)

nginx docker with certbot docker (Let's Encrypt)

docker-compose.yaml
    image: nginx:latest
    container_name: nginx
    volumes:
      - ./tmp-acme_challenge:/tmp/acme_challenge
      - ./etc-letsencrypt:/etc/letsencrypt:ro
      - ./default.conf:/etc/nginx/conf.d/default.conf

default.conf

    location ^~ /.well-known/acme-challenge/ {
        allow all;
        root /tmp/acme_challenge;
    }


    ssl_certificate /etc/letsencrypt/live/domain.tld/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.tld/privkey.pem;

# issue Let's Encrypt
docker run -it --rm -v /home/user/docker/nginx/etc-letsencrypt:/etc/letsencrypt -v /home/user/docker/nginx/tmp-acme_challenge:/tmp/acme_challenge certbot/certbot certonly  --expand --webroot -w /tmp/acme_challenge --text --agree-tos --no-eff-email --email me@domain.tld --verbose --keep-until-expiring --preferred-challenges=http -d domain.tld -d www.domain.tld
        

# renew cert
docker run -it --rm -v /home/user/docker/nginx/etc-letsencrypt:/etc/letsencrypt -v /home/user/docker/nginx/tmp-acme_challenge:/tmp/acme_challenge certbot/certbot renew

reference
- https://eff-certbot.readthedocs.io/en/stable/install.html#running-with-docker

reference
- https://eff-certbot.readthedocs.io/en/stable/install.html#running-with-docker