1 Design 2 ====== 3 4 5 Overview 6 -------- 7 Allows trying out Skia code in the browser. 8 9 10 Security 11 -------- 12 13 We're putting a C++ compiler on the web, and promising to run the results of 14 user submitted code, so security is a large concern. Security is handled in a 15 layered approach, using a combination of seccomp-bpf, chroot jail and rlimits. 16 17 *seccomp-bpf* - Used to limit the types of system calls that the user code can 18 make. Any attempts to make a system call that isn't allowed causes the 19 application to terminate immediately. 20 21 *chroot jail* - The code is run in a chroot jail, making the rest of the 22 operating system files unreachable from the running code. 23 24 *rlimits* - Used to limit the resources the running code can get access to, 25 for example runtime is limited to 5s of CPU. 26 27 User submitted code is also restricted in the following ways: 28 * Limited to 10K of code total. 29 * No preprocessor use is allowed (no lines can begin with #includes). 30 31 32 Architecture 33 ------------ 34 35 The server runs on GCE, and consists of a Go Web Server that calls out to the 36 c++ compiler and executes code in a chroot jail. See the diagram below: 37 38 ++ 39 || 40 |Browser| 41 || 42 +++ 43 | 44 +++ 45 || 46 || 47 |WebServer| 48 || 49 |(Go)| 50 || 51 || 52 +++ 53 | 54 +++ 55 |chrootjail| 56 |++| 57 ||seccomp|| 58 ||++|| 59 |||Usercode||| 60 |||||| 61 ||++|| 62 |++| 63 || 64 ++ 65 66 The user code is expanded into a simple template and linked against libskia 67 and a couple other .o files that contain main() and the code that sets up the 68 seccomp and rlimit restrictions. This code also sets up the SkCanvas that is 69 handed to the user code. Any code the user submits is restricted to running in 70 a single function that looks like this: 71 72 73 void draw(SkCanvas* canvas) { 74 // User code goes here. 75 } 76 77 The user code is tracked by taking an MD5 hash of the code The template is 78 expanded out into <hash>.cpp, which is compiled into <hash>.o, which is then 79 linked together with all the other libs and object files to create an 80 executable named <hash>. That executable is copied into a directory 81 /home/webtry/inout, that is accessible to both the web server and the schroot 82 jail. The application is then run in the schroot jail, writing its response, 83 <hash>.png, out into the same directory, /home/webtry/inout/, where is it read 84 by the web server and returned to the user. 85 86 Startup and config 87 ------------------ 88 The server is started and stopped via: 89 90 sudo /etc/init.d/webtry [start|stop|restart] 91 92 By sysv init only handles starting and stopping a program once, so we use 93 Monit to monitor the application and restart it if it crashes. The config 94 is in: 95 96 /etc/monit/conf.d/webtry 97 98 The chroot jail is implemented using schroot, its configuration 99 file is found in: 100 101 /etc/schroot/chroot.d/webtry 102 103 The seccomp configuration is in main.cpp and only allows the following system 104 calls: 105 106 exit_group 107 exit 108 fstat 109 read 110 write 111 close 112 mmap 113 munmap 114 brk 115 116 Database 117 -------- 118 119 Code submitted is stored in an SQL database so that it can be referenced 120 later, i.e. we can let users bookmark their SkFiddles. 121 122 The storage layer will be Cloud SQL (a cloud version of MySQL). Back of the 123 envelope estimates of traffic come out to a price of a about $1/month. 124 125 All passwords for MySQL are stored in valentine. 126 127 To connect to the database from the skia-webtry-b server: 128 129 $ mysql --host=173.194.83.52 --user=root --password 130 131 Initial setup of the database, the user, and the only table: 132 133 CREATE DATABASE webtry; 134 USE webtry; 135 CREATE USER 'webtry'@'%' IDENTIFIED BY '<password is in valentine>'; 136 GRANT SELECT, INSERT, UPDATE ON webtry.webtry TO 'webtry'@'%'; 137 GRANT SELECT, INSERT, UPDATE ON webtry.workspace TO 'webtry'@'%'; 138 GRANT SELECT, INSERT, UPDATE ON webtry.workspacetry TO 'webtry'@'%'; 139 GRANT SELECT, INSERT, UPDATE ON webtry.source_images TO 'webtry'@'%'; 140 141 // If this gets changed also update the sqlite create statement in webtry.go. 142 143 CREATE TABLE webtry ( 144 code TEXT DEFAULT '' NOT NULL, 145 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, 146 hash CHAR(64) DEFAULT '' NOT NULL, 147 source_image_id INTEGER DEFAULT 0 NOT NULL, 148 PRIMARY KEY(hash), 149 150 FOREIGN KEY (source) REFERENCES sources(id) 151 ); 152 153 CREATE TABLE workspace ( 154 name CHAR(64) DEFAULT '' NOT NULL, 155 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, 156 PRIMARY KEY(name), 157 ); 158 159 CREATE TABLE workspacetry ( 160 name CHAR(64) DEFAULT '' NOT NULL, 161 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, 162 hash CHAR(64) DEFAULT '' NOT NULL, 163 source_image_id INTEGER DEFAULT 0 NOT NULL, 164 hidden INTEGER DEFAULT 0 NOT NULL, 165 166 FOREIGN KEY (name) REFERENCES workspace(name), 167 ); 168 169 CREATE TABLE source_images ( 170 id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT, 171 image MEDIUMBLOB DEFAULT '' NOT NULL, -- Stored as PNG. 172 width INTEGER DEFAULT 0 NOT NULL, 173 height INTEGER DEFAULT 0 NOT NULL, 174 create_ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, 175 hidden INTEGER DEFAULT 0 NOT NULL 176 ); 177 178 ALTER TABLE webtry ADD COLUMN source_image_id INTEGER DEFAULT 0 NOT NULL AFTER hash; 179 ALTER TABLE workspacetry ADD COLUMN source_image_id INTEGER DEFAULT 0 NOT NULL AFTER hash; 180 181 Common queries webtry.go will use: 182 183 INSERT INTO webtry (code, hash) VALUES('int i = 0;...', 'abcdef...'); 184 185 SELECT code, create_ts, hash FROM webtry WHERE hash='abcdef...'; 186 187 SELECT code, create_ts, hash FROM webtry ORDER BY create_ts DESC LIMIT 2; 188 189 // To change the password for the webtry sql client: 190 SET PASSWORD for 'webtry'@'%' = PASSWORD('<password is in valentine>'); 191 192 // Run before and after to confirm the password changed: 193 SELECT Host, User, Password FROM mysql.user; 194 195 Common queries for workspaces: 196 197 SELECT hash, create_ts FROM workspace ORDER BY create_ts DESC; 198 199 INSERT INTO workspace (name, hash) VALUES('autumn-river-12354', 'abcdef...'); 200 201 SELECT name FROM workspace GROUP BY name; 202 203 Common queries for sources: 204 205 SELECT id, image, width, height, create_ts FROM source_images ORDER BY create_ts DESC LIMIT 100; 206 207 Password for the database will be stored in the metadata instance, if the 208 metadata server can't be found, i.e. running locally, then a local sqlite 209 database will be used. To see the current password stored in metadata and the 210 fingerprint: 211 212 gcutil --project=google.com:skia-buildbots getinstance skia-webtry-b 213 214 To set the mysql password that webtry is to use: 215 216 gcutil --project=google.com:skia-buildbots setinstancemetadata skia-webtry-b --metadata=password:'[mysql client webtry password]' --fingerprint=[some fingerprint] 217 218 To retrieve the password from the running instance just GET the right URL from 219 the metadata server: 220 221 curl "http://metadata/computeMetadata/v1/instance/attributes/password" -H "X-Google-Metadata-Request: True" 222 223 N.B. If you need to change the MySQL password that webtry uses, you must change 224 it both in MySQL and the value stored in the metadata server. 225 226 Source Images 227 ------------- 228 229 For every try the user can select an optional source image to use as an input. 230 The id of the source image is just an integer and is stored in the database 231 along with the other try information, such as the code. 232 233 The actual image itself is also stored in a separate table, 'sources', in the 234 database. On startup we check that all the images are available in 'inout', 235 and write out the images if not. Since they are all written to 'inout' we can 236 use the same /i/ image handler to serve them. 237 238 When a user uploads an image it is decoded and converted to PNG and stored 239 as a binary blob in the database. 240 241 The bitmap is available to user code as a module level variable: 242 243 SkBitmap source; 244 245 The bitmap is read, decoded and stored in source before the seccomp jail is 246 instantiated. 247 248 249 Squid 250 ----- 251 252 Squid is configured to run on port 80 and run as an accelerator for the actual 253 Go program which is running on port 8000. The config for the squid proxy is 254 held in sys/webtry_squid, which is copied into place during installation and 255 squid is kept running via monit. 256 257 Workspaces 258 ---------- 259 260 Workspaces are implemented by the workspace and workspacetry tables. The 261 workspace table keeps the unique list of all workspaces. The workspacetry table 262 keeps track of all the tries that have occured in a workspace. Right now the 263 hidden column of workspacetry is not used, it's for future functionality. 264 265 Code Editor 266 ----------- 267 [CodeMirror](http://codemirror.net/) is used for rich code editing. The 268 following files are included from the official CodeMirror distribution and can 269 be updated in place (no local customizations): 270 271 * codemirror.js - base CM implementation 272 * codemirror.css - base CM stylesheet 273 * clike.js - C-like syntax highlighting support 274 275 Alternatively, we may consider pulling CM as an external dependency at some 276 point. 277 278 Installation 279 ------------ 280 See the README file. 281 282 283