diff options
Diffstat (limited to 'provider_base')
33 files changed, 794 insertions, 0 deletions
diff --git a/provider_base/README b/provider_base/README new file mode 100644 index 00000000..bb80df50 --- /dev/null +++ b/provider_base/README @@ -0,0 +1,9 @@ +This directory holds the base provider files that actual providers inherit from. + +For example: + + the file........ myproject/provider/common.json + inherits from... myproject/leap_platform/provider_base/common.json + + + diff --git a/provider_base/common.json b/provider_base/common.json new file mode 100644 index 00000000..5e689109 --- /dev/null +++ b/provider_base/common.json @@ -0,0 +1,97 @@ +{ + "ip_address": null, + "environment": null, + "services": [], + "tags": [], + "contacts": "= provider.contacts.default", + "domain": { + "full_suffix": "= provider.domain", + "internal_suffix": "= provider.domain_internal", + "full": "= node.name + '.' + domain.full_suffix", + "internal": "= node.name + '.' + domain.internal_suffix", + "name": "= node.name + '.' + (dns.public ? domain.full_suffix : domain.internal_suffix)" + }, + "dns": { + "public": "= service_type != 'internal_service'" + }, + "ssh": { + "authorized_keys": "= authorized_keys", + "config": { + "AllowTcpForwarding": "no" + }, + "port": 22, + "mosh": { + "ports": "60000:61000", + "enabled": false + } + }, + "hosts": "=> hosts_file", + "x509": { + "use": true, + "use_commercial": false, + "cert": "= x509.use ? file(:node_x509_cert, :missing => 'x509 certificate for node $node. Run `leap cert update`') : nil", + "key": "= x509.use ? file(:node_x509_key, :missing => 'x509 key for node $node. Run `leap cert update`') : nil", + "ca_cert": "= try_file :ca_cert", + "commercial_cert": "= x509.use_commercial ? file([:commercial_cert, try{webapp.domain}||domain.full_suffix], :missing => 'commercial x509 certificate for node $node. Add file $file, or run `leap cert csr --domain %s` to generate a temporary self-signed cert and CSR you can use to purchase a real cert.' % (try{webapp.domain}||domain.full_suffix)) : nil", + "commercial_key": "= x509.use_commercial ? file([:commercial_key, try{webapp.domain}||domain.full_suffix], :missing => 'commercial x509 certificate for node $node. Add file $file, or run `leap cert csr --domain %s` to generate a temporary self-signed cert and CSR you can use to purchase a real cert.' % (try{webapp.domain}||domain.full_suffix)) : nil", + "commercial_ca_cert": "= x509.use_commercial ? try_file(:commercial_ca_cert) : nil" + }, + "service_type": "internal_service", + "development": { + "site_config": true + }, + "name": "common", + "location": null, + "enabled": true, + "mail": { + "smarthost": "= nodes_like_me[:services => :mx].exclude(self).field('domain.full')" + }, + "stunnel": { + "clients": {}, + "servers": {} + }, + "firewall": { + "ssh": { + "from": "sysadmin", + "to": "= ip_address", + "port": "= ssh.port" + }, + "stunnel": "=> stunnel_firewall" + }, + "platform": { + "version": "= Leap::Platform.version.to_s", + "major_version": "= Leap::Platform.major_version" + }, + "sources": { + "apt": { + "basic": "http://httpredir.debian.org/debian/", + "security": "http://security.debian.org/", + "backports": "http://httpredir.debian.org/debian/" + }, + "leap-mx": { + "type": "apt", + "package": "leap-mx", + "revision": "latest" + }, + "nickserver": { + "type": "git", + "source": "https://leap.se/git/nickserver", + "revision": "origin/version/0.8" + }, + "platform": { + "apt": { + "basic": "= 'http://deb.leap.se/' + Leap::Platform.major_version" + } + }, + "soledad": { + "type": "apt", + "package": "soledad-server", + "revision": "latest" + }, + "webapp": { + "type": "git", + "source": "https://leap.se/git/leap_web", + "revision": "origin/version/0.8" + } + } +} diff --git a/provider_base/files/branding/head.scss b/provider_base/files/branding/head.scss new file mode 100644 index 00000000..c100a004 --- /dev/null +++ b/provider_base/files/branding/head.scss @@ -0,0 +1 @@ +// no head.scss set diff --git a/provider_base/files/branding/tail.scss b/provider_base/files/branding/tail.scss new file mode 100644 index 00000000..919aeec6 --- /dev/null +++ b/provider_base/files/branding/tail.scss @@ -0,0 +1 @@ +// no tail.scss set diff --git a/provider_base/files/service-definitions/provider.json.erb b/provider_base/files/service-definitions/provider.json.erb new file mode 100644 index 00000000..a75bea61 --- /dev/null +++ b/provider_base/files/service-definitions/provider.json.erb @@ -0,0 +1,16 @@ +<%= + # grab some fields from provider.json + hsh = provider.pick( + :languages, :description, :name, :services, + :enrollment_policy, :default_language, :service + ) + hsh['domain'] = domain.full_suffix + + hsh['api_version'] = "1" + hsh['api_uri'] = ["https://", api.domain, ':', api.port].join + + hsh['ca_cert_uri'] = api.ca_cert_uri + hsh['ca_cert_fingerprint'] = fingerprint(:ca_cert) + + hsh.dump_json +%>
\ No newline at end of file diff --git a/provider_base/files/service-definitions/v1/eip-service.json.erb b/provider_base/files/service-definitions/v1/eip-service.json.erb new file mode 100644 index 00000000..4bd220df --- /dev/null +++ b/provider_base/files/service-definitions/v1/eip-service.json.erb @@ -0,0 +1,55 @@ +<%= + def underscore(words) + words = words.to_s.dup + words.downcase! + words.gsub! /[^a-z]/, '_' + words + end + + def add_gateway(node, locations, options={}) + return nil if options[:ip] == 'REQUIRED' + gateway = {} + gateway["capabilities"] = node.openvpn.pick(:ports, :protocols, :user_ips, :adblock, :filter_dns) + gateway["capabilities"]["transport"] = ["openvpn"] + gateway["host"] = node.domain.full + gateway["ip_address"] = options[:ip] + gateway["capabilities"]["limited"] = options[:limited] + if node['location'] + location_name = underscore(node.location.name) + gateway["location"] = location_name + locations[location_name] ||= node.location + end + gateway + end + + hsh = {} + hsh["serial"] = 1 + hsh["version"] = 1 + locations = {} + gateways = [] + configuration = nil + nodes_like_me[:services => 'openvpn'].each_node do |node| + if node.openvpn.allow_limited && node.openvpn.allow_unlimited + gateways << add_gateway(node, locations, :ip => node.openvpn.gateway_address, :limited => false) + gateways << add_gateway(node, locations, :ip => node.openvpn.second_gateway_address, :limited => true) + elsif node.openvpn.allow_unlimited + gateways << add_gateway(node, locations, :ip => node.openvpn.gateway_address, :limited => false) + elsif node.openvpn.allow_limited + gateways << add_gateway(node, locations, :ip => node.openvpn.gateway_address, :limited => true) + end + if configuration && node.openvpn.configuration != configuration + log :error, "OpenVPN nodes in the environment `#{node.environment}` have conflicting `openvpn.configuration` values. This will result in bad errors." + end + configuration = node.openvpn.configuration + end + if gateways.any? + configuration = configuration.dup + if configuration['fragment'] && configuration['fragment'] == 1500 + configuration.delete('fragment') + end + hsh["gateways"] = gateways.compact + hsh["locations"] = locations + hsh["openvpn_configuration"] = configuration + end + JSON.sorted_generate hsh +%>
\ No newline at end of file diff --git a/provider_base/files/service-definitions/v1/smtp-service.json.erb b/provider_base/files/service-definitions/v1/smtp-service.json.erb new file mode 100644 index 00000000..45f240ac --- /dev/null +++ b/provider_base/files/service-definitions/v1/smtp-service.json.erb @@ -0,0 +1,29 @@ +<%= + def underscore(words) + words = words.to_s.dup + words.downcase! + words.gsub! /[^a-z]/, '_' + words + end + + hsh = {} + hsh["serial"] = 1 + hsh["version"] = 1 + locations = {} + hosts = {} + nodes_like_me[:services => 'mx'].each_node do |node| + host = {} + host["hostname"] = node.domain.full + host["ip_address"] = node.ip_address + host["port"] = 465 # hard coded for now, later node.smtp.port + if node['location'] + location_name = underscore(node.location.name) + host["location"] = location_name + locations[location_name] ||= node.location + end + hosts[node.name] = host + end + hsh["hosts"] = hosts + hsh["locations"] = locations + JSON.sorted_generate hsh +%> diff --git a/provider_base/files/service-definitions/v1/soledad-service.json.erb b/provider_base/files/service-definitions/v1/soledad-service.json.erb new file mode 100644 index 00000000..0cd1c927 --- /dev/null +++ b/provider_base/files/service-definitions/v1/soledad-service.json.erb @@ -0,0 +1,29 @@ +<%= + def underscore(words) + words = words.to_s.dup + words.downcase! + words.gsub! /[^a-z]/, '_' + words + end + + hsh = {} + hsh["serial"] = 1 + hsh["version"] = 1 + locations = {} + hosts = {} + nodes_like_me[:services => 'soledad'].each_node do |node| + host = {} + host["hostname"] = node.domain.full + host["ip_address"] = node.ip_address + host["port"] = node.soledad.port + if node['location'] + location_name = underscore(node.location.name) + host["location"] = location_name + locations[location_name] ||= node.location + end + hosts[node.name] = host + end + hsh["hosts"] = hosts + hsh["locations"] = locations + JSON.sorted_generate hsh +%>
\ No newline at end of file diff --git a/provider_base/provider.json b/provider_base/provider.json new file mode 100644 index 00000000..81b2ea98 --- /dev/null +++ b/provider_base/provider.json @@ -0,0 +1,64 @@ +{ + "domain": "REQUIRED", + "domain_internal": "= domain.sub(/\\.[^\\.]*$/, '.i')", + "name": { + "en": "REQUIRED" + }, + "description": { + "en": "REQUIRED" + }, + "contacts": { + "default": ["REQUIRED"], + "english": "= contacts.default.map {|email| email.split('@').join(' at the domain ')}.join(', ')" + }, + "languages": ["en"], + "default_language": "en", + "enrollment_policy": "open", + "services": "= enabled_services", + "service": { + // bandwidth limit is in Bytes, storage limit is in MB. + // for example: + // "levels": { + // "1": {"name": "free", "description":"Limited service, but without cost to you.", "storage":50}, + // "2": {"name": "basic", "description":"The standard package.", "storage":1000, "rate": {"USD":5}}, + // "3": {"name": "pro", "description":"Extra storage for power users." , "storage":10000, "rate": {"USD":10}} + // } + "levels": { + "1": { + "name": "free", "description": "Please donate." + } + }, + "default_service_level": 1, + "bandwidth_limit": 102400, + "allow_free": "= provider.service.levels.select {|l| l['rate'].nil?}.any?", + "allow_paid": "= provider.service.levels.select {|l| !l['rate'].nil?}.any?", + "allow_anonymous": "= provider.service.levels.select {|l| l['name'] == 'anonymous'}.any? && services.include?('openvpn')", + "allow_registration": "= provider.enrollment_policy != 'closed' && provider.service.levels.select {|l| l['name'] != 'anonymous'}.any?", + "allow_limited_bandwidth": "= provider.service.levels.select {|l| l['bandwidth'] == 'limited'}.any?", + "allow_unlimited_bandwidth": "= provider.service.levels.select {|l| l['bandwidth'].nil?}.any?" + }, + "ca": { + "name": "= provider.ca.organization + ' Root CA'", + "organization": "= provider.name[provider.default_language]", + "organizational_unit": "= 'https://' + provider.domain", + "bit_size": 4096, + "digest": "SHA256", + "life_span": "10 years", + "server_certificates": { + "bit_size": 4096, + "digest": "SHA256", + "life_span": "1 years" + }, + "client_certificates": { + "bit_size": 2048, + "digest": "SHA256", + "life_span": "2 months", + "limited_prefix": "LIMITED", + "unlimited_prefix": "UNLIMITED" + } + }, + "client_version": { + "min": "0.7", + "max": null + } +} diff --git a/provider_base/services/_api_tester.json b/provider_base/services/_api_tester.json new file mode 100644 index 00000000..790aa7d8 --- /dev/null +++ b/provider_base/services/_api_tester.json @@ -0,0 +1,13 @@ +// +// This partial should be added to any service that runs tests that rely on +// accessing the bonafide webapp API. +// +{ + "testing": { + "monitor_auth_token": "= secret :api_monitor_auth_token", + "api_uri": "= global.services[:webapp].api.uri", + // api_hosts is not used directly, but calling hostnames() will ensure + // that the hostnames are added to /etc/hosts + "api_hosts": "= hostnames(nodes_like_me[:services => 'webapp'])" + } +}
\ No newline at end of file diff --git a/provider_base/services/_couchdb_mirror.json b/provider_base/services/_couchdb_mirror.json new file mode 100644 index 00000000..da496bae --- /dev/null +++ b/provider_base/services/_couchdb_mirror.json @@ -0,0 +1,22 @@ +// +// Applied to all non-master couchdb nodes +// NOT CURRENTLY SUPPORTED +// +{ + "stunnel": { + "clients": { + "couch_client": "= stunnel_client(nodes[couch.replication.masters.keys], couch.port)" + } + }, + "couch": { + "mode": "mirror", + "replication": { + // for now, pick the first close one, or the first one. + // in the future, maybe use haproxy to balance among all the masters + "masters": "= try{pick_node(:couch_master,nodes_near_me['services' => 'couchdb']['couch.master' => true]).pick_fields('domain.internal', 'couch.port')} || try{pick_node(:couch_master,nodes_like_me['services' => 'couchdb']['couch.master' => true]).pick_fields('domain.internal', 'couch.port')}", + "username": "replication", + "password": "= secret :couch_replication_password", + "role": "replication" + } + } +} diff --git a/provider_base/services/_couchdb_multimaster.json b/provider_base/services/_couchdb_multimaster.json new file mode 100644 index 00000000..803a9416 --- /dev/null +++ b/provider_base/services/_couchdb_multimaster.json @@ -0,0 +1,24 @@ +// +// Only applied to master couchdb nodes when there are multiple masters +// NOT CURRENTLY USED. +{ + "stunnel": { + "servers": { + "epmd_server": "= stunnel_server(couch.bigcouch.epmd_port)", + "ednp_server": "= stunnel_server(couch.bigcouch.ednp_port)" + }, + "clients": { + "epmd_clients": "= stunnel_client(nodes_like_me['services' => 'couchdb']['couch.mode' => 'multimaster'], couch.bigcouch.epmd_port)", + "ednp_clients": "= stunnel_client(nodes_like_me['services' => 'couchdb']['couch.mode' => 'multimaster'], couch.bigcouch.ednp_port)" + } + }, + "couch": { + "mode": "multimaster", + "bigcouch": { + "epmd_port": 4369, + "ednp_port": 9002, + "cookie": "= secret :bigcouch_cookie", + "neighbors": "= nodes_like_me['services' => 'couchdb']['couch.mode' => 'multimaster'].exclude(self).field('domain.full')" + } + } +} diff --git a/provider_base/services/couchdb.json b/provider_base/services/couchdb.json new file mode 100644 index 00000000..30cb53d1 --- /dev/null +++ b/provider_base/services/couchdb.json @@ -0,0 +1,49 @@ +{ + "x509": { + "use": true + }, + "stunnel": { + "servers": { + "couch_server": "= stunnel_server(couch.port)" + } + }, + "couch": { + "port": 5984, + "mode": "plain", + "users": { + "admin": { + "username": "admin", + "password": "= secret :couch_admin_password", + "salt": "= hex_secret :couch_admin_password_salt, 128" + }, + "leap_mx": { + "username": "leap_mx", + "password": "= secret :couch_leap_mx_password", + "salt": "= hex_secret :couch_leap_mx_password_salt, 128" + }, + "nickserver": { + "username": "nickserver", + "password": "= secret :couch_nickserver_password", + "salt": "= hex_secret :couch_nickserver_password_salt, 128" + }, + "soledad": { + "username": "soledad", + "password": "= secret :couch_soledad_password", + "salt": "= hex_secret :couch_soledad_password_salt, 128" + }, + "webapp": { + "username": "webapp", + "password": "= secret :couch_webapp_password", + "salt": "= hex_secret :couch_webapp_password_salt, 128" + }, + "replication": { + "username": "replication", + "password": "= secret :couch_replication_password", + "salt": "= hex_secret :couch_replication_password_salt, 128" + } + }, + "webapp": { + "nagios_test_pw": "= secret :nagios_test_password" + } + } +} diff --git a/provider_base/services/couchdb.rb b/provider_base/services/couchdb.rb new file mode 100644 index 00000000..ba7e5ae5 --- /dev/null +++ b/provider_base/services/couchdb.rb @@ -0,0 +1,27 @@ +# +# custom logic for couchdb json resolution +# ============================================ +# +# bigcouch is no longer maintained, so now couchdb is required... +# no matter what! +# + +if self.couch['master'] + LeapCli::log :warning, %("The node property {couch.master:true} is deprecated.\n) + + %( Only {couch.mode:plain} is supported. (node #{self.name})) +end + +couchdb_nodes = nodes_like_me['services' => 'couchdb'] + +if couchdb_nodes.size > 1 + LeapCli::log :error, "Having multiple nodes with {services:couchdb} is no longer supported (nodes #{couchdb_nodes.keys.join(', ')})." +elsif self.couch.mode == "multimaster" + LeapCli::log :error, "Nodes with {couch.mode:multimaster} are no longer supported (node #{self.name})." +end + +# +# This is needed for the "test" that creates and removes the storage db +# for test_user_email. If that test is removed, then this is no longer +# necessary: +# +apply_partial('_api_tester')
\ No newline at end of file diff --git a/provider_base/services/dns.json b/provider_base/services/dns.json new file mode 100644 index 00000000..67948ef8 --- /dev/null +++ b/provider_base/services/dns.json @@ -0,0 +1,14 @@ +{ + "hosts": { + "public": "= nodes['dns.public' => true].fields('domain.name', 'dns.aliases', 'ip_address')", + "private": "= nodes['dns.public' => false].fields('domain.name', 'dns.aliases', 'ip_address')" + }, + "service_type": "public_service", + "firewall": { + "dns": { + "from": "*", + "to": "= ip_address", + "port": "53" + } + } +}
\ No newline at end of file diff --git a/provider_base/services/monitor.json b/provider_base/services/monitor.json new file mode 100644 index 00000000..9ddc0ec7 --- /dev/null +++ b/provider_base/services/monitor.json @@ -0,0 +1,29 @@ +{ + "nagios": { + "nagiosadmin_pw": "= secret :nagios_admin_password", + "domains_internal": "= nagios.hosts.values.map{|h|h['domain_internal_suffix']}.uniq", + "environments": "= Hash[ nagios.hosts.values.map{|h|h['environment']}.uniq.map{|e| [e||'default',{'contact_emails'=>manager.env(e).provider.contacts.default}]} ]", + "hosts": "= (self.environment == 'local' ? nodes_like_me : nodes[:environment => '!local']).pick_fields('environment', 'domain.internal', 'domain.internal_suffix', 'domain.full_suffix', 'ip_address', 'services', 'openvpn.gateway_address', 'ssh.port')" + }, + "hosts": "= self.environment == 'local' ? hosts_file(nodes_like_me) : hosts_file(nodes[:environment => '!local'])", + "ssh": { + "monitor": { + "username": "= Leap::Platform.monitor_username", + "private_key": "= file(:monitor_priv_key)" + } + }, + "x509": { + "use": true, + "use_commercial": true, + "ca_cert": "= file :ca_cert, :missing => 'provider CA. Run `leap cert ca`'", + "client_ca_cert": "= file :client_ca_cert, :missing => 'Certificate Authority. Run `leap cert ca`'", + "client_ca_key": "= file :client_ca_key, :missing => 'Certificate Authority. Run `leap cert ca`'" + }, + "firewall": { + "monitor": { + "from": "sysadmin", + "to": "= ip_address", + "port": [443, 80] + } + } +} diff --git a/provider_base/services/monitor.rb b/provider_base/services/monitor.rb new file mode 100644 index 00000000..01590d5c --- /dev/null +++ b/provider_base/services/monitor.rb @@ -0,0 +1,3 @@ +unless self.services.include? "webapp" + LeapCli.log :error, "service `monitor` requires service `webapp` on the same node (node #{self.name})." +end diff --git a/provider_base/services/mx.json b/provider_base/services/mx.json new file mode 100644 index 00000000..c7e99d85 --- /dev/null +++ b/provider_base/services/mx.json @@ -0,0 +1,53 @@ +{ + "mx": { + // provider should define their own custom aliases. + // these are in *addition* to the standard reserved aliases for root and postmaster, etc. + "aliases": {}, + // this is the domain that is used for the OpenPGP header + "key_lookup_domain": "= global.services[:webapp].webapp.domain", + "dkim": { + // bit sizes larger than 2048 are not necessarily supported + "bit_size": 2048, + "public_key": "= remote_file_path(:dkim_pub_key) { generate_dkim_key(mx.dkim.bit_size) }", + "private_key": "= remote_file_path(:dkim_priv_key) { generate_dkim_key(mx.dkim.bit_size) }", + // generate selector based on first ten digits of pub key fingerprint: + "selector": "= fingerprint(local_file_path(:dkim_pub_key) { generate_dkim_key(mx.dkim.bit_size) }, :mode => :rsa).slice(0,10)" + } + }, + "stunnel": { + "clients": { + "couch_client": "= stunnel_client(nodes_like_me[:services => :couchdb], global.services[:couchdb].couch.port)" + } + }, + "haproxy": { + "couch": { + "listen_port": 4096, + "servers": "= haproxy_servers(nodes_like_me[:services => :couchdb], stunnel.clients.couch_client, global.services[:couchdb].couch.port)" + } + }, + "couchdb_leap_mx_user": { + "username": "= global.services[:couchdb].couch.users[:leap_mx].username", + "password": "= secret :couch_leap_mx_password", + "salt": "= hex_secret :couch_leap_mx_password_salt, 128" + }, + "mynetworks": "= host_ips(nodes)", + "rbls": ["zen.spamhaus.org"], + "clamav": { + "whitelisted_addresses": [] + }, + "x509": { + "use": true, + "use_commercial": true, + "ca_cert": "= file :ca_cert, :missing => 'provider CA. Run `leap cert ca`'", + "client_ca_cert": "= file :client_ca_cert, :missing => 'Certificate Authority. Run `leap cert ca`'", + "client_ca_key": "= file :client_ca_key, :missing => 'Certificate Authority. Run `leap cert ca`'" + }, + "service_type": "user_service", + "firewall": { + "mx": { + "from": "*", + "to": "= ip_address", + "port": [25, 465] + } + } +} diff --git a/provider_base/services/mx.rb b/provider_base/services/mx.rb new file mode 100644 index 00000000..03ee561f --- /dev/null +++ b/provider_base/services/mx.rb @@ -0,0 +1 @@ +apply_partial('_api_tester') diff --git a/provider_base/services/obfsproxy.json b/provider_base/services/obfsproxy.json new file mode 100644 index 00000000..979d0ef9 --- /dev/null +++ b/provider_base/services/obfsproxy.json @@ -0,0 +1,9 @@ +{ + "obfsproxy": { + "scramblesuit": { + "password": "= base32_secret('scramblesuit_password_'+name)", + "port" : "= rand_range('scramblesuit_port_'+name, 18000..32000)" + }, + "gateway_address": "= try{pick_node(:obfs_gateway,nodes_near_me['services' => 'openvpn']).pick_fields('openvpn.gateway_address')} || try{pick_node(:obfs_gateway,nodes_like_me['services' => 'openvpn']).pick_fields('openvpn.gateway_address')}" + } +} diff --git a/provider_base/services/openvpn.json b/provider_base/services/openvpn.json new file mode 100644 index 00000000..6f73e31c --- /dev/null +++ b/provider_base/services/openvpn.json @@ -0,0 +1,45 @@ +{ + "service_type": "user_service", + "x509": { + "use": true, + "client_ca_cert": "= file :client_ca_cert, :missing => 'Certificate Authority. Run `leap cert ca`'", + "dh": "= file :dh_params, :missing => 'Diffie-Hellman parameters. Run `leap cert dh`'" + }, + "location": null, + "openvpn": { + "gateway_address": "REQUIRED", + "second_gateway_address": "= openvpn.allow_limited && openvpn.allow_unlimited ? 'REQUIRED' : nil", + "ports": ["80", "443", "53", "1194"], + "protocols": ["tcp", "udp"], + "filter_dns": false, + "adblock": false, + "user_ips": false, + "allow_limited": "= provider.service.allow_limited_bandwidth", + "allow_unlimited": "= provider.service.allow_unlimited_bandwidth", + "limited_prefix": "= provider.ca.client_certificates.limited_prefix", + "unlimited_prefix": "= provider.ca.client_certificates.unlimited_prefix", + "rate_limit": "= openvpn.allow_limited ? provider.service.bandwidth_limit : nil", + "configuration": { + "tls-cipher": "DHE-RSA-AES128-SHA", + "auth": "SHA1", + "cipher": "AES-128-CBC", + "keepalive": "10 30", + "tun-ipv6": true, + "fragment": 1500 + } + }, + "obfsproxy": { + "scramblesuit": { + "password": "= base32_secret('scramblesuit_password_'+name)", + "port" : "= rand_range('scramblesuit_port_'+name, 18000..32000)" + }, + "gateway_address": "= openvpn.gateway_address" + }, + "firewall": { + "vpn": { + "from": "*", + "to": "= openvpn.gateway_address", + "port": "= openvpn.ports + [obfsproxy.scramblesuit.port]" + } + } +} diff --git a/provider_base/services/soledad.json b/provider_base/services/soledad.json new file mode 100644 index 00000000..169588c8 --- /dev/null +++ b/provider_base/services/soledad.json @@ -0,0 +1,21 @@ +{ + "soledad": { + "port": 2323, + "couchdb_soledad_user": { + "username": "= global.services[:couchdb].couch.users[:soledad].username", + "password": "= secret :couch_soledad_password", + "salt": "= hex_secret :couch_soledad_password_salt, 128" + }, + "couchdb_leap_mx_user": { + "username": "= global.services[:couchdb].couch.users[:leap_mx].username" + } + }, + "service_type": "public_service", + "firewall": { + "soledad": { + "from": "*", + "to": "= ip_address", + "port": "= soledad.port" + } + } +} diff --git a/provider_base/services/soledad.rb b/provider_base/services/soledad.rb new file mode 100644 index 00000000..9b220c39 --- /dev/null +++ b/provider_base/services/soledad.rb @@ -0,0 +1,3 @@ +unless self.services.include? "couchdb" + LeapCli.log :error, "service `soledad` requires service `couchdb` on the same node (node #{self.name})." +end diff --git a/provider_base/services/static.json b/provider_base/services/static.json new file mode 100644 index 00000000..2f408ec1 --- /dev/null +++ b/provider_base/services/static.json @@ -0,0 +1,20 @@ +{ + "static": { + "formats": "=> try{static.domains.values.collect{|d| try{d.locations.values.collect{|l|l.format}} }.flatten.compact.uniq} || []", + // include a copy of provider.json in case any of the configured domains happens to match provider.domain + "bootstrap_files": { + "domain": "= provider.domain", + "enabled": "= !! try{static.domains[provider.domain]}", + "provider_json": "=> static.bootstrap_files.enabled ? try{nodes_like_me[:services => 'webapp'].values.first.definition_files['provider']} : nil", + "client_version": "= static.bootstrap_files.enabled ? provider.client_version : nil" + } + }, + "service_type": "public_service", + "firewall": { + "static": { + "from": "*", + "to": "= ip_address", + "port": [80, 443] + } + } +}
\ No newline at end of file diff --git a/provider_base/services/tor.json b/provider_base/services/tor.json new file mode 100644 index 00000000..55d3d2ee --- /dev/null +++ b/provider_base/services/tor.json @@ -0,0 +1,15 @@ +{ + "tor": { + "bandwidth_rate": 6550, + "contacts": "= [provider.contacts['tor'] || provider.contacts.default].flatten", + "nickname": "= (self.name + secret(:tor_family)).sub('_','')[0..18]", + "family": "= nodes[:services => 'tor'][:environment => '!local'].field('tor.nickname').join(',')", + "hidden_service": { + "active": null, + "key_type": "RSA", + "public_key": "= tor_public_key_path(:node_tor_pub_key, tor.hidden_service.key_type) if tor.hidden_service.active", + "private_key": "= tor_private_key_path(:node_tor_priv_key, tor.hidden_service.key_type) if tor.hidden_service.active", + "address": "= onion_address(:node_tor_pub_key) if tor.hidden_service.active" + } + } +} diff --git a/provider_base/services/webapp.json b/provider_base/services/webapp.json new file mode 100644 index 00000000..b1d2ca59 --- /dev/null +++ b/provider_base/services/webapp.json @@ -0,0 +1,93 @@ +{ + "webapp": { + "admins": [], + "forbidden_usernames": [ + "admin", "admins", "administrator", "administrators", "arin-admin", + "certmaster", "contact", "email", "help", "help-desk", "help-ticket", + "help-tickets", "help_desk", "help_ticket", "help_tickets", "helpdesk", + "helpticket", "helptickets", "info", "mail", "maildrop", "noreply", + "owner", "owners", "postmaster", "reply", "robot", "ssladmin", "staff", + "support", "tech-support", "tech_support", "techsupport", "ticket", + "tickets", "vmail", "www-data"], + "domain": "= provider.domain", + "modules": ["user", "billing", "help"], + "couchdb_webapp_user": "= global.services[:couchdb].couch.users[:webapp]", + "couchdb_admin_user": "= global.services[:couchdb].couch.users[:admin]", + "customization_dir": "= file_path 'webapp'", + "client_certificates": "= provider.ca.client_certificates", + "allow_limited_certs": "= provider.service.allow_limited_bandwidth", + "allow_unlimited_certs": "= provider.service.allow_unlimited_bandwidth", + "allow_anonymous_certs": "= provider.service.allow_anonymous", + "allow_registration": "= provider.service.allow_registration", + "default_service_level": "= provider.service.default_service_level", + "service_levels": "= service_levels()", + "secret_token": "= secret :webapp_secret_token", + "api_version": 1, + "secure": false, + "client_version": "= provider.client_version", + "nagios_test_user": { + "username": "nagios_test", + "password": "= secret :nagios_test_password" + }, + "engines": [ + "support" + ], + "locales": "= provider.languages", + "default_locale": "= provider.default_language", + "api_tokens": { + "monitor": "= secret :api_monitor_auth_token", + "allowed_ips": "= host_ips(nodes_like_me)" + } + }, + "stunnel": { + "clients": { + "couch_client": "= stunnel_client(nodes_like_me[:services => :couchdb], global.services[:couchdb].couch.port)" + } + }, + "haproxy": { + "couch": { + "listen_port": 4096, + "servers": "= haproxy_servers(nodes_like_me[:services => :couchdb], stunnel.clients.couch_client, global.services[:couchdb].couch.port)" + } + }, + "definition_files": { + "provider": "= file :provider_json_template", + "eip_service": "= file [:eip_service_json_template, 'v'+webapp.api_version.to_s]", + "soledad_service": "= file [:soledad_service_json_template, 'v'+webapp.api_version.to_s]", + "smtp_service": "= file [:smtp_service_json_template, 'v'+webapp.api_version.to_s]" + }, + "service_type": "public_service", + "api": { + "domain": "= 'api.' + webapp.domain", + "version": 1, + "port": 4430, + "ca_cert_uri": "= 'https://' + webapp.domain + '/ca.crt'", + "uri": "= %(https://#{api.domain}:#{api.port}/#{api.version})" + }, + "nickserver": { + "domain": "= 'nicknym.' + domain.full_suffix", + "couchdb_nickserver_user": { + "username": "= global.services[:couchdb].couch.users[:nickserver].username", + "password": "= secret :couch_nickserver_password", + "salt": "= hex_secret :couch_nickserver_password_salt, 128" + }, + "port": 6425 + }, + "dns": { + "aliases": "= [domain.full, webapp.domain, api.domain, nickserver.domain]" + }, + "x509": { + "use": true, + "use_commercial": true, + "ca_cert": "= file :ca_cert, :missing => 'provider CA. Run `leap cert ca`'", + "client_ca_cert": "= file :client_ca_cert, :missing => 'Certificate Authority. Run `leap cert ca`.'", + "client_ca_key": "= file :client_ca_key, :missing => 'Certificate Authority. Run `leap cert ca`.'" + }, + "firewall": { + "webapp": { + "from": "*", + "to": "= ip_address", + "port": "= [api.port, 443, 80, nickserver.port]" + } + } +} diff --git a/provider_base/tags/development.json b/provider_base/tags/development.json new file mode 100644 index 00000000..caf18e9d --- /dev/null +++ b/provider_base/tags/development.json @@ -0,0 +1,3 @@ +{ + "environment": "development" +}
\ No newline at end of file diff --git a/provider_base/tags/local.json b/provider_base/tags/local.json new file mode 100644 index 00000000..48312b33 --- /dev/null +++ b/provider_base/tags/local.json @@ -0,0 +1,3 @@ +{ + "environment": "local" +}
\ No newline at end of file diff --git a/provider_base/tags/production.json b/provider_base/tags/production.json new file mode 100644 index 00000000..ea17498f --- /dev/null +++ b/provider_base/tags/production.json @@ -0,0 +1,3 @@ +{ + "environment": "production" +}
\ No newline at end of file diff --git a/provider_base/templates/common.json b/provider_base/templates/common.json new file mode 100644 index 00000000..a7675b15 --- /dev/null +++ b/provider_base/templates/common.json @@ -0,0 +1,3 @@ +{ + "ip_address": "REQUIRED" +}
\ No newline at end of file diff --git a/provider_base/templates/couchdb.json b/provider_base/templates/couchdb.json new file mode 100644 index 00000000..34b60915 --- /dev/null +++ b/provider_base/templates/couchdb.json @@ -0,0 +1,5 @@ +{ + "couch": { + "mode": "plain" + } +} diff --git a/provider_base/templates/openvpn.json b/provider_base/templates/openvpn.json new file mode 100644 index 00000000..cbe183e8 --- /dev/null +++ b/provider_base/templates/openvpn.json @@ -0,0 +1,7 @@ +{ + "openvpn": { + "gateway_address": "REQUIRED", + "ports": ["443"], + "protocols": ["tcp"] + } +} diff --git a/provider_base/test/openvpn/client.ovpn.erb b/provider_base/test/openvpn/client.ovpn.erb new file mode 100644 index 00000000..af183ef4 --- /dev/null +++ b/provider_base/test/openvpn/client.ovpn.erb @@ -0,0 +1,28 @@ +client +dev tun +remote-cert-tls server +remote-random +nobind +script-security 2 +verb 3 +auth SHA1 +cipher AES-128-CBC +tls-cipher DHE-RSA-AES128-SHA + +<% vpn_nodes.each_node do |node| -%> +<%= "remote #{node.openvpn.gateway_address} 1194 udp"%> +<% end -%> + +<ca> +<%= read_file! :ca_cert -%> +</ca> + +<cert> +<%# read_file! :test_client_cert -%> +<%= cert -%> +</cert> + +<key> +<%# read_file! :test_client_key -%> +<%= key -%> +</key> |