Kmom05: DevSecOps

By . Latest revision .

Devops handlar om att brygga kommunikationsbarriärer, det är stort fokus på development och operations teams men även security behöver inkluderas för att det ska bli ett bra resultat. I detta kursmoment ska vi kolla på hur vi kan inkludera säkerhet i hela utvecklingsprocessen, så att alla blir ansvariga för säkerhet i ett projekt.

Hur det inte ska se ut när man kör devops.

Hur det inte ska se ut när man kör devops.

Vi har redan gjort några saker för att förbättra vår säkerhet, vi har stängt av ssh inloggning som root användare, vi har en ny användare i database bara för microbloggen, vi pushar inte Azure credentials till GitHub och vi sparar känslig information som behövs till CircleCi som hemlig miljövariabler. Nu ska vi gå vidare med att aktivt leta efter säkerhetsrisk.

#Vad är DevSecOps

Målet med DevSecOps är att alla behöver tänka på och är ansvariga för säkerheten hos en produkt. Säkerhet behöver vara en del av hela utvecklingsprocessen. Mycket inom devops handlar om automation och där vill vi även ha med säkerheten, manuell kontroll av säkerhet ska vara ett undantag inte regeln. DevSecOps har fått ett eget namn för att det är först på senare år som man börjat med att få in säkerhetstänket, det var med inte riktigt i början av devops.

Läs The “What” “How” and “Why” of DevSecOps och What is DevSecOps? som tar upp lite olika delar av DevSecOps.

Läs också kapitell 1 “Securing devops”, 1.1-1.3, i Securing Devops (länken går till en E-bok version) för en introduktion till Continuous Security.

#Test-driven security

Vi ska nu lägga in automatiska säkerhetskontroller i vår CI/CD kedja, men vi jobbar inte med säkerhets så vi har inte kunskapen att utföra säkerhetstester på vårt projekt. Som tur är finns det många projekt andra människor och företag har gjort som testar säkerhet i olika aspekter på olika system. Ni ska koppla på en mängd olika verktyg på CI/CD kedjan som utför säkerhetstester.

#Docker

När det kommer till att göra Docker säkrare finns det väldigt mycket man kan göra, det finns flera olika långa dokument som går igenom vad man kan göra. T.ex. CIS Docker Benchmark, ett av de längre dokumenten, och OWASP Container security standard, som tycker att CIS är för långt dokument. Ni behöver inte sätta er in i dem men om ni är intresserade rekommenderar jag OWASPs standard.

Vi nöjer oss med att läsa OSWAP Docker security cheat sheet. De har en bra sammanfattning av viktiga saker att tänka på. Vi gör några av sakerna för Microbloggen men de flesta uppfyller vi inte.

Läs också Container security best practices för en kort översikt av några saker att tänkta på när man jobbar med containrar i produktion. De pratar om Immutable deployment, alltså att bygga ny instance vid varje deploy och ta bort den gamla. Vår infrastructure är inte mogen nog för det. Vår monitoring är för simpel och vi har inte satt upp någon logging monitoring som kan analysera efter säkerhetsintrång.

Docker image security scanning

Det finns några olika verktyg för att skanna Docker images, Docker runtime och inställningar i Docker host. Tanken var att vi skulle använda oss av något av de verktygen. Tyvärr finns det problem med alla jag testade som gjorde att de är jobbigare att använda dem än vad vi får ut av dem.

Vi får nöja oss med att läsa Docker Image Security Scanning: What It Can and Can’t Do, den nämner några verktyg för att skanna filer. Den nämner dock inte Docker Bench Security vilket är Dockers egna verktyg för att skanna olika delar av Docker.

Det är bra att känna till verktygen och om ni jobbar med Docker på fritiden eller senare i arbetslivet rekommenderar jag er att använda något verktyg.

#Dependency Scanning

I vårt projekt använder vi oss av många externa paket både i Python koden för Microbloggen men även i Docker imagen. Man kan aldrig riktigt veta om ett paket man installera är säkert eller om det innehåller säkerhetsrisker. Här kommer Dependency scanning in i bilden. Dependency Scanning verktyg har oftast en stor databas som kontinuerligt uppdateras med paket som man vet innehåller kända säkerhetssårbarheter. Vi ska använda oss utav verktyget Snyk som kan scanna Python paket och Docker images.

Snyk

Skapa ett konto på Snyk.io. Vi kan koppla Snyk till Microblog repot på GitHub och DockerHub här, men då blir det inte en del av vår CI kedja utan vi behöver logga in på Snyk i efterhand och kolla resultatet. Det vill vi inte, så vi ska använda oss av Orbs i CircleCi, mer specifikt Snyks orb så att det blir ett steg i CI kedjan.

Först behöver ni tillåta 3rd party Orbs i CircleCi.

  • Gå till settings, Security och klicka i Yes, allow all members of my organization to publish dev orbs....

  • Sen behöver ni hämta en API nyckel från Snyk. Gå tlll settings, personal API token och klicka click to show.

  • Kopiera nyckeln och gå till CircleCi och settings för ert Microblog projekt.

  • Skapa en ny miljövariabel som heter SNYK_TOKEN och sätt api nyckeln som värde. Nu kan vi uppdatera er CircleCi konfig.

Snyk ska fungera så att det läser av dependency filer, .requirements.txt och en docker image i vårt fall, men det verkar inte funka så bra med virtuelle miljöer och .requirements.txt filer. Men vi kan få det att fungera.

Om ni inte redan kör version 2.1 i er CircleCi konfig, uppdatera till det och lägg till snyk som en orb.

version: 2.1
orbs: 
    snyk: snyk/snyk@0.0.8

Vi börjar med att lägga till så att Python paketen skannas.

Python

Snyk cli kollar vilka paket som är installerade och klarar egentligen inte av att kolla virtual environment. Men vi kan lurar Snyk med raden - run: echo "source ~/repo/venv/bin/activate" >> $BASH_ENV i CircleCi konfigurationen.

Jag lägger till ett nytt jobb som heter snyk.

snyk:
    docker:
        - image: circleci/python:3.5
    working_directory: ~/repo
    steps:
        - checkout
        - run:
            name: install dependencies
            command: |
                python3 -m venv venv
                . venv/bin/activate
                make install
        - run: echo "source ~/repo/venv/bin/activate" >> $BASH_ENV # här gör vi så att så att CircleCi automatisk laddar venv och då kollar Snyk vad vi har installerat i den.
        - snyk/scan

Pusha upp konfigurationen och kolla att det går igenom. Glöm inte att lägga till Snyk i Workflows jobs. När ni ser att det fungerar ska vi lägga till att skanna Docker imagen.

Docker

I ert job där ni skapar och pushar docker imagen till DockerHub, lägg till ett nytt steg efter att ni byggt imagen men innan ni publicerar den.

- snyk/scan:
    docker-image-name: $IMAGE_NAME

Pusha konfigurationen och kolla att det går igenom. Här fick jag en varning som jag inte lyckades lösa:

✗ Low severity vulnerability found in musl/musl-utils
  Description: CVE-2020-28928
  Info: https://snyk.io/vuln/SNYK-ALPINE312-MUSL-1042762
  Introduced through: musl/musl-utils@1.1.24-r9, libc-dev/libc-utils@0.7.2-r3
  From: musl/musl-utils@1.1.24-r9
  From: libc-dev/libc-utils@0.7.2-r3 > musl/musl-utils@1.1.24-r9
  Fixed in: 1.1.24-r10

Ge det gärna ett försök och om ni lyckas, skriv i er redovisningstext hur ni gjorde.

Men nu har vi ett problem, bygget avbryts när Snyk hittar ett fel men vi har ingen lösning på felet. Som tur är finns det konfigurationsfiler till Snyk där vi kan ignorera felet.

Skapa filen .snyk i rooten av ert repo. Sen lägger vi följande i den:

version: v1.14.0
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
ignore:
  SNYK-ALPINE312-MUSL-1042762:
    - '*':
        reason: no remediation
        expires: 2021-06-01T00:00:00.000Z

Kolla under Syntax i dokumentationen för en förklaring av innehållet.

Nu behöver vi i circleci konfigurationen säga till Snyk att läsa in .snyk, den ska leta efter den automatisk men det funkar inte för mig.

- snyk/scan:
    docker-image-name: $IMAGE_NAME
    additional-arguments: "--policy-path=.snyk"

Nu borde er CI kedja gå igenom igen. Om ni får några andra fel i Snyk, försök lösa dem och skriv om det i er redovisningstext.

#SAST vs. DAST

Static Application Security Testing (SAST), eller bara Static Code Analysis, är att analysera källkoden för kända säkerhetsrisker och sårbarheter utan att exekvera programmet. SAST är bra på att fånga upp programmeringsmisstag tidigt i utvecklingsprocessen av en applikation. Vi kommer använda Bandit för att utföra SAST på vår Python kod.

Dynamic Application Security Testing (DAST) letar efter sårbarheter i webbapplikationer genom att skanna och utföra attacker på applikationen. Vi kommer använda Zap för att utföra DAST på Microbloggen.

Läs SAST vs. DAST för en jämförelse av de två och vad de är bra på.

Bandit

Bandit är ett linting verktyg (som pylint) fast det analyserar istället säkerhet i koden. Ladda ner Bandit och lägg till det i requirements/test.txt så att det är en del av paketen för testning. Testa att köra Bandit med bandit -r app så att det bara analyserar koden för applikationen, vi behöver inte köra det mot testerna.

Om ni har kodrader som ni anser är false-positivs kan ni lägga # nosec som en kommentar i slutet på den raden. Då ignorerar Bandit den raden. Det går även att hoppa över hela tester, ni kan skapa filen .bandit.yml och i den skriva:

skips: [<'list of tests'>]

För att Bandit ska läsa konfigurationen kör Bandit med bandit -c .bandit.yml -r app.

Om ni får några fel kan ni antingen fixa felet, lägga till # nosec eller hoppa över regeln helt. Analysera felet och gör ett aktivt val över vad som är en passande åtgärd på felet.

Lägg till bandit som ett make target I Makefile som kör Bandit på app mappen. Gör sen så att Bandit är en del av testerna som körs i Dockerfile_test och som en del av CircleCi.

Zap

Zap/Zap på Github är ett verktyg som kan testa väldigt många saker, speciellt från OWASP10. Det går att köra det både automatiskt och manuellt.

Vi kommer att nöja oss med att köra deras Baseline tester på Microbloggen, då utförs inga aktiva attacker, den bara skannar websidan. Mozilla har ett blogginlägg där de förklarar hur ni kan köra Zap med baseline testerna. Följ den för att testa köra den mot er Microblog, ni behöver inte lägga till det i CircleCi.

Det går även att köra Zap mot er lokala miljö, men då måste ni sätta nätverk när ni startar containern:

docker run --net host owasp/zap2docker-weekly zap-baseline.py -t http://0.0.0.0:8000

Fixa minst 5 valfria varningar från Zap, beskriv vilka och vad ni gjorde i redovisningstexten. Det lättaste sättet att fixa dem är att logga in på load balancer instansen och ändra i Nginx konfigurationen, ladda om Nginx och köra Zap igen för att se om varningen försvann.

Egentligen skulle vi lagt till Zap i CircleCi men vi har ingen staging miljö att köra den mot. Så vi får nöja oss med att köra det manuellt innan push. Lägg till ett make target i Makefilen som kör Zap mot er Microblog, döp det till zap.

#Infrastruktur Security

Produktionsmiljön, CI/CD och molntjänsten vi använder kan vi också göra säkrare. Vi är dock begränsade i vad vi kan göra i och med att vi har studentkonton.

#Azure

När det kommer till att säkra molnmiljön handlar det om att verifiera konfiguration istället för att testa tjänsten.

Vi borde kontrollera följande:

  • Att rätt brandväggsregler används i Security Groups.

  • Att systemen är up-to-date genom att kolla versionen på bas imagen vi använder som OS på servrarna.

  • Kontrollera rättigheterna användare har. Vi kan inte göra detta då vi har studentkonton, vi har inte tillgång till Role based access controll (RBAC). Med det kan man kontrollera vem som har rättigheter att skapa/ändra/radera resurser. Vi skulle t.ex. kunna skapa en ny användare som används av gather_vm_instances.yml playbooken och den användaren har bara rättigheter att läsa data från Azure. Då hade vi inte varit lika sårbara om vi hade råkat läcka credentials.

Det finns olika verktyg för att verifiera konfigurationer i molntjänster, men igen är vi begränsade för att vi har studentkonto och inte kan sätta roller och kontrollera subscriptions. Ett populärt open-source verktyg är ScoutSuite men vi kan inte använda det.

Vi nöjer oss med att veta att vi borde göra det, för att vi inte kan på grund av begränsningarna med studentkonton.

Security Groups

Vi kan och ska förbättra våra security groups, som det ser ut nu kan vem som helst koppla upp sig till de olika portarna som är öppna på våra servrar. Det är onödigt när vi vet vilka IP-addresser alla servrarna har. Vi kan inte göra det på ett bra sätt som det ser ut nu, för att vi kör rollen för SGs före vi skapar servrarna i Ansible. Vi behöver skapa servrarna först så att vi kan använda deras IP när vi skapar SGs.

Ändra i Ansible så att Security Groups rollen körs efter Provision rollen. Det kan ni göra genom att ta bort roles/provision_instances/meta mappen och sen lägga till gather_vm_instances och security_groups i roles listan efter provision_instances i filen provision_instances.yml. Vi behöver lägga till servrarna i hosts innan vi kan skapa SGs så att vi vet vilka ip addresser de har.

I roles/security_groups/vars/main.yml hittar ni all security groups, vilka portar som är öppna och vilka IP som kan koppla upp sig mot dem. Nu borde alla IP vara 0.0.0.0/0, vi vill bara att det är SSH portarna och port 80 och 443 på load balancern som ska ha det så. Övriga portar ska bara specifika servrar koppla upp sig till. T.ex. är det bara load balancern som behöver kunna koppla sig till app serverns port 8000 och det är bara app servern som behöver kunna koppla upp sig till databasens port 3306.

Gå igenom main.yml och byt ut alla 0.0.0.0/0 mot specifika IP addresser (förutom för SSH och port 80 och 443 på LB). Ni kan använda {{ groups["<host>"][0] }}/32 för att sätta en IP address.

Med detta har vi begränsat var personer kan utnyttja säkerhetshål för att ta sig in i våra servrar.

#Produktionsmiljön

Det finns en hel del vi kan göra med servrarna i produktion. SSH är en viktig del i vårt arbetsflöde, Ansible behöver kunna SSH:a in till varje server för att konfigurera dem och vi gör det för att felsöka och testa saker. Dock så är vår SSH setup inte särskilt säker, även om vi har stängt av root och password login vilket är steg 1.

I vår struktur kan man SSH:a in till varje server från vilken IP som helst. En säkrar struktur än vad vi har är att ha en bastion/access node som fungerar som ingång till hela produktions infrastrukturen. Då hade vi skapat en till instans som endast är till för att ge tillgång till resten av servrarna. Servern hade haft en security group så att man kan SSH:a till den från vilken IP som helst. På övriga servrar sätter vi security groups som bara tillåter SSH kopplingar från bastion nodens IP. Vi kommer inte att skapa en bastion node då vi har begränsat med resurser men med en större budget hade vi gjort detta. Ni kan läsa lite mer om det på What is a bastion host?

SSH

När vi ändå är inne på SSH kopplingar så kan vi konfigurera säkrare kopplingar på servrarna. Vi börjar med att använda Mozillas ssh_scan verktyg för att skanna SSH konfigurationen på våra servrar. Kör följande kommando lokalt på er dator.

docker run -it mozilla/ssh_scan /app/bin/ssh_scan -t <domain>

Alla servrar borde ha samma SSH konfiguration så det räcker att köra den mot er load balancer. Man får rätt mycket text utskriven men det viktiga är vad den skriver för recommendation, jag fick följande:

"recommendations": [
  "Remove these key exchange algorithms: diffie-hellman-group16-sha512, diffie-hellman-group18-sha512, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1",
  "Remove these MAC algorithms: umac-64-etm@openssh.com, hmac-sha1-etm@openssh.com, umac-64@openssh.com, hmac-sha1"
],

Scannern tycker att jag borde ta bort gamla algoritmer som inte längre är säkra. Istället för att in och leta efter vilka algoritmer vi använder och hur vi stänger av dem så kan tänker jag att vi använder oss Mozillas moderna openSSH konfigurationer. På guidelines/openssh finns det färdiga konfigurations filer för säkrare SSH.

  • Kopiera konfigurationen för Modern (OpenSSH 6.7+), SSH:a in på load balancern och ersätt ssh konfigurationen i /etc/ssh/sshd_config med den nya.

  • Lägg till raden AllowUsers deploy.

  • Ändra följande rad Subsystem sftp /usr/lib/ssh/sftp-server -f AUTHPRIV -l INFO till Subsystem sftp /usr/lib/openssh/sftp-server -f AUTHPRIV -l INFO. Filvägen till sftp-servern är fel, och då klagar Ansible om det inte är konfigurerat rätt.

Kör ssh_scan igen och kolla att ni inte har några rekommendationer kvar. Uppdatera Ansible rollen 10-first-minutes så att den nya SSH konfigurationen sätts på alla servrar.

Det finns givetvis sätt att göra SSH ännu säkrare, det är inget vi ska göra men det kan vara bra att ha på sina egna servrar hemma om man har det. T.ex. har tjänsten Duo multi-factor authentication för SSH. Så när någon försöker logga in via SSH till er server får ni en notifikation och behöver godkänna det i mobilen.

#Hur säker är vår CI/CD pipeline?

Det är inte bara vår kod som behöver vara säker, även vår CI/CD infrastruktur är en säkerhetsrisk. Någon kan ta sig in i CircleCi’s system och komma åt våra olika API nycklar t.ex. och på så sätt få tillgång till vår kod.

Läs How Secure Is Your CICD Pipeline? som går igenom vad man ska tänka på när man sätter upp sin CI/CD pipeline och kopplar ihop olika tjänster.

#Lästips

  1. Zapping the top 10, hur ni kan använda Zap för att testa OWASP10 sårbarheterna.

#Video

Det finns generellt kursmaterial i video form.

  1. Kursen innehåller föreläsningar som spelas in och därefter läggs i spellistan “devops streams ht20”.

  2. I “kursen devops” hittar du alla videor som är kopplade till kursmomentet, de börjar på 5xx i namnet.

#Uppgifter

Följande uppgifter skall utföras och resultatet skall redovisas via me-sidan.

  1. Skanna Python paketen och Microbloggen Docker image med Snyk på CircleCi.

  2. Skapa make target bandit. Lägg till så att det körs med testerna i docker och i CircleCI.

  3. Fixa minst 5 varningar från Zap testerna. Glöm inte bort att uppdatera er Nginx konfiguration i Ansible!

  4. Uppdatera Security Groups rollen så att bara specifika IP kan koppla upp sig mot de olika portarna.

  5. Uppdatera Ansible rollen 10-first-minutes så att servrarna använder den rekommenderade SSH konfigurationen.

  6. Försäkra dig om att du har pushat repot med din senaste kod och taggat din inlämning med version v5.0.0.

#Resultat & Redovisning

Läs instruktionen om hur du skall redovisa.

Se till att följande frågor besvaras i texten:

  1. Varnade Snyk om några paket som ni behövde uppdatera?

  2. Ändrade ni något i er kod efter ni kört Bandit? Använder ni # nosec för att ignorera någon kod eller skippa något test? Varför?

  3. Beskriv vilka Zap varningar ni fixade och hur ni löste dem.

  4. Gick det bra med SSH konfigurationen?

  5. Hur skulle du definiera DevSecOps och dess roll inom devops?

  6. Var skulle du säga att vi har den största säkerhets risken i vårt system och infrastruktur?

#Revision history

  • 2020-11-26: (B, aar) Upp putsad inför HT20.
  • 2019-10-15: (A, aar) Första versionen.

Document source.