Friday, June 04, 2021

DevSecOps: Running a React Single Page App with minimal privilege in Kubernetes

 Dockerfile

FROM node:16-alpine3.11 as build
WORKDIR /app

ENV PATH /app/node_modules/.bin:$PATH

COPY Moneta.Frontend.Web/package.json ./
COPY Moneta.Frontend.Web/package-lock.json ./
RUN npm install --silent

COPY Moneta.Frontend.Web/ ./

RUN yarn build

FROM nginx:latest
COPY --from=build /app/build /usr/share/nginx/html

COPY Moneta.Frontend.Web/nginx/default.conf /etc/nginx/conf.d/default.conf
COPY Moneta.Frontend.Web/nginx/nginx.conf /etc/nginx/nginx.conf

RUN chown -R nobody:nogroup /usr/share/nginx/html && chmod -R 755 /usr/share/nginx/html && \
        chown -R nobody:nogroup /var/cache/nginx && \
        chown -R nobody:nogroup /var/log/nginx && \
        chown -R nobody:nogroup /etc/nginx/conf.d

RUN touch /var/run/nginx.pid && \
        chown -R nobody:nogroup /var/run/nginx.pid

EXPOSE 8080

CMD ["nginx", "-g", "daemon off;"]

nginx/default.conf

server {
  listen 8080;

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
    try_files $uri $uri/ /index.html;
  }

  error_page   500 502 503 504  /50x.html;

  location = /50x.html {
    root   /usr/share/nginx/html;
  }
}

nginx/nginx.conf

worker_processes  1;
           
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}


k8s deployment

podSecurityContext
  runAsUser65534  #nobody
  fsGroup65534    #nogroup
securityContext
  capabilities:
    drop:
    - ALL
    add
    - "NET_ADMIN"
  readOnlyRootFilesystemfalse
  runAsNonRoottrue
  runAsUser65534 # run as the nobody user

Thursday, April 22, 2021

Fix Warning ResponseCookies - The cookie '.AspNetCore.OpenIdConnect.Nonce.xyz' has set 'SameSite=None' and must also set 'Secure'

            
In Startup.cs add the following snippet in Configure

 public void Configure(IApplicationBuilder appIWebHostEnvironment env)
        {
            //...

            app.UseCookiePolicy(new CookiePolicyOptions
            {
                Secure = CookieSecurePolicy.Always,
                MinimumSameSitePolicy = SameSiteMode.None
            });

            //...
        }

Saturday, April 17, 2021

DevSecOps: Runing ASP .NET Core Applications with minimal privileges in Kubernetes

 


Configure podSecurityContext

Configure the pod to run as nobody/nogroup user as follows:

podSecurityContext
  runAsUser65534
  fsGroup65534



Configure SecurityContext

Configure security context to run with minimal possible privileges:

securityContext
  capabilities:
    drop:
    - ALL
    add
    - "NET_ADMIN"
  readOnlyRootFilesystemfalse
  runAsNonRoottrue
  runAsUser65534 # run as the nobody/nogroup user



Run on non standard port

Since we do not have permission to run ports lower tan 1024 (normally assigned by adding capability NET_BIND_SERVICE but this requires root privileges) we have to configure ASP .Net Core to listen to a port above 1024.

env:
nameASPNETCORE_URLS
    valuehttp://+:8080



Tuesday, April 13, 2021

Zero trust architecture with Istio

Disabling access to services outside the mesh

The following command will restrict all outbound traffic to services defined in the service registry as well as those defined through ServiceEntries:

istioctl install --set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY. 

Enabling access to an URL outside the mesh

apiVersionnetworking.istio.io/v1beta1
kindServiceEntry
metadata:
  namemoneta-egress
spec:
  hosts:
  - '*.microsoft.com'
  - '*.microsoftonline.com'
  - '*.windows.net'
  locationMESH_EXTERNAL
  ports:
  - namehttps
    number443
    protocolHTTPS
  resolutionNONE

Set up the namespace to be secure by default

Enable mTLS in strict mode for a specific namespace

apiVersion"security.istio.io/v1beta1"
kind"PeerAuthentication"
metadata:
  name"default"
spec:
  mtls:
    modeSTRICT


Apply ALLOW NOTHING policy

apiVersionsecurity.istio.io/v1beta1
kindAuthorizationPolicy
metadata:
  nameallow-nothing
spec:
  {}

Creating allow rules for the different components

Create allow rule for the frontend

apiVersionsecurity.istio.io/v1beta1
kindAuthorizationPolicy
metadata:
  namefrontend
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "web.name" . }}
  actionALLOW
  rules:
   - {}

Create allow rule for accounts service

apiVersionsecurity.istio.io/v1beta1
kindAuthorizationPolicy
metadata:
  name: {{ include "accounts.name" . }}
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: {{ include "accounts.name" . }}
  actionALLOW
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/moneta/sa/frontend-web"]
    to:
    - operation:
        paths: ["/accounts/*"]

check policies applied 

istioctl x authz check $(kubectl get pods -l app=mssql -n moneta -o jsonpath="{.items[0].metadata.name}").moneta

Thursday, March 11, 2021

Cloud-native Architecture

Definition:
Cloud native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.


These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil.


The Cloud Native Computing Foundation seeks to drive adoption of this paradigm by fostering and sustaining an ecosystem of open source, vendor-neutral projects. We democratize state-of-the-art patterns to make these innovations accessible for everyone.


ref: https://github.com/cncf/foundation/blob/master/charter.md

Traits of a cloud-native architecture:
  • Self-healing
  • cost efficient
  • easy to update 
Principles
  1. Design for automation
  2. Favor stateless over statefull applications
  3. Favor Managed Services
  4. Practice defense in depth
  5. Evolutionary Architecture

ref: https://cloud.google.com/blog/products/application-development/5-principles-for-cloud-native-architecture-what-it-is-and-how-to-master-it


Sunday, August 25, 2019

Micro8ks on windows


Running Micro8ks on a windows machine
  • Install Multipass: https://multipass.run/#install
  • From Powershell:
    • multipass launch -n microk8s-vm -m 4g -d 40g ubuntu
    • multipass list
    • multipass exec microk8s-vm  -- sudo snap install microk8s --classic
    • multipass connect microk8s-vm
References:

Tuesday, January 29, 2019

Check GDPR Compliancy in SQL Server with Dynamic Masking and Encryption

SELECT S.name AS schema_name,
       T.name AS table_name,
       C.name AS column_name,
       TY.name AS type_name,
       COALESCE(IT.value, N'') AS information_type,
       COALESCE(SL.value, N'') AS sensitivity_label,
          COALESCE(mc.is_masked, '0') as IsMasked,
          ISNULL(c.encryption_type, 0) as IsEncrypted
FROM sys.schemas AS S
    JOIN sys.tables AS T
        ON T.schema_id = S.schema_id
    JOIN sys.columns AS C
        ON C.object_id = T.object_id
    JOIN sys.types AS TY
        ON TY.user_type_id = C.user_type_id
    LEFT OUTER JOIN sys.extended_properties AS IT
        ON IT.major_id = C.object_id
           AND IT.minor_id = C.column_id
           AND IT.name = 'sys_information_type_name'
    LEFT OUTER JOIN sys.extended_properties AS SL
        ON SL.major_id = C.object_id
           AND SL.minor_id = C.column_id
           AND SL.name = 'sys_sensitivity_label_name'
       LEFT OUTER JOIN sys.masked_columns as mc
             ON mc.object_id = t.object_id
             AND mc.column_id = c.column_id
where it.value is not null
ORDER BY S.name,
         T.name,
         C.name;

Wednesday, May 10, 2017

Why using { } even for single line if else statemens in C#


If programmers at Apple had simply followed a couple of the rules in the Embedded C Coding Standard, they could have prevented the very serious `Gotofail` SSL bug from entering the iOS and OS X operating systems. Here’s a look at the programming mistakes involved and the easy-to-follow coding standard rules that could have easily prevent the bug.
Source: http://embeddedgurus.com/barr-code/2014/03/apples-gotofail-ssl-security-bug-was-easily-preventable/


Wednesday, June 01, 2016

Converting unix epoch to datetime in streaming analytics

sample input:
    {
        "Key": "Dryer_Sensor",
        "State": "0.8",
        "Timestamp": "1464782405968",
        "Site": "Bir57",
        "EventProcessedUtcTime": "2016-06-01T13:24:49.4517020Z",
        "PartitionId": 0,
        "EventEnqueuedUtcTime": "2016-06-01T12:00:06.4520000Z",
        "IoTHub": {
            "MessageId": "7e6d0379d85046f295c0cdaeaabe25d8",
            "CorrelationId": null,
            "ConnectionDeviceId": "openhab",
            "ConnectionDeviceGenerationId": "635957210185596241",
            "EnqueuedTime": "0001-01-01T00:00:00.0000000",
            "StreamId": null
        }
   }

 query: DATEADD(millisecond, CAST([Timestamp] as bigint), '1970-01-01T00:00:00Z') as timeFromString

this will result in  '1970-01-03T07:09:18.032Z' which is wrong!

you can correct this by modifying the input so its strips the quotes from the epoch


    {
        "Key": "Dryer_Sensor",
        "State": "0.8",
        "Timestamp": "1464782405968",
        "epochtime": 1464782405968,
        "Site": "Bir57",
        "EventProcessedUtcTime": "2016-06-01T13:24:49.4517020Z",
        "PartitionId": 0,
        "EventEnqueuedUtcTime": "2016-06-01T12:00:06.4520000Z",
        "IoTHub": {
            "MessageId": "7e6d0379d85046f295c0cdaeaabe25d8",
            "CorrelationId": null,
            "ConnectionDeviceId": "openhab",
            "ConnectionDeviceGenerationId": "635957210185596241",
            "EnqueuedTime": "0001-01-01T00:00:00.0000000",
            "StreamId": null
        }
   }

and modifying the query so it omits the cast (where the bug resides):  DATEADD(millisecond, epochtime, '1970-01-01T00:00:00Z') as time

this will result in the expected result: 2016-06-01T12:00:05.968Z