Commit 29bbf837 by Kálmán Viktor

firewall: fixing trivial pep8 errors

parent 935d2a02
# -*- coding: utf8 -*-
from django.contrib import admin
from firewall.models import *
from firewall.models import (Rule, Host, Vlan, Group, VlanGroup, Firewall,
Domain, Record, Blacklist)
from django import contrib
class RuleInline(contrib.admin.TabularInline):
model = Rule
class RecordInline(contrib.admin.TabularInline):
model = Record
class HostAdmin(admin.ModelAdmin):
list_display = ('hostname', 'vlan', 'ipv4', 'ipv6', 'pub_ipv4', 'mac',
'shared_ip', 'owner', 'description', 'reverse', 'list_groups')
'shared_ip', 'owner', 'description', 'reverse',
'list_groups')
ordering = ('hostname', )
list_filter = ('owner', 'vlan', 'groups')
search_fields = ('hostname', 'description', 'ipv4', 'ipv6', 'mac')
......@@ -26,20 +30,24 @@ class HostAdmin(admin.ModelAdmin):
names = [group.name for group in instance.groups.all()]
return u', '.join(names)
class HostInline(contrib.admin.TabularInline):
model = Host
fields = ('hostname', 'ipv4', 'ipv6', 'pub_ipv4', 'mac', 'shared_ip',
'owner', 'reverse')
class VlanAdmin(admin.ModelAdmin):
list_display = ('vid', 'name', 'ipv4', 'net_ipv4', 'ipv6', 'net_ipv6',
'description', 'domain', 'snat_ip', )
ordering = ('vid', )
inlines = (RuleInline, )
class RuleAdmin(admin.ModelAdmin):
list_display = ('r_type', 'color_desc', 'owner', 'extra', 'direction',
'accept', 'proto', 'sport', 'dport', 'nat', 'nat_dport', 'used_in')
'accept', 'proto', 'sport', 'dport', 'nat',
'nat_dport', 'used_in')
list_filter = ('r_type', 'vlan', 'owner', 'direction', 'accept',
'proto', 'nat')
......@@ -81,16 +89,20 @@ class RuleAdmin(admin.ModelAdmin):
class AliasAdmin(admin.ModelAdmin):
list_display = ('alias', 'host')
class GroupAdmin(admin.ModelAdmin):
list_display = ('name', 'owner', 'description')
inlines = (RuleInline, )
class FirewallAdmin(admin.ModelAdmin):
inlines = (RuleInline, )
class DomainAdmin(admin.ModelAdmin):
list_display = ('name', 'owner')
class RecordAdmin(admin.ModelAdmin):
list_display = ('name_', 'type', 'address_', 'ttl', 'host', 'owner')
......@@ -104,6 +116,7 @@ class RecordAdmin(admin.ModelAdmin):
a = instance.get_data()
return a['name'] if a else None
class BlacklistAdmin(admin.ModelAdmin):
list_display = ('ipv4', 'reason', 'created_at', 'modified_at')
......@@ -116,4 +129,3 @@ admin.site.register(Firewall, FirewallAdmin)
admin.site.register(Domain, DomainAdmin)
admin.site.register(Record, RecordAdmin)
admin.site.register(Blacklist, BlacklistAdmin)
......@@ -6,12 +6,14 @@ from django.utils.ipv6 import is_valid_ipv6_address
from south.modelsinspector import add_introspection_rules
import re
mac_re = re.compile(r'^([0-9a-fA-F]{2}(:|$)){6}$')
alfanum_re = re.compile(r'^[A-Za-z0-9_-]+$')
domain_re = re.compile(r'^([A-Za-z0-9_-]\.?)+$')
ipv4_re = re.compile('^[0-9]+\.([0-9]+)\.([0-9]+)\.([0-9]+)$')
reverse_domain_re = re.compile(r'^(%\([abcd]\)d|[a-z0-9.-])+$')
class MACAddressFormField(fields.RegexField):
default_error_messages = {
'invalid': _(u'Enter a valid MAC address.'),
......@@ -20,8 +22,10 @@ class MACAddressFormField(fields.RegexField):
def __init__(self, *args, **kwargs):
super(MACAddressFormField, self).__init__(mac_re, *args, **kwargs)
class MACAddressField(models.Field):
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 17
super(MACAddressField, self).__init__(*args, **kwargs)
......@@ -35,44 +39,53 @@ class MACAddressField(models.Field):
return super(MACAddressField, self).formfield(**defaults)
add_introspection_rules([], ["firewall\.fields\.MACAddressField"])
def val_alfanum(value):
"""Validate whether the parameter is a valid alphanumeric value."""
if not alfanum_re.match(value):
raise ValidationError(_(u'%s - only letters, numbers, underscores '
'and hyphens are allowed!') % value)
def is_valid_domain(value):
"""Check whether the parameter is a valid domain name."""
return domain_re.match(value) is not None
def val_domain(value):
"""Validate whether the parameter is a valid domin name."""
if not is_valid_domain(value):
raise ValidationError(_(u'%s - invalid domain name') % value)
def is_valid_reverse_domain(value):
"""Check whether the parameter is a valid reverse domain name."""
return reverse_domain_re.match(value) is not None
def val_reverse_domain(value):
"""Validate whether the parameter is a valid reverse domain name."""
if not is_valid_reverse_domain(value):
raise ValidationError(u'%s - invalid reverse domain name' % value)
def is_valid_ipv4_address(value):
"""Check whether the parameter is a valid IPv4 address."""
return ipv4_re.match(value) is not None
def val_ipv4(value):
"""Validate whether the parameter is a valid IPv4 address."""
if not is_valid_ipv4_address(value):
raise ValidationError(_(u'%s - not an IPv4 address') % value)
def val_ipv6(value):
"""Validate whether the parameter is a valid IPv6 address."""
if not is_valid_ipv6_address(value):
raise ValidationError(_(u'%s - not an IPv6 address') % value)
def val_mx(value):
"""Validate whether the parameter is a valid MX address definition.
......@@ -84,6 +97,7 @@ def val_mx(value):
raise ValidationError(_("Bad MX address format. "
"Should be: <priority>:<hostname>"))
def ipv4_2_ipv6(ipv4):
"""Convert IPv4 address string to IPv6 address string."""
val_ipv4(ipv4)
......
from django.contrib import auth
from firewall import models
import os
import django.conf
import subprocess
import re
import json
from datetime import datetime, timedelta
from django.db.models import Q
settings = django.conf.settings.FIREWALL_SETTINGS
class Firewall:
IPV6=False
IPV6 = False
RULES = None
RULES_NAT = []
vlans = None
......@@ -35,7 +34,6 @@ class Firewall:
retval = '-p %s ' % rule.proto
return retval
def iptables(self, s):
"""Append rule to filter table."""
self.RULES.append(s)
......@@ -70,12 +68,13 @@ class Firewall:
action = 'LOG_DROP'
if rule.direction == '1': # going TO host
self.iptables('-A %s_%s -d %s %s %s -g %s' % (vlan,
host.vlan, ipaddr, dport_sport, rule.extra, action))
self.iptables('-A %s_%s -d %s %s %s -g %s' %
(vlan, host.vlan, ipaddr, dport_sport,
rule.extra, action))
else:
self.iptables('-A %s_%s -s %s %s %s -g %s' % (host.vlan,
vlan, ipaddr, dport_sport, rule.extra, action))
self.iptables('-A %s_%s -s %s %s %s -g %s' %
(host.vlan, vlan, ipaddr, dport_sport,
rule.extra, action))
def fw2vlan(self, rule):
if not rule.foreign_network:
......@@ -109,12 +108,12 @@ class Firewall:
action = 'LOG_DROP'
if rule.direction == '1': # going TO host
self.iptables('-A %s_%s %s %s -g %s' % (vlan, l_vlan,
dport_sport, rule.extra, action))
self.iptables('-A %s_%s %s %s -g %s' %
(vlan, l_vlan, dport_sport, rule.extra, action))
else:
self.iptables('-A %s_%s %s %s -g %s' % (l_vlan, vlan,
dport_sport, rule.extra, action))
dport_sport,
rule.extra, action))
def prerun(self):
self.iptables('*filter')
......@@ -137,7 +136,8 @@ class Firewall:
self.iptables('-N PUB_OUT')
self.iptables('-A FORWARD -m set --match-set blacklist src,dst -j DROP')
self.iptables('-A FORWARD -m set --match-set blacklist src,dst'
'-j DROP')
self.iptables('-A FORWARD -m state --state INVALID -g LOG_DROP')
self.iptables('-A FORWARD -m state --state ESTABLISHED,RELATED '
'-j ACCEPT')
......@@ -155,7 +155,6 @@ class Firewall:
self.iptables('-A OUTPUT -m state --state ESTABLISHED,RELATED '
'-j ACCEPT')
def postrun(self):
self.iptables('-A PUB_OUT -s 152.66.243.160/27 -p tcp --dport 25 '
'-j LOG_ACC')
......@@ -171,9 +170,6 @@ class Firewall:
self.iptables('-A OUTPUT -g LOG_DROP')
self.iptables('COMMIT')
def ipt_nat(self):
self.iptablesnat('*nat')
self.iptablesnat(':PREROUTING ACCEPT [0:0]')
......@@ -187,34 +183,37 @@ class Firewall:
dport_sport = self.dportsport(rule, False)
if host.vlan.snat_ip:
self.iptablesnat('-A PREROUTING -d %s %s %s -j DNAT '
'--to-destination %s:%s' % (host.pub_ipv4,
dport_sport, rule.extra, host.ipv4,
rule.nat_dport))
'--to-destination %s:%s' %
(host.pub_ipv4, dport_sport, rule.extra,
host.ipv4, rule.nat_dport))
# rules for machines with dedicated public IP
for host in self.hosts.exclude(shared_ip=True):
if host.pub_ipv4:
self.iptablesnat('-A PREROUTING -d %s -j DNAT '
'--to-destination %s' % (host.pub_ipv4, host.ipv4))
'--to-destination %s' %
(host.pub_ipv4, host.ipv4))
self.iptablesnat('-A POSTROUTING -s %s -j SNAT '
'--to-source %s' % (host.ipv4, host.pub_ipv4))
'--to-source %s' %
(host.ipv4, host.pub_ipv4))
# default NAT rules for VLANs
for s_vlan in self.vlans:
if s_vlan.snat_ip:
for d_vlan in s_vlan.snat_to.all():
self.iptablesnat('-A POSTROUTING -s %s -o %s -j SNAT '
'--to-source %s' % (s_vlan.net_ipv4(),
d_vlan.interface, s_vlan.snat_ip))
'--to-source %s' %
(s_vlan.net_ipv4(), d_vlan.interface,
s_vlan.snat_ip))
# hard-wired rules
self.iptablesnat('-A POSTROUTING -s 10.5.0.0/16 -o vlan0003 -j SNAT '
'--to-source 10.3.255.254') # man elerheto legyen
self.iptablesnat('-A POSTROUTING -o vlan0008 -j SNAT '
'--to-source 10.0.0.247') # wolf network for printing
self.iptablesnat('-A POSTROUTING -s 10.3.0.0/16 -p udp --dport 53 -o vlan0002 -j SNAT '
'--to-source %s' % self.pub.ipv4) # kulonben nem megy a dns man-ban
self.iptablesnat('-A POSTROUTING -s 10.3.0.0/16 -p udp --dport 53'
'-o vlan0002 -j SNAT ''--to-source %s' %
self.pub.ipv4) # kulonben nem megy a dns man-ban
self.iptablesnat('COMMIT')
......@@ -234,7 +233,8 @@ class Firewall:
for d_vlan in self.vlans:
self.iptables('-N %s_%s' % (s_vlan, d_vlan))
self.iptables('-A FORWARD -i %s -o %s -g %s_%s' %
(s_vlan.interface, d_vlan.interface, s_vlan, d_vlan))
(s_vlan.interface, d_vlan.interface, s_vlan,
d_vlan))
# hosts' rules
for i_vlan in self.vlans:
......@@ -263,8 +263,8 @@ class Firewall:
self.RULES = [x.replace('icmp', 'icmpv6') for x in self.RULES]
def __init__(self, IPV6=False):
self.RULES=[]
self.RULES_NAT=[]
self.RULES = []
self.RULES_NAT = []
self.IPV6 = IPV6
self.vlans = models.Vlan.objects.all()
self.hosts = models.Host.objects.all()
......@@ -277,21 +277,23 @@ class Firewall:
def reload(self):
if self.IPV6:
process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'/usr/bin/sudo', '/sbin/ip6tables-restore', '-c'],
'/usr/bin/sudo',
'/sbin/ip6tables-restore', '-c'],
shell=False, stdin=subprocess.PIPE)
process.communicate('\n'.join(self.RULES) + '\n')
else:
process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'/usr/bin/sudo', '/sbin/iptables-restore', '-c'],
'/usr/bin/sudo',
'/sbin/iptables-restore', '-c'],
shell=False, stdin=subprocess.PIPE)
process.communicate('\n'.join(self.RULES) + '\n' +
'\n'.join(self.RULES_NAT) + '\n')
def get(self):
if self.IPV6:
return { 'filter': self.RULES, }
return {'filter': self.RULES, }
else:
return { 'filter': self.RULES, 'nat': self.RULES_NAT }
return {'filter': self.RULES, 'nat': self.RULES_NAT}
def show(self):
if self.IPV6:
......@@ -300,9 +302,12 @@ class Firewall:
return ('\n'.join(self.RULES) + '\n' +
'\n'.join(self.RULES_NAT) + '\n')
def ipset():
week = datetime.now()-timedelta(days=2)
return models.Blacklist.objects.filter(Q(type='tempban', modified_at__gte=week) | Q(type='permban')).values('ipv4', 'reason')
week = datetime.now() - timedelta(days=2)
filter_ban = (Q(type='tempban', modified_at__gte=week) |
Q(type='permban')).values('ipv4', 'reason')
return models.Blacklist.objects.filter(filter_ban)
def ipv6_to_octal(ipv6):
......@@ -319,6 +324,7 @@ def ipv6_to_octal(ipv6):
octets.append(int(part[2:], 16))
return '\\' + '\\'.join(['%03o' % x for x in octets])
def ipv4_to_arpa(ipv4, cname=False):
m2 = re.search(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$', ipv4)
if cname:
......@@ -328,6 +334,7 @@ def ipv4_to_arpa(ipv4, cname=False):
return ('%s.%s.%s.%s.in-addr.arpa' %
(m2.group(4), m2.group(3), m2.group(2), m2.group(1)))
def ipv6_to_arpa(ipv6):
while len(ipv6.split(':')) < 8:
ipv6 = ipv6.replace('::', ':::')
......@@ -355,11 +362,11 @@ def ipv6_to_arpa(ipv6):
def dns():
vlans = models.Vlan.objects.all()
regex = re.compile(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$')
# regex = re.compile(r'^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$')
DNS = []
for i_vlan in vlans:
m = regex.search(i_vlan.net4)
# m = regex.search(i_vlan.net4)
rev = i_vlan.reverse_domain
for i_host in i_vlan.host_set.all():
......@@ -372,8 +379,8 @@ def dns():
# ipv4
if i_host.ipv4:
DNS.append("^%s:%s:%s" % (
(rev % { 'a': int(i[0]), 'b': int(i[1]), 'c': int(i[2]),
'd': int(i[3]) }),
(rev % {'a': int(i[0]), 'b': int(i[1]), 'c': int(i[2]),
'd': int(i[3])}),
reverse, models.settings['dns_ttl']))
# ipv6
......@@ -382,16 +389,17 @@ def dns():
reverse, models.settings['dns_ttl']))
for domain in models.Domain.objects.all():
DNS.append("Z%s:%s:support.ik.bme.hu::::::%s" % (domain.name,
settings['dns_hostname'], models.settings['dns_ttl']))
DNS.append("Z%s:%s:support.ik.bme.hu::::::%s" %
(domain.name, settings['dns_hostname'],
models.settings['dns_ttl']))
for r in models.Record.objects.all():
d = r.get_data()
if d['type'] == 'A':
DNS.append("+%s:%s:%s" % (d['name'], d['address'], d['ttl']))
elif d['type'] == 'AAAA':
DNS.append(":%s:28:%s:%s" % (d['name'],
ipv6_to_octal(d['address']), d['ttl']))
DNS.append(":%s:28:%s:%s" %
(d['name'], ipv6_to_octal(d['address']), d['ttl']))
elif d['type'] == 'NS':
DNS.append("&%s::%s:%s" % (d['name'], d['address'], d['ttl']))
elif d['type'] == 'CNAME':
......@@ -406,8 +414,9 @@ def dns():
return DNS
process = subprocess.Popen(['/usr/bin/ssh', 'tinydns@%s' %
settings['dns_hostname']], shell=False, stdin=subprocess.PIPE)
process.communicate("\n".join(DNS)+"\n")
settings['dns_hostname']],
shell=False, stdin=subprocess.PIPE)
process.communicate("\n".join(DNS) + "\n")
# print "\n".join(DNS)+"\n"
......@@ -420,6 +429,7 @@ def prefix_to_mask(prefix):
t[i] = 256 - (2 ** ((i + 1) * 8 - prefix))
return ".".join([str(i) for i in t])
def dhcp():
vlans = models.Vlan.objects.all()
regex = re.compile(r'^([0-9]+)\.([0-9]+)\.[0-9]+\.[0-9]+\s+'
......@@ -432,7 +442,7 @@ def dhcp():
if(i_vlan.dhcp_pool):
m = regex.search(i_vlan.dhcp_pool)
if(m or i_vlan.dhcp_pool == "manual"):
DHCP.append ('''
DHCP.append('''
# %(name)s - %(interface)s
subnet %(net)s netmask %(netmask)s {
%(extra)s;
......@@ -459,7 +469,7 @@ def dhcp():
})
for i_host in i_vlan.host_set.all():
DHCP.append ('''
DHCP.append('''
host %(hostname)s {
hardware ethernet %(mac)s;
fixed-address %(ipv4)s;
......@@ -472,22 +482,7 @@ def dhcp():
return DHCP
process = subprocess.Popen(['/usr/bin/ssh', 'fw2',
'cat > /tools/dhcp3/dhcpd.conf.generated;'
'sudo /etc/init.d/isc-dhcp-server restart'], shell=False,
stdin=subprocess.PIPE)
'sudo /etc/init.d/isc-dhcp-server restart'],
shell=False, stdin=subprocess.PIPE)
# print "\n".join(DHCP)+"\n"
process.communicate("\n".join(DHCP)+"\n")
'''
i=2
for mac, name, ipend in [("18:a9:05:64:19:aa", "mega6", 16), ("00:1e:0b:e9:79:1e", "blade1", 21), ("00:22:64:9c:fd:34", "blade2", 22), ("00:1e:0b:ec:65:46", "blade3", 23), ("b4:b5:2f:61:d2:5a", "cloud-man", 1)]:
h1 = models.Host(hostname= name, vlan=models.Vlan.objects.get(vid=3), mac=mac, ipv4="10.3.1.%d" % ipend, ipv6="2001:738:2001:4031:3:1:%d:0" % ipend, owner=auth.models.User.objects.get(username="bd"))
try:
h1.save()
h1.groups.add(models.Group.objects.get(name="netezhet manbol"))
h1.save()
# i = i + 1
except:
print "nemok %s" % name
'''
process.communicate("\n".join(DHCP) + "\n")
......@@ -4,7 +4,9 @@ from django.contrib.auth.models import User
from django.db import models
from django.forms import ValidationError
from django.utils.translation import ugettext_lazy as _
from firewall.fields import *
from firewall.fields import (MACAddressField, val_alfanum, val_reverse_domain,
val_domain, val_ipv4, val_ipv6, val_mx,
ipv4_2_ipv6)
from django.core.validators import MinValueValidator, MaxValueValidator
import django.conf
from django.db.models.signals import post_save
......@@ -186,14 +188,16 @@ class Vlan(models.Model):
verbose_name=_('IPv4 address'),
help_text=_(
'The IPv4 address of the gateway. '
'Recommended value is the last valid '
'address of the subnet, for example '
'Recommended value is the last '
'valid address of the subnet, '
'for example '
'10.4.255.254 for 10.4.0.0/16.'))
ipv6 = models.GenericIPAddressField(protocol='ipv6',
unique=True,
verbose_name=_('IPv6 address'),
help_text=_(
'The IPv6 address of the gateway.'))
'The IPv6 address of the '
'gateway.'))
snat_ip = models.GenericIPAddressField(protocol='ipv4', blank=True,
null=True,
verbose_name=_('NAT IP address'),
......
from celery.task import Task, PeriodicTask
import celery
from django.core.cache import cache
import os
import time
from firewall.fw import *
import django.conf
settings = django.conf.settings.FIREWALL_SETTINGS
@celery.task
def reload_dns_task(data):
pass
@celery.task
def reload_firewall_task(data4, data6):
pass
@celery.task
def reload_dhcp_task(data):
pass
@celery.task
def reload_blacklist_task(data):
pass
class Periodic(PeriodicTask):
run_every = timedelta(seconds=10)
......@@ -48,6 +54,7 @@ class Periodic(PeriodicTask):
reload_blacklist_task.delay(list(ipset()))
print "blacklist ujratoltese kesz"
class ReloadTask(Task):
def run(self, type='Host'):
......@@ -64,4 +71,3 @@ class ReloadTask(Task):
cache.add("blacklist_lock", "true", 30)
print type
......@@ -2,12 +2,10 @@ import base64
import datetime
import json
import re
import sys
from django.conf import settings
from django.db import IntegrityError
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template.loader import render_to_string
from django.utils import translation
from django.utils.timezone import utc
......@@ -15,13 +13,13 @@ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from celery.task.control import inspect
from tasks import *
from firewall.fw import *
from firewall.models import *
from one.tasks import SendMailTask
def reload_firewall(request):
if request.user.is_authenticated():
if request.user.is_superuser:
......@@ -34,34 +32,44 @@ def reload_firewall(request):
html = _("Dear anonymous, you've not signed in yet!")
return HttpResponse(html)
@csrf_exempt
@require_POST
def firewall_api(request):
try:
data=json.loads(base64.b64decode(request.POST["data"]))
data = json.loads(base64.b64decode(request.POST["data"]))
command = request.POST["command"]
if data["password"] != "bdmegintelrontottaanetet":
raise Exception(_("Wrong password."))
if command == "blacklist":
obj, created = Blacklist.objects.get_or_create(ipv4=data["ip"])
obj.reason=data["reason"]
obj.snort_message=data["snort_message"]
obj.reason = data["reason"]
obj.snort_message = data["snort_message"]
if created:
try:
obj.host = Host.objects.get(ipv4=data["ip"])
user = obj.host.owner
lang = user.person_set.all()[0].language
translation.activate(lang)
msg = render_to_string('mails/notification-ban-now.txt',
{ 'user': user,
msg = render_to_string(
'mails/notification-ban-now.txt',
{'user': user,
'bl': obj,
'instance:': obj.host.instance_set.get(),
'url': settings.CLOUD_URL} )
SendMailTask.delay(to=obj.host.owner.email, subject='[IK Cloud] %s' % obj.host.instance_set.get().name, msg=msg, sender=u'cloud@ik.bme.hu')
except (Host.DoesNotExist, ValidationError, IntegrityError, AttributeError):
'url': settings.CLOUD_URL})
SendMailTask.delay(
to=obj.host.owner.email,
subject='[IK Cloud] %s' %
obj.host.instance_set.get().name,
msg=msg, sender=u'cloud@ik.bme.hu')
except (Host.DoesNotExist, ValidationError,
IntegrityError, AttributeError):
pass
if obj.type == 'tempwhite' and obj.modified_at + datetime.timedelta(minutes=1) < datetime.datetime.utcnow().replace(tzinfo=utc):
modified = obj.modified_at + datetime.timedelta(minutes=1)
now = datetime.dateime.utcnow().replace(tzinfo=utc)
if obj.type == 'tempwhite' and modified < now:
obj.type = 'tempban'
obj.save()
return HttpResponse(unicode(_("OK")))
......@@ -86,8 +94,7 @@ def firewall_api(request):
host.enable_net()
for p in data["portforward"]:
host.add_port(proto=p["proto"],
public=int(p["public_port"]),
host.add_port(proto=p["proto"], public=int(p["public_port"]),
private=int(p["private_port"]))
elif command == "destroy":
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment