name: inverse layout: true class: center, middle, inverse --- # Safely Running User-Provided Code Sid Shanker - Fog City Ruby 01-09-2018 --- layout: false # Hi, I'm Sid! .left-column-even[I'm a software engineer at [Fin](https://fin.com), and have been working in Ruby on and off for ~5 years **Email:** sid.p.shanker@gmail.com **Twitter:** [@sidpshanker](https://twitter.com/sidpshanker) **Github:** [@squidarth](https://github.com/squidarth) ] .right-column-even[ .right[.padding-right[![Right-aligned image](sid_as_jenkins.JPG)]] .red[Me dressed as my favorite CI system] ] --- ## RubyProctor .left-column-even[ ### What is it? RubyProctor is a simple website for rapidly developing RuboCop rules. It provides the Ruby AST of some example code, and as you type, evaluates the rule you have provided. ] .right-column-even[ ![Right-aligned image](rubyproctor_ss.png) ] --- layout: false ## Rubocop .left-column-even[ RuboCop is a command line tool that allows you to enforce coding styles on your codebase. Using Rubocop: * Saves time in code review (no need to deal with nits) * Educates developers * Reduces # of bike-shedding arguments ] .right-column-even[ ![Right-aligned image](rubocop_logo.png) ] .footnote[Image from: http://rubocop.readthedocs.io/en/latest/] --- layout: false ## Rubocop Rubocop comes with configurable set of rules that you can toggle on/off Example snippet of Rubocop configuration: ```yaml Metrics/LineLength: Max: 120 Style/CollectionMethods: Enabled: true PreferredMethods: reduce: "inject" Style/AlignHash: EnforcedHashRocketStyle: table EnforcedColonStyle: table ``` Rubocop also allows you to write your own rules. However, writing these custom rules can be somewhat annoying to do. --- class: center, middle # [Ruby Proctor Demo](http://rubyproctor.com) --- class: center, middle ![Right-aligned image](danger_gif.gif) --- class: center, middle # Someone could mine some hip cryptocurrency ![Right-aligned image](cryptos.jpg) --- class: center, middle # Someone could DDoS your favorite website ![Right-aligned image](github_unicorn.png) --- class: center, middle # Or, more benevolently, just crash your server ![Right-aligned image](plane_crash.jpg) --- class: center, middle ## Protect the filesystem --- .left-column[ ## Protecting the filesystem ### - Use Docker ] .right-column[ # Use Docker * Docker is a system that allows developers to run applications in "containers". * You can think of containers as "mini-OSs" that run on a host OS * They are quick to start, so if something goes wrong, you can start over easily ![Right-aligned image](docker_logo.png) ] --- .left-column[ ## Protecting the filesystem ### - Use Docker ### - Set the filesystem to read-only ] .right-column[ # Use the --read-only flag when running the container `docker run` has a `--read-only` option (see [the docs](https://docs.docker.com/engine/reference/commandline/run/#options)) that mounts the root filesystem as read-only. This prevents any attacker from modifying any files on the file system at all, preventing attacks involving corrupting data ] --- .left-column[ ## Protecting the filesystem ### - Use Docker ### - Set the filesystem to read-only ### - How Docker's Filesystem Works ] .right-column[ # How Docker's Filesystem Works * Docker containers are created from **images** * Docker containers use a layered filesystem * It uses "copy-on-write" to make changes to files * This means that everything in the Docker container has a "read-only" version .center[![Right-aligned image](docker_containers.png)] .right[[Image credit](https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/#container-and-layers) ] ] .center[] --- class: center, middle ## Network Access --- .left-column[ ## Network Access ### - Disabling External Network Access ] .right-column[ # Disabling Network Access Allowing clients to make arbitrary network calls is dangerous because of the potential to use your server to launch an attack on another site. I'll demonstrate disallowing networking calls doing this using `iptables` Linux tool. ![Right-aligned image](unplugged_ethernet.jpg) ] .right-column[.footnote[[Photo Credit](https://c1.staticflickr.com/7/6196/6088751332_7da4134066_b.jpg)]] --- .left-column[ ## Network Access ### - Disabling External Network Access ### - iptables ] # iptables `iptables` is a powerful firewall tool on Linux for managing inbound and outbound traffic. For docker setups running on a single box, it's the simplest way to set up rules. This rule: ``` sudo iptables -A FORWARD -i docker0 -j DROP ``` drops any packets originating from the "docker" network interface. `iptables` can be complicated to use, but this is a simple enough rule. --- class: center, middle ![Right-aligned image](hotel_california.jpg) .footnote[[Image Credit](http://cdn.ipernity.com/143/09/47/33140947.863d9992.500.jpg?r2)] --- class: center, middle ## Process Permissions --- .left-column[ ## Process Permissions ### - Running External Code in a Separate Process ] .right-column[ # Running External Code in a Separate Process In RubyProctor, I make sure not to execute the rule by running `eval` in the webserver code, so that an attacker can't modify the webserver. ```ruby # In the webserver code Process.spawn("ruby runner.rb '#{code}' '#{rule}'") ``` Other possible risks: * The user could include an infinite loop * The user could spin off a bunch of other processes ] --- .left-column[ ## Process Permissions ### - Running External Code in a Separate Process ### - rlimits ] .right-column[ # rlimits Linux has a handy way of setting kernel-enforced resource limits on individual processes. See the full list [here](https://www.systutorials.com/docs/linux/man/2-setrlimit/#lbAE). The two relevant ones to this project are `RLIMIT_NPROC` and `RLIMIT_CPU`. These regulate the number of processes that a process is allowed to create and the amount of CPU time a process is allowed to use respectively. ] --- .left-column[ ## Process Permissions ### - Running External Code in a Separate Process ### - rlimits ### - Setting rlimits in Ruby ] .right-column[ # Setting rlimits in Ruby Setting `rlimits` on proceses in Ruby is simply a matter of passing arguments to `Process.spawn`. ```ruby Process.spawn( "ruby runner.rb #{arg}", rlimit_nproc: [2,2], rlimit_cpu: [2,2] ) ``` Other libraries for running shell commands, such as `Open3`, also support these arguments. ] --- .left-column[ ## Process Permissions ### - rlimits ### - Setting rlimits in Ruby ### - How ironclad are these limits? ] .right-column[ # How ironclad are these limits? `rlimits` are enforced by the Linux kernel, and once set on a process by a parent process, cannot be changed. To demonstrate: ```ruby # parent_script.rb Process.spawn( "ruby child_script.rb", rlimit_nproc: [2,2], ) ``` ```ruby # child_script.rb Process.setrlimit(Process::RLIMIT_NPROC, 5) # raises Operation not permitted - setrlimit (Errno::EPERM) ``` ] --- # Further Reading * [RubyProctor](http://rubyproctor.com) * [Rubocop Documentation](http://rubocop.readthedocs.io/en/latest/) * [Docker Storage, Image, and Containers](https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/#sharing-promotes-smaller-images) * [Docker Container Networking ](https://docs.docker.com/engine/userguide/networking/#default-networks) * [Does GNU/Linux counts processes and threads together when I limit their number? ](https://superuser.com/questions/376532/does-gnu-linux-counts-processes-and-threads-together-when-i-limit-their-number ) * [limits.conf documentation](https://linux.die.net/man/5/limits.conf) * [More details on rlimits](https://www.systutorials.com/docs/linux/man/2-setrlimit/#lbAE) * [Networking Docker Containers - Mesosphere](https://mesosphere.com/blog/networking-docker-containers/) * [A Deep Dive into Iptables and Netfilter Architecture](https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture) --- # Questions? Again, I'm Sid Shanker. **Email:** sid.p.shanker@gmail.com **Twitter:** [@sidpshanker](https://twitter.com/sidpshanker) **Github:** [@squidarth](https://github.com/squidarth)