├── .gitignore ├── README.md ├── Vagrantfile ├── hiera.yaml ├── packer └── aws.json └── puppet ├── hiera.yaml ├── hiera ├── aws.yaml ├── common.yaml └── virtualbox.yaml ├── manifests ├── basenode.pp ├── cpp-ethereum.pp └── go-ethereum.pp └── modules ├── apt ├── files │ ├── 20auto-upgrades │ └── 50unattended-upgrades └── manifests │ └── init.pp ├── cpp-ethereum ├── manifests │ └── init.pp └── templates │ └── cpp-ethereum.upstart.conf.erb ├── fail2ban └── manifests │ └── init.pp ├── go-ethereum ├── manifests │ └── init.pp └── templates │ └── go-ethereum.upstart.conf.erb ├── golang └── manifests │ └── init.pp ├── sshd └── manifests │ └── init.pp ├── time └── manifests │ └── init.pp ├── ufw └── manifests │ └── init.pp └── users ├── files └── sudoers_local └── manifests └── init.pp /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant 2 | .DS_Store 3 | tmp 4 | aws.env 5 | puppet/modules/users/files/.ssh/authorized_keys 6 | packer_cache 7 | boxes/* 8 | doc/local -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ethereum-environments 2 | ===================== 3 | 4 | This projects provides the environments that allows you to create virtual machines (both remote and local) that run the various kinds of ethereum clients. Basically it allows you to build a full ethereum node with one command. 5 | 6 | The two node types currently supported are the cpp and go implementations of the ethereum client. The full-node client is set up to run as a system service on the VM. 7 | 8 | ## Features 9 | - apt management with unattended security updates 10 | - time server 11 | - users (ubuntu admin and ethereum) 12 | - ethereum full node cli built from head 13 | - ethereum launched as system service running as ethereum user 14 | - ssh setup only public key connection (authorized keys setup) 15 | - ufw firewall only open ethereum port and ssh 16 | - fail2ban against ssh ddos 17 | 18 | ## Disclaimer 19 | 20 | The ethereum project is in inception phase. All software and tools being developed are alpha. Adjust your expectations. 21 | 22 | ## TL;DR 23 | 24 | ### remote vm: 25 | 26 | cpp: 27 | 28 | packer build -var 'node=cpp-ethereum' -var 'name=cpp-ethereum' packer/aws.json 29 | vagrant box add aws-cpp-ethereum boxes/aws-cpp-ethereum.aws.box 30 | ETH_NODE=cpp-ethereum vagrant up aws-cpp-ethereum --provider=aws --no-provision 31 | 32 | go: 33 | 34 | packer build -var 'node=go-ethereum' -var 'name=go-ethereum' packer/aws.json 35 | vagrant box add aws-go-ethereum boxes/aws-go-ethereum.aws.box 36 | ETH_NODE=go-ethereum vagrant up aws-go-ethereum --provider=aws --no-provision 37 | 38 | ### local vm: 39 | 40 | cpp: 41 | 42 | ETH_NODE=cpp-ethereum vagrant up virtualbox-cpp-ethereum 43 | 44 | go: 45 | 46 | ETH_NODE=go-ethereum vagrant up virtualbox-go-ethereum 47 | 48 | ## What is installed on the VM 49 | 50 | ### Client version 51 | 52 | By default the clients are installed from their github repo using the latest code on the master branch. The actual branch is controlled by a puppet facter variable, which you can set on the command line: 53 | 54 | ETH_NODE=go-ethereum ETH_BRANCH=develop vagrant up virtualbox-go-ethereum 55 | 56 | You can also set up multiple vm instances of the same node-type running different client versions. In this case you need to set an alternative name via ETH_NAME variable (which defaults to the nodename). The vm name argument you pass to vagrant should match this name. 57 | 58 | ETH_NODE=go-ethereum ETH_BRANCH=master vagrant up virtualbox-go-ethereum 59 | ETH_NODE=go-ethereum ETH_BRANCH=develop ETH_NAME=go-ethereum-dev vagrant up virtualbox-go-ethereum-dev 60 | 61 | the same for remote aws instances: 62 | 63 | packer build -var 'node=go-ethereum' -var 'name=go-ethereum' packer/aws.json 64 | packer build -var 'node=go-ethereum' -var 'name=go-ethereum-deb' --branch=develop' packer/aws.json 65 | ETH_NODE=go-ethereum ETH_BRANCH=master vagrant up aws-go-ethereum --provider=aws --no-provision 66 | ETH_NODE=go-ethereum ETH_BRANCH=develop ETH_NAME=go-ethereum-dev vagrant up aws-go-ethereum-dev --provider=aws --no-provision 67 | 68 | 69 | ### System service 70 | 71 | After the VM is provisioned with puppet, the ethereum client will be running as a system service using upstart. The clients use data directory in `/usr/local/share/cpp-ethereum/` or `/usr/local/share/go-ethereum/` and logging in `/var/log/go-ethereum/cpp-ethereum.log` or `/var/log/go-ethereum/go-ethereum.log`. These locations can be reset in `puppet/hiera/common.yaml`. The ethereum client service logs can be viewed on the vm as ethereum user: 72 | 73 | sudo su ethereum tail -f /var/log/go-ethereum/go-ethereum.log 74 | 75 | ### GUI client 76 | 77 | By default the GUI client is not installed. You can choose to install the GUI client by setting the ETH_GUI variable (passed to puppet as the gui facter variable). 78 | 79 | ETH_NODE=go-ethereum ETH_GUI=true vagrant up virtualbox-go-ethereum 80 | 81 | This only makes sense for local VM really. You need to use virtualbox app to start your vm with a GUI and start the ethereum gui clients from your vm screen. 82 | 83 | If the GUI is installed, the system service full node will not be running by default. You can still start it by ssh-ing to your vm and 84 | 85 | sudo start go-ethereum 86 | 87 | or 88 | 89 | sudo start cpp-ethereum 90 | 91 | see upstart documentation on how to control upstart system services. 92 | 93 | ### 94 | 95 | ## Prerequisites 96 | 97 | * packer - http://www.packer.io for remote 98 | * virtualbox - for local 99 | * vagrant - http://www.vagrantup.com/ for local and remote 100 | * vagrant plugins recommended: aws, vbguest 101 | 102 | Tested on OSX with 103 | * Packer v0.5.2, Vagrant 1.5.1, vagrant-aws (0.4.1), virtualbox 4.3.8 (4.3.10 buggy on OSX), vagrant-vbguest (0.10.0) 104 | * Packer v0.6.0, Vagrant 1.6.3, vagrant-aws (0.4.1), virtualbox 4.3.12 vagrant-vbguest (0.10.0) 105 | 106 | ### Linux 107 | 108 | Packer is distributed as a binary package and i know of no way to install it with a package manager. 109 | 110 | On deb style systems, vagrant installs simply with: 111 | 112 | sudo apt-get -y install vagrant 113 | 114 | ### on Mac OS X 115 | 116 | There are various ways to install packer and vagrant. Here is a pure command line version using homebrew and cask. 117 | 118 | brew tap homebrew/binary 119 | brew install packer 120 | brew tap phinze/cask 121 | brew install brew-cask 122 | brew cask install vagrant 123 | 124 | ### vagrant plugins 125 | 126 | The `vbguest` plugin is useful to keep your guest editions uptodate (with virtualbox version). Version mismatch can often result in nasty errors. 127 | 128 | vagrant plugin install vagrant-vbguest 129 | 130 | if you use this plugin, `vagrant up` will not be able to download the basebox, so you need to add it manually 131 | 132 | vagrant box add --name ubuntu14.04 --provider virtualbox http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box 133 | 134 | vagrant-aws is used to manage remote aws vm-s with vagrant. 135 | 136 | vagrant plugin install vagrant-aws 137 | 138 | ## base OS for VMs 139 | 140 | The base OS used for VMs here is cutting edge Ubuntu 14.04 (trusty): 141 | 142 | - AWS EC2 eu-west-1 region ami: ami-335da344 143 | - vagrant box: http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box 144 | 145 | Note if you change to an older base OS, you need to make sure puppet 3.x is installed on the VM (not locally). For instance, ubuntu precise has puppet 2.7.x which is too old; to upgrade to puppet 3.x follow http://docs.puppetlabs.com/guides/puppetlabs_package_repositories.html#for-debian-and-ubuntu: 146 | 147 | wget https://apt.puppetlabs.com/puppetlabs-release-precise.deb 148 | sudo dpkg -i puppetlabs-release-precise.deb 149 | sudo apt-get update 150 | sudo apt-get install puppet 151 | 152 | to automate this step you can add the lines to the shell provisioning section in `packer/aws-template.json`: 153 | 154 | ## setting ssh access via authorized_keys 155 | 156 | Before either local or remote vm provisioning, you need to set up your ssh keys. 157 | 158 | Create a file `puppet/modules/users/files/.ssh/authorized_keys` within your working copy. Put your favourite public keys in there to grant access to the VM for both VM users: `ethereum` and `ubuntu` (admin). This file should *not* be under source control to avoid leaking email address etc. (as a precaution it is added to `.gitignore`). The format of the file is your usual `~/.ssh/authorized_keys`, simply one public key per line. 159 | 160 | Note that we do not allow unsafe access to the remote vm by vagrant instead force it to connect as `ubuntu` user and your aws private key. This means you must add at least your aws public key to this file, otherwise `vagrant ssh` will be denied access after provisioning. 161 | 162 | Note that ssh access to your remote VM is also controlled by your instance's security group. If you explicitly whitelisted IP addresses, access will be limited to connections coming from those. 163 | 164 | ## local VM 165 | 166 | The multi-machine `Vagrantfile` includes a section for local vms using virtualbox as provider. So you can use it to boot up temporary local instances for any node locally. This requires virtualbox to be installed on your host. 167 | 168 | ETH_NODE=go-ethereum vagrant up virtualbox-go-ethereum 169 | 170 | Perform https://github.com/ethersphere/ethereum-environments#setting-ssh-access-via-authorized_keys 171 | 172 | Then: 173 | 174 | ETH_NODE=go-ethereum vagrant ssh virtualbox-go-ethereum 175 | 176 | ## remote VM 177 | 178 | You can create remote vms on amazon ec2 (called ami-s). This requires packer installed as well as having an amazon aws account. The setup is detailed below. 179 | 180 | ### aws ec2 setup 181 | 182 | Assuming you are set up on amazon, go to console > 183 | account > security credentials https://console.aws.amazon.com/iam/home?#security_credential 184 | and export your credentials (AWSAccessKeyId, AWSSecretKey) in `rootkey.cvs` 185 | 186 | AWSAccessKeyId=XXXXXXXXXXXXXXX 187 | AWSSecretKey=XXXXXXXXXXXXXX 188 | 189 | To enable network access to your instances, you must allow inbound traffic to your instance. Create a security group within ec2 and add an inbound rule allowing all TCP traffic from anywhere. This sounds dangerous but we provision the server with firewall. 190 | https://console.aws.amazon.com/ec2/home?region=us-east-1#s=SecurityGroups 191 | Remember your security group name and id (this will be environment vars `AWS_SECURITY_GROUP` and `AWS_SECURITY_GROUP_ID`). 192 | 193 | You should also create a named keypair and export its key into a `.pem` file. The path to this file should be `AWS_PRIVATE_KEY_FILE` and the name is `AWS_KEYPAIR_NAME`. 194 | 195 | All credentials and other ec2 related variables are set via user variables http://www.packer.io/docs/templates/user-variables.html reading environment variables. E.g., `packer/aws-template.json` 196 | 197 | "variables": { 198 | "aws_access_key": "{{env `AWSAccessKeyId`}}", 199 | "aws_secret_key": "{{env `AWSSecretKey`}}" 200 | }, 201 | 202 | So create a file (say `aws.env`) setting environment variables (never share or commit this, as a precaution I added this to .gitignore): 203 | 204 | export AWS_ACCESS_KEY_ID= 205 | export AWS_SECRET_KEY= 206 | export AWS_PRIVATE_KEY_FILE= 207 | export AWS_KEYPAIR_NAME= 208 | export AWS_SECURITY_GROUP= 209 | export AWS_SECURITY_GROUP_ID= 210 | export AWS_REGION= 211 | 212 | You need to source this file in your shell terminal. 213 | 214 | source ./aws.env 215 | 216 | ### building remote VMs on aws ec2 217 | 218 | `packer/aws-template.json` is the template to create amazon machine instances (ami-s). For each node, there is a var file in `packer/nodes`. So to build an ec2 ami for say `go-ethereum` node: 219 | 220 | source ./aws.env 221 | packer build -var 'node=go-ethereum' -var 'name=go-ethereum' packer/aws.json 222 | 223 | VMs are available for the following nodes: 224 | 225 | * cpp-ethereum (ethereum full node client cpp implementation built from head of master branch) 226 | * go-ethereum (ethereum full node client go implementation built from head of master branch) 227 | 228 | user variables in `packer/aws.json` can be overwritten on the command line. 229 | - `node`: should match a top-level manifest basename with node def 230 | - `name`: used in the ami name and passed as facter variable to puppet to set client id 231 | - `source_ami`: base ami (by default it is a eu-west1 region ubuntu trusty) 232 | - `instance_type`: aws instance type (e.g., m1.small) 233 | 234 | Once the ami is created, you can make it public, see http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sharingamis-intro.html 235 | If you play around and create ami-s you no longer want, make sure that you deregister them on the ec2 console or amazon will charge you (a minuscule fee) for storing them. 236 | 237 | ### bringing up VMs with vagrant 238 | 239 | After packer builds the ami, it also exports a corresponding aws vagrant box (using the vagrant postprocessor http://www.packer.io/intro/getting-started/vagrant.html. The box is saved under `boxes/aws-.box`. This box contains the actual ami number so you do not need to set it. 240 | 241 | To use this box, you need to install the `vagrant-aws` plugin for vagrant https://github.com/mitchellh/vagrant-aws, simply with 242 | 243 | vagrant plugin install vagrant-aws 244 | 245 | Once you got the ami built with packer, you add the box: 246 | 247 | vagrant box add aws-go-ethereum boxes/aws-go-ethereum.box 248 | 249 | Now you can use the provided multi-machine `Vagrantfile` to boot up temporary instances for any node. (Note the extra `--provider=aws`): 250 | 251 | ETH_NODE=go-ethereum vagrant up aws-go-ethereum --provider=aws --no-provision 252 | 253 | If your packer build was successful, you can safely use the ` --no-provision` option. 254 | You can ssh into your remote instance (if you added your aws public key to the ssh authorized key file): 255 | 256 | ETH_NODE=go-ethereum vagrant ssh aws-go-ethereum 257 | 258 | or you can reprovision your instance using: 259 | 260 | ETH_NODE=go-ethereum vagrant provision aws-go-ethereum 261 | 262 | This is set up to use the exact same puppet masterless process as packer. Remote provisioning with vagrant is only useful if you develop this project and want to test modifications in provisioning without recreating an instance with packer. It is also useful if packer provisioning fails. In this case, just delete the puppet section from the aws packer template, create the instance and then try provisioning with vagrant which you can debug properly by ssh-ing into the vm. 263 | 264 | If you do `vagrant destroy`, the instance will indeed be terminated (in aws lingo): 265 | 266 | ETH_NODE=go-ethereum vagrant destroy aws-go-ethereum 267 | 268 | If you recreate an instance with packer, you need to remove and add the box again to vagrant. 269 | 270 | ## Hiera 271 | 272 | I use hiera as parameter abstraction layer. A bit overkill at this stage but nice to document options. 273 | Hiera calls in puppet should not use defaults, better style documenting all hiera variables by giving the default in `puppet/hiera/common.yaml` 274 | 275 | ## Developer notes 276 | 277 | Compiling on the VM is a bit of a hack since it merges two distinct steps. 278 | The ideal scenario is that we have a continuous release setup that creates unstable or head binary packages using development/compiler baseboxes. Node VMs on the other hand would then be created using these packages, ie., the relevant puppet modules would just install from a repo using a node basebox. 279 | This setup cuts across these two problems and implements it in one step until a binary repo with automated dev builds is available. 280 | An additional benefit is that now developers can use the exact same environment to compile and test using vagrant on their private or remote aws instances. 281 | 282 | ### Vision of a third layer for network testing 283 | 284 | Once the node VMs are created, their clones can be launched with automated scripts resulting in ethereum testnets composed of nodes with uniform and mixed implementations. 285 | These isolated testnets could then be used for integration testing and benchmarking: in one test round consisting of X blocks a suite of precanned transactions and contracts would be fired at the testnet and checked for correctness of operation as well as for expected measures on various network and mining statistics. 286 | 287 | ## Troubleshooting 288 | 289 | ### memory 290 | cpp-ethereum compilation needs a lot of memory. If you get a mysterious `c++: internal compiler error: Killed (program cc1plus)` error, try increase your VM-s memory. In Vagrant, a generous 2GB is requested since the default 512MB is not enough. For aws m1.small instance type was chosen since m1.micro don't cut it. 291 | 292 | ### 293 | packer fails with `Build 'amazon-ebs' errored: extra data in buffer` or `Build 'amazon-ebs' errored: gob: decoding array or slice: length exceeds input size`,just run it again. 294 | 295 | ## Credits 296 | * https://github.com/zelig 297 | * https://github.com/valzav 298 | * https://github.com/caktux 299 | 300 | ## TODO 301 | * support builds from other branches (needs more trix for go client) 302 | * nodes running multiple clients 303 | * add peer server nodes or components 304 | * sort out miners address/key export and import 305 | * add packer template to support other cloud providers 306 | 307 | ##Contribute 308 | 309 | Please contribute with pull requests. 310 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*_ mode: ruby _*_ 2 | # vi: set ft=ruby : 3 | VAGRANTFILE_API_VERSION = "2" 4 | node = ENV['ETH_NODE'] 5 | name = ENV['ETH_NAME'] || node 6 | gui = ENV['ETH_GUI'] || 'false' 7 | branch = ENV['ETH_BRANCH'] || 'master' 8 | fqdn = ENV['ETH_FQDN'] 9 | 10 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |vagrant| 11 | 12 | vagrant.vm.define "virtualbox-#{name}", primary: true do |config| 13 | config.vm.box = 'ubuntu14.04' 14 | config.vm.host_name = node 15 | config.vm.box_url = 'http://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box' 16 | config.vm.provider "virtualbox" do |v| 17 | v.customize ["modifyvm", :id, '--memory', 2048] 18 | end 19 | config.vm.provision :shell do |shell| 20 | shell.inline = "sudo apt-get -y install puppet; sudo rm -f /tmp/hiera; sudo ln -sf /vagrant/puppet/hiera /tmp/hiera" 21 | end 22 | config.vm.provision :puppet do |puppet| 23 | puppet.options = "--verbose" 24 | puppet.facter = { 25 | "fqdn" => "local.leaf", 26 | "hostname" => name, 27 | "gui" => gui, 28 | "branch" => branch, 29 | "environment" => 'virtualbox' 30 | } 31 | puppet.hiera_config_path = "puppet/hiera.yaml" 32 | # puppet.working_directory = "/vagrant" 33 | puppet.module_path = 'puppet/modules' 34 | puppet.manifests_path = 'puppet/manifests' 35 | puppet.manifest_file = "#{node}.pp" 36 | end 37 | end 38 | 39 | # vagrant config for a remote ec2 AMI built by packer 40 | # before you can use vagrant for remote provisioning, you need: 41 | # - ec2 access and setup with a security group AWS_SECURITY_GROUP allowing ssh 42 | # - install vagrant-aws plugin 43 | # - set environment variables 44 | # - run packer on the template 45 | # - add box to vagrant 46 | # - vagrant up/ssh/provision/destroy available: ETH_NODE=go-ethereum vagrant up aws-go-ethereum --provider=aws 47 | 48 | vagrant.vm.define "aws-#{name}" do |config| 49 | config.vm.provider :aws do |aws, override| 50 | aws.access_key_id = ENV['AWS_ACCESS_KEY_ID'] 51 | aws.secret_access_key = ENV['AWS_SECRET_KEY'] 52 | aws.security_groups = [ ENV['AWS_SECURITY_GROUP'] ] 53 | aws.keypair_name = ENV['AWS_KEYPAIR_NAME'] 54 | aws.region = ENV['AWS_REGION'] 55 | override.ssh.username = "ubuntu" 56 | override.ssh.private_key_path = ENV['AWS_PRIVATE_KEY_FILE'] 57 | end 58 | config.vm.box = "aws-#{name}" 59 | config.vm.provision :shell do |shell| 60 | shell.inline = "sudo apt-get -y install puppet; sudo rm -f /tmp/hiera; sudo ln -sf /vagrant/puppet/hiera /tmp/hiera" 61 | end 62 | config.vm.provision :puppet do |puppet| 63 | puppet.facter = { 64 | "fqdn" => fqdn, 65 | "hostname" => name, 66 | "gui" => gui, 67 | "branch" => branch, 68 | "environment" => 'aws' 69 | } 70 | puppet.options = "--verbose" 71 | puppet.hiera_config_path = "puppet/hiera.yaml" 72 | puppet.working_directory = "/vagrant" 73 | puppet.module_path = 'puppet/modules' 74 | puppet.manifests_path = 'puppet/manifests' 75 | puppet.manifest_file = "#{node}.pp" 76 | end 77 | end 78 | 79 | end 80 | -------------------------------------------------------------------------------- /hiera.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :hierarchy: 3 | - "%{::environment}" 4 | - common 5 | :backends: 6 | - yaml 7 | - json 8 | :json: 9 | :datadir: "puppet/hiera" 10 | -------------------------------------------------------------------------------- /packer/aws.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}", 4 | "aws_secret_key": "{{env `AWS_SECRET_KEY`}}", 5 | "aws_security_group_id": "{{env `AWS_SECURITY_GROUP_ID`}}", 6 | "source_ami": "ami-335da344", 7 | "instance_type": "m1.small", 8 | "rootdir": ".", 9 | "name": "go-ethereum", 10 | "node": "go-ethereum", 11 | "gui": "false", 12 | "branch": "master", 13 | "fqdn": "" 14 | }, 15 | 16 | "builders": [{ 17 | "type": "amazon-ebs", 18 | "access_key": "{{user `aws_access_key`}}", 19 | "secret_key": "{{user `aws_secret_key`}}", 20 | "region": "eu-west-1", 21 | "source_ami": "{{user `source_ami`}}", 22 | "instance_type": "{{user `instance_type`}}", 23 | "ssh_username": "ubuntu", 24 | "ami_name": "{{user `name`}} {{isotime | clean_ami_name}}", 25 | "security_group_id": "{{user `aws_security_group_id`}}" 26 | }], 27 | 28 | "provisioners": [{ 29 | "type": "file", 30 | "source": "puppet/hiera", 31 | "destination": "/tmp" 32 | }, { 33 | "type": "shell", 34 | "inline": [ 35 | "sudo apt-get -y install puppet" 36 | ] 37 | }, { 38 | "type": "puppet-masterless", 39 | "manifest_file": "{{user `rootdir`}}/puppet/manifests/{{user `node`}}.pp", 40 | "module_paths": ["{{user `rootdir`}}/puppet/modules"], 41 | "manifest_dir": "{{user `rootdir`}}/puppet/manifests", 42 | "hiera_config_path": "{{user `rootdir`}}/puppet/hiera.yaml", 43 | "staging_directory": "/tmp/packer-puppet/", 44 | "facter": { 45 | "fqdn": "", 46 | "gui" : "{{user `gui`}}", 47 | "branch" : "{{user `branch`}}", 48 | "hostname": "{{user `name`}}", 49 | "nodename": "{{user `name`}}", 50 | "fqdn": "{{user `fqdn`}}", 51 | "environment": "aws" 52 | } 53 | }], 54 | "post-processors": [{ 55 | "type": "vagrant", 56 | "keep_input_artifact": true, 57 | "output": "{{user `rootdir`}}/boxes/aws-{{user `name`}}.box" 58 | }] 59 | } 60 | -------------------------------------------------------------------------------- /puppet/hiera.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :hierarchy: 3 | - "%{::environment}" 4 | - common 5 | :backends: 6 | - yaml 7 | :yaml: 8 | :datadir: "/tmp/hiera" -------------------------------------------------------------------------------- /puppet/hiera/aws.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # variables specific to aws environment to overwrite common 3 | # need a placeholder data value here otherwise hiera complains 4 | provider: aws -------------------------------------------------------------------------------- /puppet/hiera/common.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # keys containing colon must be single quoted 3 | # string values must be double quoted (if contain eg colon) 4 | 5 | 'go-ethereum::dirs': [ 6 | '/var/log/go-ethereum', 7 | '/usr/local/share/go-ethereum' 8 | ] 9 | 'go-ethereum::log_file': "/var/log/go-ethereum/go-ethereum.log" 10 | 'go-ethereum::cli_path': "/usr/local/bin/go-ethereum" 11 | 'go-ethereum::data_dir': "/usr/local/share/go-ethereum" # unused 12 | 'go-ethereum::inbound_port': 30303 # only used to open port with ufw 13 | 'go-ethereum::outbound_port': 30303 14 | 'go-ethereum::max_peer': 5 15 | 16 | 'cpp-ethereum::dirs': [ 17 | '/var/log/cpp-ethereum', 18 | '/usr/local/share/cpp-ethereum' 19 | ] 20 | 'cpp-ethereum::log_file': "/var/log/cpp-ethereum/cpp-ethereum.log" 21 | 'cpp-ethereum::cli_path': "/usr/local/bin/eth" 22 | 'cpp-ethereum::data_dir': "/usr/local/share/cpp-ethereum" 23 | 'cpp-ethereum::outbound_port': 30303 24 | 'cpp-ethereum::inbound_port': 30303 25 | 'cpp-ethereum::max_peer': 5 26 | 'cpp-ethereum::verbosity': 9 27 | 28 | 'users::ubuntu': true 29 | -------------------------------------------------------------------------------- /puppet/hiera/virtualbox.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # variables specific to aws environment to overwrite common 3 | # need a placeholder data value here otherwise hiera complains 4 | provider: virtualbox -------------------------------------------------------------------------------- /puppet/manifests/basenode.pp: -------------------------------------------------------------------------------- 1 | node basenode { 2 | 3 | # APT setup with security updates 4 | 5 | include apt 6 | 7 | apt::unattended_upgrade { 'enable-unattended-upgrades': } 8 | 9 | # Enable firewall and close all ports other 22 and 80 10 | 11 | include ufw 12 | 13 | ufw::allow { 'open-port-22': port => 22 } 14 | # ufw::allow { 'open-port-80': port => 80 } 15 | 16 | # Fail2ban with ssh protection 17 | 18 | include fail2ban 19 | 20 | fail2ban::jail { 'ssh': } 21 | fail2ban::jail { 'ssh-ddos': } 22 | 23 | # SSH with default config 24 | 25 | include sshd 26 | 27 | sshd::default_config { 'setup-sshd-config': } 28 | 29 | include time 30 | include users 31 | 32 | Exec { path => "/bin:/sbin:/usr/bin:/usr/sbin/:/usr/local/bin" } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /puppet/manifests/cpp-ethereum.pp: -------------------------------------------------------------------------------- 1 | import 'basenode.pp' 2 | 3 | node default inherits basenode { 4 | include cpp-ethereum 5 | } -------------------------------------------------------------------------------- /puppet/manifests/go-ethereum.pp: -------------------------------------------------------------------------------- 1 | import 'basenode.pp' 2 | 3 | node default inherits basenode { 4 | include go-ethereum 5 | } 6 | -------------------------------------------------------------------------------- /puppet/modules/apt/files/20auto-upgrades: -------------------------------------------------------------------------------- 1 | APT::Periodic::Update-Package-Lists "1"; 2 | APT::Periodic::Unattended-Upgrade "1"; 3 | -------------------------------------------------------------------------------- /puppet/modules/apt/files/50unattended-upgrades: -------------------------------------------------------------------------------- 1 | // Automatically upgrade packages from these (origin, archive) pairs 2 | Unattended-Upgrade::Allowed-Origins { 3 | // ${distro_id} and ${distro_codename} will be automatically expanded 4 | "${distro_id} ${distro_codename}-security"; 5 | }; 6 | 7 | // List of packages to not update 8 | Unattended-Upgrade::Package-Blacklist { 9 | // "vim"; 10 | // "libc6"; 11 | // "libc6-dev"; 12 | // "libc6-i686"; 13 | }; 14 | 15 | // Send email to this address for problems or packages upgrades 16 | // If empty or unset then no email is sent, make sure that you 17 | // have a working mail setup on your system. The package 'mailx' 18 | // must be installed or anything that provides /usr/bin/mail. 19 | //Unattended-Upgrade::Mail "sysadmins@getfactual.com"; 20 | 21 | // Do automatic removal of new unused dependencies after the upgrade 22 | // (equivalent to apt-get autoremove) 23 | //Unattended-Upgrade::Remove-Unused-Dependencies "false"; 24 | 25 | // Automatically reboot *WITHOUT CONFIRMATION* if a 26 | // the file /var/run/reboot-required is found after the upgrade 27 | //Unattended-Upgrade::Automatic-Reboot "false"; 28 | 29 | -------------------------------------------------------------------------------- /puppet/modules/apt/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class apt { 2 | 3 | exec { 'apt-get update': 4 | command => '/usr/bin/apt-get update', 5 | } 6 | 7 | package { [ 'apt-file', 'apt-transport-https', 'python-software-properties' ]: 8 | ensure => present, 9 | } 10 | 11 | Exec["apt-get update"] -> Package <| |> 12 | 13 | } 14 | 15 | define apt::ppa() { 16 | exec { "/usr/bin/add-apt-repository $title -y": } 17 | } 18 | 19 | define apt::add_key( $keyserver = 'keyserver.ubuntu.com', $key = $title ) { 20 | exec { "/usr/bin/apt-key adv --keyserver $keyserver --recv-keys $key": 21 | unless => "/usr/bin/apt-key finger | /bin/grep fingerprint | /usr/bin/tr -d ' ' | /bin/grep $key", 22 | } 23 | } 24 | 25 | define apt::unattended_upgrade() { 26 | 27 | package { 'unattended-upgrades': 28 | ensure => present, 29 | } 30 | 31 | file { '/etc/apt/apt.conf.d/20auto-upgrades': 32 | ensure => present, 33 | owner => root, 34 | group => root, 35 | mode => 644, 36 | source => 'puppet:///modules/apt/20auto-upgrades', 37 | require => Package['unattended-upgrades'], 38 | } 39 | 40 | file { '/etc/apt/apt.conf.d/50unattended-upgrades': 41 | ensure => present, 42 | owner => root, 43 | group => root, 44 | mode => 644, 45 | source => 'puppet:///modules/apt/50unattended-upgrades', 46 | require => Package['unattended-upgrades'], 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /puppet/modules/cpp-ethereum/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class cpp-ethereum { 2 | 3 | include ufw 4 | 5 | $deps = [ 6 | 'build-essential', 7 | 'g++-4.8', 8 | 'git', 9 | 'cmake', 10 | 'automake', 11 | 'libtool', 12 | 'unzip', 13 | 'yasm', 14 | 'libncurses5-dev', 15 | 'libgmp-dev', 16 | 'libgmp3-dev', 17 | # 'libcrypto++-dev', # only 5.6.1 18 | 'libboost-all-dev', # 1.54 19 | 'libleveldb-dev', 20 | 'libminiupnpc-dev', 21 | 'libreadline-dev', 22 | 'libcurl4-openssl-dev' 23 | ] 24 | 25 | $gui_deps = [ 26 | 'qtbase5-dev', 27 | 'qt5-default', 28 | 'qtdeclarative5-dev', 29 | 'libqt5webkit5-dev' 30 | ] 31 | 32 | package { [$deps]: 33 | ensure => latest, 34 | } 35 | 36 | if ($gui == 'true') { 37 | package { [$gui_deps]: 38 | ensure => latest, 39 | } 40 | $all_deps = [$deps, $gui_deps] 41 | } else { 42 | $all_deps = $deps 43 | } 44 | 45 | $download_dir = "/tmp/" 46 | 47 | file { $download_dir: 48 | ensure => directory 49 | } 50 | 51 | $build_path = "$download_dir/cpp-ethereum/build" 52 | 53 | exec { 'download': 54 | cwd => $download_dir, 55 | command => "git clone https://github.com/ethereum/cpp-ethereum; cd cpp-ethereum; git checkout $branch; mkdir -p $build_path", 56 | require => File[$download_dir] 57 | } 58 | 59 | file { $build_path: 60 | ensure => directory, 61 | require => Exec['download'] 62 | } 63 | 64 | exec { 'cryptopp': 65 | cwd => $build_path, 66 | command => "mkdir cryptopp562; 67 | cd cryptopp562; 68 | wget http://www.cryptopp.com/cryptopp562.zip; 69 | unzip -a cryptopp562.zip; 70 | CXX='g++ -fPIC' make; 71 | make dynamic; 72 | sudo make install || echo # cos no exe built 73 | ", 74 | timeout => 0, # this can take looong 75 | require => [Package[$all_deps],File[$download_dir]] 76 | } 77 | 78 | $ethereum = 'cpp-ethereum' 79 | 80 | if ($gui == 'true') { 81 | $build_flags = '-DCMAKE_BUILD_TYPE=Release -DHEADLESS=1 -DCMAKE_THREAD_LIBS_INIT=pthread' 82 | } else { 83 | $build_flags = '-DCMAKE_BUILD_TYPE=Release -DCMAKE_THREAD_LIBS_INIT=pthread' 84 | } 85 | 86 | exec { 'build': 87 | cwd => $build_path, 88 | command => "cmake .. ${build_flags}; make; make install", 89 | timeout => 0, # this can take looong 90 | require => [Package[$deps],Exec['cryptopp']], 91 | } 92 | 93 | $daemon_path = hiera('cpp-ethereum::cli_path') 94 | $log_file = hiera('cpp-ethereum::log_file') 95 | $data_dir = hiera('cpp-ethereum::data_dir') 96 | $outbound_port = hiera('cpp-ethereum::outbound_port') 97 | $inbound_port = hiera('cpp-ethereum::inbound_port') 98 | $max_peer = hiera('cpp-ethereum::max_peer') 99 | $verbosity = hiera('cpp-ethereum::verbosity') 100 | $dirs = hiera('cpp-ethereum::dirs') 101 | 102 | file { $dirs: 103 | ensure => directory, 104 | owner => ethereum, 105 | group => ethereum, 106 | mode => "0640", 107 | } 108 | 109 | file { "/etc/init/cpp-ethereum.conf": 110 | ensure => "file", 111 | content => template("${module_name}/cpp-ethereum.upstart.conf.erb"), 112 | require => Exec['build'] 113 | } 114 | 115 | file { "/etc/init.d/cpp-ethereum": 116 | ensure => "link", 117 | target => "/lib/init/upstart-job", 118 | require => File["/etc/init/cpp-ethereum.conf"] 119 | } 120 | 121 | ufw::allow { 'open-port-cpp-ethereum': port => $inbound_port } 122 | 123 | if ($gui == 'true') { 124 | $service = "stopped" 125 | } else { 126 | $service = "running" 127 | } 128 | 129 | service { "cpp-ethereum": 130 | ensure => $service, 131 | provider => "upstart", 132 | require => [ 133 | File["/etc/init.d/cpp-ethereum"], 134 | File[$dirs], 135 | Ufw::Allow['open-port-cpp-ethereum'] 136 | ], 137 | subscribe => File["/etc/init.d/cpp-ethereum"] 138 | } 139 | 140 | } -------------------------------------------------------------------------------- /puppet/modules/cpp-ethereum/templates/cpp-ethereum.upstart.conf.erb: -------------------------------------------------------------------------------- 1 | description "cpp-ethereum service" 2 | 3 | start on runlevel [23] 4 | stop on shutdown 5 | 6 | setuid ethereum 7 | setgid ethereum 8 | 9 | kill signal INT 10 | 11 | exec <%= @daemon_path %> -d <%= @data_dir %> -o full -m on -x <%= @max_peer %> -p <%= @outbound_port %> -l <%= @inbound_port %> -v <%= @verbosity %> 2>&1 >> <%= @log_file %> 12 | 13 | respawn -------------------------------------------------------------------------------- /puppet/modules/fail2ban/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class fail2ban { 2 | 3 | package { 'fail2ban': 4 | ensure => present, 5 | } 6 | 7 | service { 'fail2ban': 8 | ensure => running, 9 | } 10 | 11 | } 12 | 13 | define fail2ban::jail() { 14 | 15 | include fail2ban 16 | 17 | exec { "add-fail2ban-jail-${name}": 18 | command => "/bin/echo -e \"[${name}]\nenabled = true\n\" >> /etc/fail2ban/jail.local", 19 | unless => "/bin/cat /etc/fail2ban/jail.local | /bin/grep -A 1 \"\\[${name}\\]\" | grep \"enabled = true\"", 20 | notify => Service['fail2ban'], 21 | require => Package['fail2ban'], 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /puppet/modules/go-ethereum/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class go-ethereum { 2 | 3 | include golang 4 | include ufw 5 | 6 | $deps = [ 7 | 'libgmp3-dev', 8 | 'pkg-config', 9 | 'mercurial', 10 | 'libleveldb1', 11 | 'libreadline6-dev' 12 | ] 13 | 14 | $gui_deps = [ 15 | 'ubuntu-sdk', 16 | 'qtbase5-private-dev', 17 | 'qtdeclarative5-private-dev', 18 | 'libqt5opengl5-dev' 19 | ] 20 | 21 | package { [$deps]: 22 | ensure => present, 23 | } 24 | 25 | if ($gui == 'true') { 26 | $ppa='ppa:ubuntu-sdk-team/ppa' 27 | apt::ppa { $ppa: } 28 | package { [$gui_deps]: 29 | ensure => latest, 30 | require => Apt::Ppa[$ppa] 31 | } 32 | $eth_deps = ["go-ethereum", "go-ethereal"] 33 | golang::install { "go-ethereal": 34 | source => 'github.com/ethereum/go-ethereum/ethereal', 35 | binary => "ethereal", 36 | destination => "/usr/local/bin/ethereal", 37 | branch => $branch, 38 | require => [Package[$deps, $gui_deps],Golang::Compile['eth-go']] 39 | } 40 | } else { 41 | $eth_deps = "go-ethereum" 42 | } 43 | 44 | # compile from source 45 | $daemon_path = hiera('go-ethereum::cli_path') 46 | 47 | golang::compile { "eth-go": 48 | source => 'github.com/ethereum/eth-go', 49 | branch => $branch, 50 | require => Package[$deps] 51 | } 52 | 53 | # I have no idea how go can get away without version management - it's a mystery 54 | golang::install { "go-ethereum": 55 | source => 'github.com/ethereum/go-ethereum/ethereum', 56 | binary => "ethereum", 57 | destination => $daemon_path, 58 | branch => $branch, 59 | require => [Package[$deps],Golang::Compile['eth-go']] 60 | } 61 | 62 | # config variables for go-ethereum upstart service conf file 63 | $log_file = hiera('go-ethereum::log_file') 64 | $data_dir = hiera('go-ethereum::data_dir') #-datadir only in develop as o 2014-06-14 65 | $outbound_port = hiera('go-ethereum::outbound_port') 66 | $inbound_port = hiera('go-ethereum::inbound_port') # only used to open port 67 | $max_peer = hiera('go-ethereum::max_peer') 68 | $dirs = hiera('go-ethereum::dirs') 69 | 70 | file { $dirs: 71 | ensure => directory, 72 | owner => ethereum, 73 | group => ethereum, 74 | mode => "0640", 75 | } 76 | 77 | file { "/etc/init/go-ethereum.conf": 78 | ensure => "file", 79 | content => template("${module_name}/go-ethereum.upstart.conf.erb"), 80 | require => Golang::Install[$eth_deps] 81 | } 82 | 83 | file { "/etc/init.d/go-ethereum": 84 | ensure => "link", 85 | target => "/lib/init/upstart-job", 86 | require => File["/etc/init/go-ethereum.conf"] 87 | } 88 | 89 | ufw::allow { 'open-port-go-ethereum': port => $inbound_port } 90 | 91 | if ($gui == 'true') { 92 | $service = "stopped" 93 | } else { 94 | $service = "running" 95 | } 96 | 97 | service { "go-ethereum": 98 | ensure => $service, 99 | provider => "upstart", 100 | require => [ 101 | File["/etc/init.d/go-ethereum"], 102 | File[$dirs], 103 | Ufw::Allow['open-port-go-ethereum'] 104 | ], 105 | subscribe => File["/etc/init.d/go-ethereum"] 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /puppet/modules/go-ethereum/templates/go-ethereum.upstart.conf.erb: -------------------------------------------------------------------------------- 1 | description "go-ethereum service" 2 | 3 | start on runlevel [23] 4 | stop on shutdown 5 | 6 | setuid ethereum 7 | setgid ethereum 8 | 9 | kill signal INT 10 | 11 | exec <%= @daemon_path %> -mine -maxpeer <%= @max_peer %> -port <%= @outbound_port %> -logfile <%= @log_file %> 12 | 13 | respawn -------------------------------------------------------------------------------- /puppet/modules/golang/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class golang( 2 | $version = 'present' 3 | ) { 4 | 5 | package { 'golang': 6 | ensure => $version, 7 | } 8 | 9 | package { 'git': 10 | ensure => latest, 11 | } 12 | 13 | $gopath = "/usr/local/share/go/share" 14 | $gopathdirs = ["/usr/local/share/go", "/usr/local/share/go/share"] 15 | 16 | file { $gopathdirs: 17 | ensure => directory, 18 | owner => root, 19 | group => root, 20 | mode => 644, 21 | require => Package['golang'] 22 | } 23 | 24 | define compile ($source, $branch) { 25 | exec { "get $source": 26 | environment => "GOPATH=${Golang::gopath}", 27 | command => "go get -v ${source}", 28 | require => [Package['golang', 'git'],File[$Golang::gopathdirs]] 29 | } 30 | $build_path = "${Golang::gopath}/src/${source}" 31 | exec { "build $source": 32 | environment => "GOPATH=${Golang::gopath}", 33 | cwd => $build_path, 34 | command => "git checkout ${branch}; go build -v", 35 | require => Exec["get $source"] 36 | } 37 | } 38 | 39 | define install ($source, $binary, $destination, $branch) { 40 | 41 | golang::compile { $binary: 42 | source => $source, 43 | branch => $branch 44 | } 45 | 46 | file { $destination: 47 | ensure => present, 48 | source => "${Golang::gopath}/src/${source}/${binary}", 49 | require => Golang::Compile[$binary] 50 | } 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /puppet/modules/sshd/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class sshd { 2 | service { 'ssh': 3 | ensure => running, 4 | } 5 | } 6 | 7 | define sshd::default_config { 8 | 9 | include sshd 10 | 11 | file { '/etc/ssh/sshd_config': 12 | ensure => present, 13 | owner => root, 14 | group => root, 15 | mode => 600, 16 | notify => Service['ssh'], 17 | content => " 18 | ChallengeResponseAuthentication no 19 | Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour 20 | GSSAPIAuthentication no 21 | HostKey /etc/ssh/ssh_host_rsa_key 22 | HostKey /etc/ssh/ssh_host_dsa_key 23 | HostKey /etc/ssh/ssh_host_ecdsa_key 24 | HostbasedAuthentication no 25 | IgnoreRhosts yes 26 | KerberosAuthentication no 27 | KeyRegenerationInterval 3600 28 | LogLevel INFO 29 | LoginGraceTime 60 30 | MACs hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-sha1-96,hmac-md5-96 31 | PasswordAuthentication no 32 | PermitEmptyPasswords no 33 | PermitRootLogin without-password 34 | PermitUserEnvironment no 35 | Port 22 36 | Protocol 2 37 | PubKeyAuthentication yes 38 | RSAAuthentication yes 39 | RhostsRSAAuthentication no 40 | ServerKeyBits 768 41 | StrictModes yes 42 | SyslogFacility AUTH 43 | Subsystem sftp /usr/lib/sftp-server 44 | UseDNS no 45 | UseLogin no 46 | UsePAM yes 47 | UsePrivilegeSeparation yes 48 | X11Forwarding no 49 | ", 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /puppet/modules/time/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class time { 2 | 3 | package { ntp: 4 | ensure => present 5 | } 6 | 7 | package { tzdata: 8 | ensure => present 9 | } 10 | 11 | file { '/etc/timezone': 12 | content => 'Europe/London', 13 | require => Package['tzdata'], 14 | } 15 | 16 | exec { 'reconfigure-tzdata': 17 | command => '/usr/sbin/dpkg-reconfigure -f noninteractive tzdata', 18 | subscribe => File['/etc/timezone'], 19 | require => [ Package['tzdata'], File['/etc/timezone'] ], 20 | refreshonly => true, 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /puppet/modules/ufw/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class ufw { 2 | 3 | package { 'ufw': 4 | ensure => present, 5 | } 6 | 7 | Package['ufw'] -> Exec['ufw-default-deny'] -> Exec['ufw-enable'] 8 | 9 | exec { 'ufw-default-deny': 10 | command => '/usr/sbin/ufw default deny', 11 | unless => '/usr/sbin/ufw status verbose | /bin/grep "Default: deny (incoming), allow (outgoing)"', 12 | } 13 | 14 | exec { 'ufw-enable': 15 | command => '/usr/bin/yes | /usr/sbin/ufw enable', 16 | unless => '/usr/sbin/ufw status | /bin/grep "Status: active"', 17 | } 18 | 19 | service { 'ufw': 20 | ensure => running, 21 | enable => true, 22 | hasstatus => true, 23 | subscribe => Package['ufw'], 24 | } 25 | 26 | } 27 | 28 | define ufw::allow($proto='tcp', $port='all', $ipadr='any', $from='any') { 29 | 30 | $from_match = $from ? { 31 | 'any' => 'Anywhere', 32 | default => $from, 33 | } 34 | 35 | exec { "ufw-allow-${proto}-from-${from}-to-${ipadr}-port-${port}": 36 | command => $port ? { 37 | 'all' => "/usr/sbin/ufw allow proto $proto from $from to $ipadr", 38 | default => "/usr/sbin/ufw allow proto $proto from $from to $ipadr port $port", 39 | }, 40 | unless => "$ipadr:$port" ? { 41 | 'any:all' => "/usr/sbin/ufw status | /bin/grep -E \" +ALLOW +$from_match\"", 42 | /[0-9]:all$/ => "/usr/sbin/ufw status | /bin/grep -E \"$ipadr/$proto +ALLOW +$from_match\"", 43 | /^any:[0-9]/ => "/usr/sbin/ufw status | /bin/grep -E \"$port/$proto +ALLOW +$from_match\"", 44 | default => "/usr/sbin/ufw status | /bin/grep -E \"$ipadr $port/$proto +ALLOW +$from_match\"", 45 | }, 46 | require => Exec['ufw-default-deny'], 47 | before => Exec['ufw-enable'], 48 | } 49 | } 50 | 51 | define ufw::deny($proto='tcp', $port='all', $ipadr='any', $from='any') { 52 | 53 | $from_match = $from ? { 54 | 'any' => 'Anywhere', 55 | default => "$from/$proto", 56 | } 57 | 58 | exec { "ufw-deny-${proto}-from-${from}-to-${ipadr}-port-${port}": 59 | command => $port ? { 60 | 'all' => "/usr/sbin/ufw deny proto $proto from $from to $ipadr", 61 | default => "/usr/sbin/ufw deny proto $proto from $from to $ipadr port $port", 62 | }, 63 | unless => $port ? { 64 | 'all' => "/usr/sbin/ufw status | /bin/grep -E \"$ipadr/$proto +DENY +$from_match\"", 65 | default => "/usr/sbin/ufw status | /bin/grep -E \"$ipadr $port/$proto +DENY +$from_match\"", 66 | }, 67 | require => Exec['ufw-default-deny'], 68 | before => Exec['ufw-enable'], 69 | } 70 | 71 | } 72 | 73 | -------------------------------------------------------------------------------- /puppet/modules/users/files/sudoers_local: -------------------------------------------------------------------------------- 1 | Cmnd_Alias WEB = /bin/mkdir, /bin/cat, /bin/ls, /bin/tail, /usr/bin/service 2 | # ethereum ALL=(ALL) NOPASSWD: WEB 3 | -------------------------------------------------------------------------------- /puppet/modules/users/manifests/init.pp: -------------------------------------------------------------------------------- 1 | class users { 2 | 3 | group { ethereum: 4 | ensure => present, 5 | } 6 | 7 | group { 'www-data': 8 | ensure => present 9 | } 10 | 11 | user { ethereum: 12 | ensure => present, 13 | comment => 'Ethereum User', 14 | gid => ethereum, 15 | shell => '/bin/bash', 16 | home => '/home/ethereum', 17 | groups => ['ethereum', 'www-data'], 18 | require => Group['ethereum', 'www-data'], 19 | } 20 | 21 | exec { 'ethereum_homedir': 22 | command => '/bin/cp -R /etc/skel /home/ethereum; /bin/chown -R ethereum:ethereum /home/ethereum', 23 | creates => '/home/ethereum', 24 | require => User['ethereum'], 25 | } 26 | 27 | file { '/etc/sudoers.d/local': 28 | owner => root, 29 | group => root, 30 | ensure => present, 31 | mode => 440, 32 | source => 'puppet:///modules/users/sudoers_local', 33 | } 34 | 35 | file { '/home/ethereum/.ssh': 36 | ensure => directory, 37 | owner => ethereum, 38 | group => ethereum, 39 | mode => 600, 40 | require => Exec['ethereum_homedir'], 41 | } 42 | 43 | file { '/home/ethereum/.ssh/authorized_keys': 44 | ensure => present, 45 | replace => true, 46 | owner => ethereum, 47 | group => ethereum, 48 | mode => 600, 49 | require => File['/home/ethereum/.ssh'], 50 | source => 'puppet:///modules/users/.ssh/authorized_keys', 51 | } 52 | 53 | # if the admin user is ubuntu (true by default) 54 | if hiera(users::ubuntu) { 55 | file { '/home/ubuntu/.ssh': 56 | ensure => directory, 57 | owner => ubuntu, 58 | group => ubuntu, 59 | mode => 600, 60 | } 61 | 62 | file { '/home/ubuntu/.ssh/authorized_keys': 63 | ensure => present, 64 | replace => true, 65 | owner => ubuntu, 66 | group => ubuntu, 67 | mode => 600, 68 | require => File['/home/ubuntu/.ssh'], 69 | source => 'puppet:///modules/users/.ssh/authorized_keys', 70 | } 71 | } 72 | 73 | } 74 | --------------------------------------------------------------------------------