Move firewall to external service

This commit is contained in:
Lauri Võsandi 2021-06-15 23:49:22 +03:00
parent 6351f8a231
commit b935a26961
4 changed files with 49 additions and 113 deletions

View File

@ -9,7 +9,7 @@ RUN echo force-unsafe-io > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup \
&& apt-get install -y -qq \ && apt-get install -y -qq \
bash build-essential python3-dev cython3 libffi-dev libssl-dev \ bash build-essential python3-dev cython3 libffi-dev libssl-dev \
libkrb5-dev ldap-utils libsasl2-modules-gssapi-mit libsasl2-dev libldap2-dev \ libkrb5-dev ldap-utils libsasl2-modules-gssapi-mit libsasl2-dev libldap2-dev \
python python3-pip python3-cffi iptables ipset \ python python3-pip python3-cffi \
libncurses5-dev gawk wget unzip git rsync \ libncurses5-dev gawk wget unzip git rsync \
&& apt-get clean \ && apt-get clean \
&& rm /etc/dpkg/dpkg.cfg.d/docker-apt-speedup && rm /etc/dpkg/dpkg.cfg.d/docker-apt-speedup

View File

@ -411,6 +411,7 @@ def sign(profile, skip_notify=False, overwrite=False, signer=None, namespace=con
d = { d = {
"common_name": common_name, "common_name": common_name,
"status": "signed", "status": "signed",
"disabled": False,
"serial_number": "%x" % builder.serial_number, "serial_number": "%x" % builder.serial_number,
"signed": builder.begin_date, "signed": builder.begin_date,
"expires": builder.end_date, "expires": builder.end_date,

View File

@ -14,7 +14,6 @@ import logging
import os import os
import pymongo import pymongo
import signal import signal
import socket
import sys import sys
import pytz import pytz
from asn1crypto import pem, x509 from asn1crypto import pem, x509
@ -107,6 +106,36 @@ def pinecone_sign(common_name, overwrite, profile):
authority.sign(common_name, overwrite=overwrite, profile=profile) authority.sign(common_name, overwrite=overwrite, profile=profile)
@click.command("disable", help="Disable client node or gateway replica temporarily")
@click.argument("common_name")
def pinecone_disable(common_name):
from pinecrypt.server import db
result = db.certificates.update_one({
"common_name": common_name
}, {
"$set": {
"disabled": datetime.utcnow()
}
})
if result.matched_count != 1:
raise click.ClickException("Invalid common name")
@click.command("enable", help="Enable client node or gateway replica")
@click.argument("common_name")
def pinecone_enable(common_name):
from pinecrypt.server import db
result = db.certificates.update_one({
"common_name": common_name
}, {
"$set": {
"disabled": False
}
})
if result.matched_count != 1:
raise click.ClickException("Invalid common name")
@click.command("revoke", help="Revoke certificate") @click.command("revoke", help="Revoke certificate")
@click.option("--reason", "-r", default="key_compromise", @click.option("--reason", "-r", default="key_compromise",
help="Revocation reason, one of: key_compromise affiliation_changed superseded cessation_of_operation privilege_withdrawn") help="Revocation reason, one of: key_compromise affiliation_changed superseded cessation_of_operation privilege_withdrawn")
@ -185,110 +214,6 @@ def pinecone_serve_builder():
@click.command("provision", help="Provision keys") @click.command("provision", help="Provision keys")
def pinecone_provision(): def pinecone_provision():
default_policy = "REJECT" if const.DEBUG else "DROP"
click.echo("Setting up firewall rules")
if const.REPLICAS:
# TODO: atomic update with `ipset restore`
for replica in const.REPLICAS:
for fam, _, _, _, addrs in socket.getaddrinfo(replica, None):
if fam == 10:
os.system("ipset add ipset6-mongo-replicas %s" % addrs[0])
elif fam == 2:
os.system("ipset add ipset4-mongo-replicas %s" % addrs[0])
os.system("ipset create -exist -quiet ipset4-client-ingress hash:ip timeout 3600 counters")
os.system("ipset create -exist -quiet ipset6-client-ingress hash:ip family inet6 timeout 3600 counters")
os.system("ipset create -exist -quiet ipset4-client-egress hash:ip timeout 3600 counters")
os.system("ipset create -exist -quiet ipset6-client-egress hash:ip family inet6 timeout 3600 counters")
os.system("ipset create -exist -quiet ipset4-mongo-replicas hash:ip")
os.system("ipset create -exist -quiet ipset6-mongo-replicas hash:ip family inet6")
os.system("ipset create -exist -quiet ipset4-prometheus-subnets hash:net")
os.system("ipset create -exist -quiet ipset6-prometheus-subnets hash:net family inet6")
for subnet in const.PROMETHEUS_SUBNETS:
os.system("ipset add -exist -quiet ipset%d-prometheus-subnets %s" % (subnet.version, subnet))
def g():
yield "*filter"
yield ":INBOUND_BLOCKED - [0:0]"
yield "-A INBOUND_BLOCKED -j %s -m comment --comment \"Default policy\"" % default_policy
yield ":OUTBOUND_CLIENT - [0:0]"
yield "-A OUTBOUND_CLIENT -m set ! --match-set ipset4-client-ingress dst -j SET --add-set ipset4-client-ingress dst"
yield "-A OUTBOUND_CLIENT -j ACCEPT"
yield ":INBOUND_CLIENT - [0:0]"
yield "-A INBOUND_CLIENT -m set ! --match-set ipset4-client-ingress src -j SET --add-set ipset4-client-ingress src"
yield "-A INBOUND_CLIENT -j ACCEPT"
yield ":INPUT DROP [0:0]"
yield "-A INPUT -i lo -j ACCEPT -m comment --comment \"Allow loopback\""
yield "-A INPUT -p icmp -j ACCEPT -m comment --comment \"Allow ping\""
yield "-A INPUT -p esp -j ACCEPT -m comment --comment \"Allow ESP traffic\""
yield "-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT -m comment --comment \"Allow returning packets\""
yield "-A INPUT -p tcp --dport 22 -j ACCEPT -m comment --comment \"Allow SSH\""
yield "-A INPUT -p udp --dport 53 -j ACCEPT -m comment --comment \"Allow GoreDNS over UDP\""
yield "-A INPUT -p tcp --dport 53 -j ACCEPT -m comment --comment \"Allow GoreDNS over TCP\""
yield "-A INPUT -p tcp --dport 80 -j ACCEPT -m comment --comment \"Allow insecure HTTP\""
yield "-A INPUT -p tcp --dport 443 -j ACCEPT -m comment --comment \"Allow HTTPS / OpenVPN TCP\""
yield "-A INPUT -p tcp --dport 8443 -j ACCEPT -m comment --comment \"Allow mutually authenticated HTTPS\""
yield "-A INPUT -p udp --dport 1194 -j ACCEPT -m comment --comment \"Allow OpenVPN UDP\""
yield "-A INPUT -p udp --dport 500 -j ACCEPT -m comment --comment \"Allow IPsec IKE\""
yield "-A INPUT -p udp --dport 4500 -j ACCEPT -m comment --comment \"Allow IPsec NAT traversal\""
if const.REPLICAS:
yield "-A INPUT -p tcp --dport 27017 -j ACCEPT -m set --match-set ipset4-mongo-replicas src -m comment --comment \"Allow MongoDB internode\""
yield "-A INPUT -p tcp --dport 9090 -j ACCEPT -m set --match-set ipset4-prometheus-subnets src -m comment --comment \"Allow Prometheus\""
yield "-A INPUT -j INBOUND_BLOCKED"
yield ":FORWARD DROP [0:0]"
yield "-A FORWARD -i tun0 -j INBOUND_CLIENT -m comment --comment \"Inbound traffic from OpenVPN UDP clients\""
yield "-A FORWARD -i tun1 -j INBOUND_CLIENT -m comment --comment \"Inbound traffic from OpenVPN TCP clients\""
yield "-A FORWARD -m policy --dir in --pol ipsec -j INBOUND_CLIENT -m comment --comment \"Inbound traffic from IPSec clients\""
yield "-A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j OUTBOUND_CLIENT -m comment --comment \"Outbound traffic to clients\""
yield "-A FORWARD -j %s -m comment --comment \"Default policy\"" % default_policy
yield ":OUTPUT DROP [0:0]"
yield "-A OUTPUT -j ACCEPT"
yield "COMMIT"
yield "*nat"
yield ":PREROUTING ACCEPT [0:0]"
yield ":INPUT ACCEPT [0:0]"
yield ":OUTPUT ACCEPT [0:0]"
yield ":POSTROUTING ACCEPT [0:0]"
if not const.DISABLE_MASQUERADE:
yield "-A POSTROUTING -j MASQUERADE"
yield "COMMIT"
with open("/tmp/rules4", "w") as fh:
for line in g():
fh.write(line)
fh.write("\n")
if not const.DISABLE_FIREWALL:
os.system("iptables-restore < /tmp/rules4")
os.system("sed -e 's/ipset4/ipset6/g' -e 's/p icmp/p ipv6-icmp/g' /tmp/rules4 > /tmp/rules6")
os.system("ip6tables-restore < /tmp/rules6")
os.system("sysctl -w net.ipv6.conf.all.forwarding=1")
os.system("sysctl -w net.ipv6.conf.default.forwarding=1")
os.system("sysctl -w net.ipv4.ip_forward=1")
if const.REPLICAS:
click.echo("Provisioning MongoDB replicaset")
# WTF https://github.com/docker-library/mongo/issues/339
c = pymongo.MongoClient("localhost", 27017)
config = {"_id": "rs0", "members": [
{"_id": index, "host": "%s:27017" % hostname} for index, hostname in enumerate(const.REPLICAS)]}
print("Provisioning MongoDB replicaset: %s" % repr(config))
try:
c.admin.command("replSetInitiate", config)
except pymongo.errors.OperationFailure:
print("Looks like it's already initialized")
pass
# Expand variables # Expand variables
distinguished_name = cn_to_dn(const.AUTHORITY_COMMON_NAME) distinguished_name = cn_to_dn(const.AUTHORITY_COMMON_NAME)
@ -432,9 +357,22 @@ def pinecone_provision():
const.SELF_KEY_PATH const.SELF_KEY_PATH
)) ))
if const.REPLICAS:
click.echo("Provisioning MongoDB replicaset")
# WTF https://github.com/docker-library/mongo/issues/339
c = pymongo.MongoClient("localhost", 27017)
config = {"_id": "rs0", "members": [
{"_id": index, "host": "%s:27017" % hostname} for index, hostname in enumerate(const.REPLICAS)]}
print("Provisioning MongoDB replicaset: %s" % repr(config))
try:
c.admin.command("replSetInitiate", config)
except pymongo.errors.OperationFailure:
print("Looks like it's already initialized")
pass
# TODO: use this task to send notification emails maybe? # TODO: use this task to send notification emails maybe?
click.echo("Finished starting up") click.echo("Finished starting up")
sleep(86400) sleep(999999999)
@click.command("backend", help="Serve main backend") @click.command("backend", help="Serve main backend")
@ -572,6 +510,8 @@ entry_point.add_command(pinecone_test)
entry_point.add_command(pinecone_log) entry_point.add_command(pinecone_log)
entry_point.add_command(pinecone_provision) entry_point.add_command(pinecone_provision)
entry_point.add_command(pinecone_session) entry_point.add_command(pinecone_session)
entry_point.add_command(pinecone_disable)
entry_point.add_command(pinecone_enable)
if __name__ == "__main__": if __name__ == "__main__":
entry_point() entry_point()

View File

@ -30,9 +30,8 @@ IMAGE_BUILDER_PROFILES = []
SERVICE_PROTOCOLS = ["ikev2", "openvpn"] SERVICE_PROTOCOLS = ["ikev2", "openvpn"]
MONGO_URI = os.getenv("MONGO_URI") MONGO_URI = os.getenv("MONGO_URI")
REPLICAS = os.getenv("REPLICAS") REPLICAS = [j for j in os.getenv("REPLICAS", "").split(",") if j]
if REPLICAS: if REPLICAS:
REPLICAS = REPLICAS.split(",")
if MONGO_URI: if MONGO_URI:
raise ValueError("Simultanously specifying MONGO_URI and REPLICAS doesn't make sense") raise ValueError("Simultanously specifying MONGO_URI and REPLICAS doesn't make sense")
MONGO_URI = "mongodb://%s/default?replicaSet=rs0" % (",".join(["%s:27017" % j for j in REPLICAS])) MONGO_URI = "mongodb://%s/default?replicaSet=rs0" % (",".join(["%s:27017" % j for j in REPLICAS]))
@ -147,7 +146,6 @@ CRL_SUBNETS = getenv_subnets("AUTH_CRL_SUBNETS", "0.0.0.0/0 ::/0")
OVERWRITE_SUBNETS = getenv_subnets("AUTH_OVERWRITE_SUBNETS", "") OVERWRITE_SUBNETS = getenv_subnets("AUTH_OVERWRITE_SUBNETS", "")
MACHINE_ENROLLMENT_SUBNETS = getenv_subnets("AUTH_MACHINE_ENROLLMENT_SUBNETS", "0.0.0.0/0 ::/0") MACHINE_ENROLLMENT_SUBNETS = getenv_subnets("AUTH_MACHINE_ENROLLMENT_SUBNETS", "0.0.0.0/0 ::/0")
KERBEROS_SUBNETS = getenv_subnets("AUTH_KERBEROS_SUBNETS", "") KERBEROS_SUBNETS = getenv_subnets("AUTH_KERBEROS_SUBNETS", "")
PROMETHEUS_SUBNETS = getenv_subnets("PROMETHEUS_SUBNETS", "")
BOOTSTRAP_TEMPLATE = "" BOOTSTRAP_TEMPLATE = ""
USER_ENROLLMENT_ALLOWED = True USER_ENROLLMENT_ALLOWED = True
@ -176,6 +174,3 @@ SESSION_COOKIE = "sha512brownies"
SESSION_AGE = 3600 SESSION_AGE = 3600
SECRET_STORAGE = getenv_in("SECRET_STORAGE", "fs", "db") SECRET_STORAGE = getenv_in("SECRET_STORAGE", "fs", "db")
DISABLE_FIREWALL = os.getenv("DISABLE_FIREWALL")
DISABLE_MASQUERADE = os.getenv("DISABLE_MASQUERADE")