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 \
bash build-essential python3-dev cython3 libffi-dev libssl-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 \
&& apt-get clean \
&& 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 = {
"common_name": common_name,
"status": "signed",
"disabled": False,
"serial_number": "%x" % builder.serial_number,
"signed": builder.begin_date,
"expires": builder.end_date,

View File

@ -14,7 +14,6 @@ import logging
import os
import pymongo
import signal
import socket
import sys
import pytz
from asn1crypto import pem, x509
@ -107,6 +106,36 @@ def pinecone_sign(common_name, overwrite, 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.option("--reason", "-r", default="key_compromise",
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")
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
distinguished_name = cn_to_dn(const.AUTHORITY_COMMON_NAME)
@ -432,9 +357,22 @@ def pinecone_provision():
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?
click.echo("Finished starting up")
sleep(86400)
sleep(999999999)
@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_provision)
entry_point.add_command(pinecone_session)
entry_point.add_command(pinecone_disable)
entry_point.add_command(pinecone_enable)
if __name__ == "__main__":
entry_point()

View File

@ -30,9 +30,8 @@ IMAGE_BUILDER_PROFILES = []
SERVICE_PROTOCOLS = ["ikev2", "openvpn"]
MONGO_URI = os.getenv("MONGO_URI")
REPLICAS = os.getenv("REPLICAS")
REPLICAS = [j for j in os.getenv("REPLICAS", "").split(",") if j]
if REPLICAS:
REPLICAS = REPLICAS.split(",")
if MONGO_URI:
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]))
@ -147,7 +146,6 @@ CRL_SUBNETS = getenv_subnets("AUTH_CRL_SUBNETS", "0.0.0.0/0 ::/0")
OVERWRITE_SUBNETS = getenv_subnets("AUTH_OVERWRITE_SUBNETS", "")
MACHINE_ENROLLMENT_SUBNETS = getenv_subnets("AUTH_MACHINE_ENROLLMENT_SUBNETS", "0.0.0.0/0 ::/0")
KERBEROS_SUBNETS = getenv_subnets("AUTH_KERBEROS_SUBNETS", "")
PROMETHEUS_SUBNETS = getenv_subnets("PROMETHEUS_SUBNETS", "")
BOOTSTRAP_TEMPLATE = ""
USER_ENROLLMENT_ALLOWED = True
@ -176,6 +174,3 @@ SESSION_COOKIE = "sha512brownies"
SESSION_AGE = 3600
SECRET_STORAGE = getenv_in("SECRET_STORAGE", "fs", "db")
DISABLE_FIREWALL = os.getenv("DISABLE_FIREWALL")
DISABLE_MASQUERADE = os.getenv("DISABLE_MASQUERADE")