Skip to content
Stribog

Compliance

All writing

ITAR Compliance Is an Access Problem, Not a Cloud Region

ITAR compliance turns on who obtains technical data, not where it sits. Why a sovereign cloud region cannot settle that, and how to build an enclave that does.

Stribog13 min read

Most regulations can be argued with geography: keep the data in Frankfurt, keep the keys out of a US parent's reach, produce the records. ITAR is where that instinct runs out. The controlled event is a person obtaining information, and that person's nationality is the variable.

The regulation that controls who, not where

The residency controls a platform team already owns — regional pinning, key custody, transfer paperwork — answer a question ITAR does not ask. Under 22 CFR 120.50(a)(2), releasing or otherwise transferring technical data to a foreign person in the United States is an export: the deemed export. No border is crossed and no transfer is there to block. 120.50(b) goes further: such a release is deemed an export to *all* countries in which that person has held or holds citizenship, or holds permanent residency — so a current-citizenship field in an HR system is the wrong input.

Two applicability questions get conflated, with different owners. Classification — is this repository, model or artefact technical data under 22 CFR 120.33? — belongs to a licensed export-control function, not a platform team and not this article. Registration is separate: 22 CFR 122.1(a) requires anyone in the business of manufacturing, exporting or temporarily importing defense articles, or furnishing defense services, to register with DDTC; one occasion suffices, and manufacturers who never export must still register. It is a business-activity trigger, not a data-scope test, so your employer's registration status says nothing about whether your cluster's data is controlled.

One scope note. What follows is an access-restricted enclave: a namespace whose egress is default-deny with a small, named, in-perimeter allowlist. It is not a disconnected cluster. If you need offline operation, start from our disconnected and air-gapped edge Kubernetes playbook instead.

The definitions that bind your architecture

Part 120 was restructured and most explainers have not caught up. "Export" now lives at 120.50; the frequently cited 120.17 was reassigned to "End-use monitoring," so a first-page result citing it for deemed exports is quoting a section that no longer says it. The sections that matter: 120.33 (technical data), 120.50 (export), 120.54 (activities that are not exports), 120.55 (access information), 120.56 (release), 120.62 and 120.63 (U.S. and foreign person).

Read 120.56(a) carefully, because the design rests on it. Technical data is released through visual or other inspection by foreign persons of a *defense article* that reveals technical data, and through oral or written exchanges of technical data. Those are things that happen. For digital systems the operative path is 120.56(a)(2), the exchange, and under 120.50(a)(2) that release is the deemed export. The engineering consequence follows: a RoleBinding, a namespace boundary, a node taint and an egress rule exist to make that event impossible. They are not regulated artefacts in themselves, and an over-broad binding is a standing risk of a release rather than something you can score off the text. Design the possibility out rather than adjudicating it in prose.

The second hook is real and separate. 120.56(a)(3) makes the *use* of access information to cause or enable a foreign person to access, view or possess unencrypted technical data a release; 120.56(b) requires that providing such access information carry the same authorization as releasing the data itself. That governs key custody and secret distribution — KMS grants, Secrets carrying decryption material, any operated decrypt path — and each needs an authorization record distinct from your RBAC records.

Take the person definitions from the CFR. 22 CFR 120.62 defines a U.S. person as a lawful permanent resident under 8 U.S.C. 1101(a)(20) or a protected individual under 8 U.S.C. 1324b(a)(3) — a status test, not one of employment, clearance or desk location, so an HR "US-based employee" flag is not a valid gating attribute. 120.63 is the complement and expressly reaches foreign corporations, international organizations and foreign governments — which matters the moment a vendor rather than an individual holds the credential.

Why a sovereign region does not answer the question

Government and sovereign regions are not marketing. AWS states that GovCloud (US) "is managed solely by US Citizens in US locations" and that "accounts are only granted to US Persons working for US organizations." That is a genuine personnel control covering the provider's half of the problem: their operators, their support staff, their physical access.

The other half is untouched. Nothing about a region constrains which of *your* engineers holds a kubeconfig, which contractor joined the on-call rotation, or which vendor was screen-shared into a debugging session. The shared-responsibility line falls through the middle of the ITAR question, and the customer side has more people on it. Our European sovereign cloud build-versus-buy analysis lands harder here: a contract is not a substitute for this control.

Two related mistakes are worth naming. First, taking "US Person" from a provider's FAQ rather than the CFR: AWS's compliance page phrases it more narrowly than 22 CFR 120.62, whose protected-individual limb reaches people a green-card-or-citizen shorthand misses. Cite the provider for its own operational claim and the regulation for who counts. Second, treating DFARS 252.204-7012 as a partial answer. It is a covered-defense-information and cyber-incident clause: a provider holding that information must meet security requirements equivalent to the FedRAMP Moderate baseline, and "rapidly report" means within 72 hours of discovery. Both run concurrently for a DoD contractor; neither is about nationality. The mirror image — a foreign government compelling access rather than a foreign person obtaining it — is the Schrems II analysis.

The encryption carve-out, read to its limit

22 CFR 120.54 lists activities that are not exports, reexports, retransfers or temporary imports, and (a)(5) is the one that makes modern infrastructure workable. It covers sending, taking or storing technical data that is unclassified; end-to-end encrypted; secured using cryptographic modules compliant with FIPS 140-2 "or its successors", or other means at least comparable to the 128 bits of security strength of AES; not intentionally sent to or stored in a 126.1 proscribed country; and not sent from one. All five, not a majority.

The end-to-end definition at 120.54(b) carries the two limbs that decide real designs. 120.54(b)(1)(ii) requires that "the means of decryption are not provided to any third party" — a custody question, not a product verdict. Customer-held keys in a module under the customer's exclusive control can satisfy it; any configuration in which the provider can decrypt cannot. It depends on the configuration you deployed, so read yours rather than a vendor's compliance page. Then 120.54(b)(2), the limb most summaries drop: the intended recipient must be the originator, a U.S. person in the United States, or a person otherwise authorized by licence or approval. Impeccable key custody does not save an unauthorized recipient at the far end.

Two more edges. 120.54(c) says the ability to access technical data in encrypted form satisfying (a)(5) is not a release or export — the carve-out protects ciphertext and stops there. Note 1 to (a)(5)(iv) adds that data in transit is not deemed stored in a country it transits, keeping the geographic condition about intentional destination and origin. Confidential computing narrows the plaintext window further, but attestation proves what code runs, not who is entitled to the output.

Every condition is a conjunct. The two that catch real architectures are 120.54(b)(1)(ii), who holds the means of decryption, and 120.54(b)(2), who the intended recipient is allowed to be.

A dual-regime estate needs one more comparison. The EAR's parallel carve-out at 15 CFR 734.18 has the same shape and three textual differences: the geographic exclusion is Country Group D:5, not the ITAR 126.1 list; there are four conditions and no limb matching ITAR's "not sent from" a proscribed country; and the crypto alternative reads "other equally or more effective cryptographic means" rather than an AES-128 comparison. One storage policy cannot serve both regimes unchecked.

Building the enclave: US-person gating in Kubernetes

Start with what the cluster cannot know. There is no standard OIDC claim for nationality, and inventing one is the fastest way to write a design nobody can implement. The attestation happens outside the cluster, in a process that evaluates status against 120.62 and records a determination and a date. The cluster consumes only the resulting group membership — and that group name is the seam between a legal process and a technical control.

Take the portable identity path first: regulated estates routinely run several minors behind, and a design that silently needs the newest release is one they cannot deploy. --oidc-groups-claim names the claim, which "must be an array of strings"; --oidc-groups-prefix prepends a namespace so an IdP group cannot collide with a system: group. Treat that prefix as mandatory: the flag applies only "if provided", so omitting it leaves unprefixed groups meeting prefixed RoleBinding subjects, which match nothing. Set it in the same change as the subject.

bash
--oidc-issuer-url=https://sso.enclave.example.com/realms/enclave
--oidc-client-id=kubernetes
--oidc-username-claim=sub
--oidc-username-prefix=oidc:
--oidc-groups-claim=groups
--oidc-groups-prefix=oidc:
kube-apiserver flags spliced into the static-pod manifest or the unit that starts the API server. Portable path — no minimum version beyond the flags being documented.

The modern path is structured authentication configuration, stable in Kubernetes v1.34 and enabled by default there. It is an alternative, not a prerequisite: every manifest after it is identical under either path, which is what makes the portability claim worth anything. For the identity provider behind it, our Keycloak, Authentik and Zitadel comparison covers the trade-offs.

yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AuthenticationConfiguration
jwt:
  - issuer:
      url: https://sso.enclave.example.com/realms/enclave
      audiences:
        - kubernetes
    claimMappings:
      username:
        claim: sub
        prefix: "oidc:"
      groups:
        # Array of strings in the token. The prefix must match the
        # RoleBinding subject exactly — set both in one change.
        claim: groups
        prefix: "oidc:"
    userValidationRules:
      # Nothing in a JWT proves nationality. This only guarantees every
      # subject carries the prefix, so an unprefixed group cannot collide.
      - expression: "user.groups.all(g, g.startsWith('oidc:'))"
        message: "every group must carry the oidc: prefix"
/etc/kubernetes/authn.yaml, loaded with --authentication-config. Kubernetes v1.34 or later; earlier minors shipped this under a beta API and should use the flags above.

Now the binding. Note pods/log: logs are the most reliable way for plaintext to reach somebody who never opened the application.

yaml
# An engineering control: it exists so a foreign person never obtains
# unencrypted technical data — the release at 22 CFR 120.56(a)(2), hence
# the deemed export at 120.50(a)(2). Not itself a release, and not
# "access information" under 120.55, which covers what unlocks ciphertext.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: itar-enclave-operator
  namespace: itar-enclave
rules:
  - apiGroups: [""]
    resources: [pods, pods/log, configmaps, services]
    verbs: [get, list, watch]
  - apiGroups: ["apps"]
    resources: [deployments]
    verbs: [get, list, watch, patch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: itar-enclave-operator
  namespace: itar-enclave
subjects:
  # Prefix matches --oidc-groups-prefix / claimMappings.groups.prefix.
  # Portable name, not an IdP-internal object identifier.
  - kind: Group
    name: "oidc:itar-enclave-cleared"
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: itar-enclave-operator
  apiGroup: rbac.authorization.k8s.io
Namespaced Role and RoleBinding for the export-controlled namespace. The group is issued only after an attestation recorded outside the cluster.

Node isolation takes both halves: a taint on the enclave nodes repels everything else, the workload's own affinity attracts it. A toleration alone permits a pod onto the node without requiring it to land there.

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tdp-analysis
  namespace: itar-enclave
spec:
  replicas: 2
  selector:
    matchLabels: { app: tdp-analysis }
  template:
    metadata:
      labels: { app: tdp-analysis }
    spec:
      tolerations:
        - key: stribog.example/enclave
          operator: Equal
          value: itar
          effect: NoSchedule
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            # nodeSelectorTerms are ORed — one term only, deliberately.
            # A second permissive term would widen placement silently.
            nodeSelectorTerms:
              - matchExpressions:
                  - key: stribog.example/enclave
                    operator: In
                    values: [itar]
      automountServiceAccountToken: false
      containers:
        - name: analysis
          image: registry.internal.example.com/enclave/tdp-analysis:1.4.2
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
Deployment pinned to attested enclave nodes. The required nodeAffinity term is the placement rule; the toleration only makes the tainted nodes eligible.

Egress is the half RBAC does not cover, and where this design earns its name: default-deny, with an allowlist short enough to read aloud.

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: itar-enclave-egress
  namespace: itar-enclave
spec:
  # Selecting every pod with policyTypes: [Egress] makes the namespace
  # default-deny: anything not listed below is dropped, including hosted
  # observability, vendor telemetry and crash reporters.
  podSelector: {}
  policyTypes: [Egress]
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            # Match your cluster's real DNS pods (node-local-dns differs).
            matchLabels:
              k8s-app: kube-dns
      ports:
        - { protocol: UDP, port: 53 }
        - { protocol: TCP, port: 53 }
    # One in-perimeter artefact endpoint, by address.
    - to:
        - ipBlock:
            cidr: 10.42.7.15/32
      ports:
        - { protocol: TCP, port: 443 }
Default-deny egress for the enclave namespace. An access-restricted enclave with a two-entry allowlist — not a disconnected cluster.

A rule that lives in a review comment is not a control. Push placement and volume restrictions to admission, where a workload that opts out is rejected rather than found later — the discipline we argue for in policy as code with Kyverno.

yaml
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
  name: itar-enclave-placement
  annotations:
    policies.kyverno.io/minversion: "1.17.0"
spec:
  validationActions: [Deny]
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: [v1]
        operations: [CREATE, UPDATE]
        resources: [pods]
  matchConditions:
    - name: enclave-namespace-only
      expression: "request.namespace == 'itar-enclave'"
  validations:
    # Every nodeSelectorTerm must require the enclave label. Terms are
    # ORed, so .all() — not .exists() — is what actually pins placement.
    - expression: >
        has(object.spec.affinity) &&
        has(object.spec.affinity.nodeAffinity) &&
        object.spec.affinity.nodeAffinity
          .requiredDuringSchedulingIgnoredDuringExecution
          .nodeSelectorTerms.all(t,
            t.matchExpressions.exists(e,
              e.key == 'stribog.example/enclave' &&
              e.operator == 'In' && 'itar' in e.values))
      message: >
        Every nodeSelectorTerm must require stribog.example/enclave=itar.
    - expression: >
        !has(object.spec.volumes) ||
        object.spec.volumes.all(v, !has(v.hostPath))
      message: >
        hostPath volumes place technical data outside enclave storage.
Kyverno ValidatingPolicy on the green-field policies.kyverno.io/v1 surface (Kyverno 1.17+). Legacy ClusterPolicy is deprecated; the equivalent there is validate.failureAction: Enforce.
The two hooks produce two different evidence trails. Conflating them is what makes an otherwise sound design hard to defend to export counsel.

Failure modes that quietly produce a release

Namespace RBAC is the control people build first and the one covering the fewest paths. Each of the following moves plaintext outward without touching a RoleBinding, which is why each is an egress or procurement decision rather than an identity one.

  • Logs and crash dumps. Logs quote payloads, stack traces embed parameters, core dumps contain everything resident. Shipping them to a hosted platform sends technical data to whoever operates it. Control: keep the logging pipeline in-perimeter and treat the log store as in-scope storage, not telemetry.
  • Vendor support and screen sharing. A support engineer in a debugging session views whatever is on screen; a support bundle collects config, secret references and sample data. Neither shows up in an access review. Control: no third-party session without a named, attested participant list.
  • AI coding assistants. A completion provider that reads the open file reads the technical data in it — a procurement and endpoint decision, not a cluster one. Control: self-hosted inference or nothing on machines that touch controlled repositories.
  • Backups, snapshots and CI artefacts. Backups outlive the namespace, replicate to a second site and get restored into non-enclave environments during a drill. Build caches and container layers leave the same way. Control: enclave-held keys, policy-restricted restore targets, artefact stores in-perimeter.
  • A key leaving the perimeter. Not egress of data at all. A decryption key, network access code or password copied into a shared vault, a laptop or a chat message is 120.55 access information, and providing it is governed by 120.56(b). Control: keys never leave the module; distribution is brokered and logged, as in our external secrets patterns.

Evidence an auditor can actually check

Two records, kept separately, because the hooks are separate. The first ties every subject with plaintext access to a dated U.S.-person attestation — the 120.50(a)(2) and 120.56(a)(2) trail. The second covers who holds or was given decryption keys and other access information: the 120.56(b) trail, which lives in your KMS logs, not the API server's.

bash
#!/usr/bin/env bash
# Plaintext-access evidence only. Key-custody evidence for 120.55 access
# information comes from the KMS audit trail and is a separate record.
set -euo pipefail

NS="${NS:-itar-enclave}"
AUDIT_LOG="${AUDIT_LOG:-/var/log/kubernetes/audit.log}"

jq -r --arg ns "$NS" '
  select(.objectRef.namespace == $ns)
  | select(.verb | test("^(get|list|watch|create|update|patch)$"))
  | [ .user.username,
      (.user.groups // [] | map(select(startswith("oidc:"))) | sort | join(",")),
      .verb,
      (.objectRef.resource // "-")
    ]
  | @tsv
' "$AUDIT_LOG" | sort -u
Distinct subjects and their prefixed groups against the enclave namespace. Every username this prints must resolve to a dated attestation.

Retain both as long as you retain the data they describe, and reconcile the first against the attestation register on a schedule rather than at audit time — a subject in the log and not in the register is the finding you want to make yourself. The bookkeeping is worth it: under 22 CFR 127.10(a)(1)(i) the civil penalty for each violation of 22 U.S.C. 2778 may reach the greater of a figure in the low seven figures or twice the transaction value, adjusted for inflation annually. Read the section rather than quoting a number from a blog.

Exit ramps and the long game

Three portability decisions, cheap now and expensive later. First, group names: oidc:itar-enclave-cleared is a string every identity provider can emit, while an IdP-internal object identifier binds your RBAC to one vendor's database. Second, CEL admission rules and NetworkPolicy objects move between clusters and distributions; the same rules in a provider's IAM dialect do not. Third, keep the identity path dual: the --oidc-* flags and structured configuration produce identical groups, so a cluster upgrade is a configuration migration, not a redesign.

The long game here is not about hardware lifetimes. USML categories get revised and classifications reviewed; a programme in scope this year may not be next. Gating that lives in portable group names, in-cluster policy and an attestation register survives that; a bespoke arrangement with one provider gets renegotiated each time. Internal PKI for the enclave's service identities, issued by a CA you operate, is the same argument one layer down.

What ITAR makes unusually plain is true of every regime we write about: a control you do not operate is one you are describing rather than holding. Most regulations let a contract paper over that gap. This one asks who was in the room.

§FAQ/Common questions

Frequently asked

Does hosting in AWS GovCloud make a workload ITAR compliant?

No. AWS states that GovCloud (US) is managed solely by US Citizens in US locations and that accounts are only granted to US Persons working for US organizations — a genuine provider-side personnel control. It says nothing about which of your own engineers, contractors or vendors obtain the technical data once it is there, and that customer-side question is the one 22 CFR 120.50(a)(2) turns on. Take the definition of U.S. person from 22 CFR 120.62 rather than from a provider FAQ, which phrases it more narrowly than the regulation does.

What is a deemed export under ITAR?

Under 22 CFR 120.50(a)(2), releasing or otherwise transferring technical data to a foreign person in the United States is itself an export, even though nothing crosses a border. 22 CFR 120.50(b) adds that such a release is deemed an export to all countries in which the foreign person has held or holds citizenship, or holds permanent residency — so prior nationality matters, not only current nationality. Note that the section is 120.50; the widely cited 120.17 was reassigned to End-use monitoring in the Part 120 restructure.

Is a Kubernetes RoleBinding "access information" under 22 CFR 120.55?

No. 120.55 defines access information as information that allows access to encrypted technical data in an unencrypted form, and gives decryption keys, network access codes and passwords as its examples. A RoleBinding, an OIDC group membership or a kubeconfig is an engineering control over who can reach plaintext, governed by the release and deemed-export provisions at 120.56(a)(2) and 120.50(a)(2). Key custody and secret distribution are the 120.55 and 120.56(b) problem, and they produce a different evidence trail.

Does encrypting the data remove the ITAR problem?

Only within limits. The carve-out at 22 CFR 120.54(a)(5) requires all of: unclassified data, end-to-end encryption, FIPS 140-2-compliant or successor modules (or comparable strength to AES-128), not intentionally sent to or stored in a 126.1 country, and not sent from one. The end-to-end definition at 120.54(b)(1)(ii) requires that the means of decryption are not provided to any third party, and 120.54(b)(2) requires the intended recipient to be the originator, a U.S. person in the United States, or a person otherwise authorized. 120.54(c) protects access to the data in encrypted form only.

Does DFARS 252.204-7012 cover the same ground as ITAR?

No, and treating it as a partial answer is a common mistake. DFARS 252.204-7012 is a covered-defense-information and cyber-incident clause: it requires a cloud service provider used to store covered defense information to meet security requirements equivalent to the FedRAMP Moderate baseline, and defines rapidly report as within 72 hours of discovery of a cyber incident. For a DoD contractor both obligations run concurrently with ITAR, but the clause is not an export-control or nationality provision.

itar complianceitar compliant infrastructure export controlled datadeemed export foreign person access control kubernetesexport control kubernetes access-restricted enclaveitar encryption carve-out end-to-end encryptionus person access enforcement technical data

Executive Briefing

Thirty minutes to clarify your infrastructure risk

Walk us through your vendor footprint and regulatory constraints. We will tell you honestly where sovereignty creates leverage — and where it does not. No pitch deck. No obligation.